Raspberry Pi Email Server Part 5: Spam Sorting with LMTP & Sieve

This is the fifth and final part of a five part tutorial that will show you how to install a full featured email server on your Raspberry Pi.

This tutorial covers how to automatically sort spam emails into the spam folder using Dovecot’s Local Mail Transfer Protocol (LMTP) and Sieve rules.

The parts are:
The Introduction & Contents Page (read first)
Raspberry Pi Email Server Part 1: Postfix
Raspberry Pi Email Server Part 2: Dovecot
Raspberry Pi Email Server Part 3: Squirrelmail
Raspberry Pi Email Server Part 4: Spam Detection with Spamassassin
Raspberry Pi Email Server Part 5: Spam Sorting with LMTP & Sieve


If you followed the previous tutorial, you currently have an email server that automatically scans incoming emails using Spamassassin. However, in its current state, Spam and Ham alike are delivered to the inbox, which is annoying. Since Spamassassin only marks emails based on their spam score, we need to use an external program to handle sorting & delivery.

The best tool for the job is Sieve: a set of rules defined by each user to determine how incoming emails are filtered. Sieve rules can sort emails based on a myriad of things: headers, the body of the email, various tags added by external programs, sender address… the list goes on.

In this case, we are going to use Sieve rules to send emails that have been marked by Spamassassin with spam flags like this (“X-Spam-Flag: YES“) in the headers straight into the Spam folder.

At present, emails are delivered to the inbox by Postfix. However, since Sieve is a Dovecot plugin, the final step of the delivery must be handled by Dovecot. This gives us two choices:

  1. Use Dovecot’s Local Delivery Agent (LDA)
  2. Use Dovecot’s Local Mail Transfer Protocol (LMTP)

For our setup, LDA is not ideal because it doesn’t run as root and therefore can’t access the individual inboxes of each user due to permissions. There are some other differences too…from the Dovecot Wiki:

The main difference is that the LDA is a short-running process, started as a binary from command line, while LMTP is a long-running process started by Dovecot’s master process.

In other words, with LDA a new process is started each time an email needs to be delivered, whereas with LMTP a process is always running and it handles a queue of emails. From what I have read on mailing lists etc. it seems that LMTP is more efficient.

So… LMTP it is!

Install & Configure Dovecot LMTP

First, install dovecot-lmtpd:

sudo apt-get update
sudo apt-get install dovecot-lmtpd

This will create a new config file at /etc/dovecot/conf.d/20-lmtp.conf.

Now to change the config in a few files:


Append this to enable lmtp:

protocols = imap lmtp


Add this line to enable address extensions:

lmtp_save_to_detail_mailbox = yes

This means that if you send an email to you+folder@yourdomain.com it should be automatically placed in the “folder” folder. Cool, eh? You can use this for loads of things, but here’s a typical student example from a recent graduate ;) …

Change your email address for Pizza takeaway companies to you+pizza@yourdomain.com and create a folder called “pizza”. Now all your emails about pizza go into a separate folder, instead of cluttering your inbox. Awesome :)

Note: folder names are case sensitive, and the folder must be top level (not a folder within your inbox).

Now change the lmtp protocol block to look like this:

protocol lmtp {
  mail_plugins = $mail_plugins sieve
  postmaster_address = postmaster@yourdomain.com


Now find the service lmtp {… block and then change the line unix_listener lmtp {… to look like this. This will allow postfix to access Dovecot’s LMTP from within its chroot:

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0666


By default, Dovecot will try to look up “you@yourdomain.com” in your user database, when it should be looking up just the first bit (“you”). This setting instructs Dovecot to strip the domain name before doing the lookup, and convert the username to all lowercase letters:

auth_username_format = %Ln

(the L is the lowercase part and the n drops the domain name).


I’m not sure if this bit is necessary, but I commented out the “protocol lmtp {…” block completely.


We still need to instruct Postfix to hand over control to Dovecot’s LMTP for the final stage of delivery.

Comment out:


…and add:

mailbox_transport = lmtp:unix:private/dovecot-lmtp

Sieve Rules

Dovecot's sieve is already installed, you can check by running:

sudo apt-get install dovecot-sieve

Now we need to change one more parameter in /etc/dovecot/conf.d/90-sieve.conf:

Uncomment this setting:

recipient_delimiter = +

We still need to reload/restart Postfix and Dovecot to make that all the changes are loaded:

sudo service postfix reload
sudo service dovecot reload

The default place to put the sieve script is in the user's home folder: ~/.dovecot.sieve.

Create it like this:

sudo nano /home/user/.dovecot.sieve

and add this:

require ["fileinto"];
# Move spam to spam folder
if header :contains "X-Spam-Flag" "YES" {
  fileinto "Spam";
  # Stop here - if there are other rules, ignore them for spam messages

Or, if you want spam messages to be marked as read as well as moved:

require ["fileinto","imap4flags"];
if header :contains "X-Spam-Flag" "YES" {
        addflag "\\Seen";
        fileinto "Spam";

Now chown the file to the owner of the mailbox, e.g.:

sudo chown sam:sam /home/user/.dovecot.sieve

When Spamassassin marks emails as Spam it adds X-Spam-Flag: YES to the headers. This rule checks the headers and sends mail to the spam folder if that flag exists.

Testing: GTUBE SPAM email

Here's how to send an email to your server using Telnet that will definitely be marked as spam. There's a neat trigger called GTUBE (Generic Trigger for Unsolicited Bulk Email) that is implemented in spamassassin.

All we need to do is send a message that contains this line in the body:


We can use Telnet to send this email like we did when testing in parts 1 and 2:

feathers-mcgraw@Hobbs-T440s:~$ telnet yourdomain.com 25
Connected to yourdomain.com.
Escape character is '^]'.
220 yourdomain.com ESMTP Postfix (Debian/GNU)
ehlo randomdomain.com
250-SIZE 10240000
250 DSN
mail from: test@randomdomain.com
250 2.1.0 Ok
rcpt to: pi
250 2.1.5 Ok
354 End data with .
Subject: test spam email
This should set it off...
250 2.0.0 Ok: queued as DDFDEDA77
221 2.0.0 Bye
Connection closed by foreign host.

That email should land in your Spam folder. Note that if you're using Squirrelmail you won't be automatically subscribed to the Spam folder, you have to add it yourself.

Thanks to Jens for finding this test!

Optional: Managesieve

Managesieve is a service that will allow you to log in remotely with a compatible email client and manage your sieve scripts.

First, we need to install the necessary package:

sudo apt-get update
sudo apt-get install dovecot-managesieved

You will notice that this creates a new configuration file: /etc/dovecot/conf.d/20-managesieve.conf, which you can leave as it is.

One more configuration change to make: open /etc/dovecot/dovecot.conf and add sieve to the protocols line:

protocols = imap lmtp sieve

and restart Dovecot:

sudo service dovecot restart

The managesieve service uses port 4190, so log in to your router's admin page and forward this port to your Pi.

You can now test if the service is running with telnet:

sam@samhobbs:/etc/dovecot$ telnet localhost 4190
Connected to localhost.
Escape character is '^]'.
"IMPLEMENTATION" "Dovecot (Ubuntu) Pigeonhole"
"SIEVE" "fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date ihave"
"NOTIFY" "mailto"
"VERSION" "1.0"
OK "Dovecot (Ubuntu) ready."

If it's up and running, then you can try connecting with a compatible email client, like Kmail, the KDE email client that ships with distributions like Kubuntu. There is also a free software sieve plugin for Thunderbird that you might like to try if that's your client of choice.

To enable sieve in Kmail, go to:
Settings --> configure Kmail --> accounts --> Modify --> Filtering

Check "Server supports Sieve" and "Reuse host and login configuration".


Now you can select Settings --> "Manage Sieve Scripts" and edit your scripts using Kmail's editor, complete with syntax highlighting.


If you write multiple scripts, they will be stored in ~/sieve and the active one will be symlinked from ~/.dovecot.sieve. If you had a script at ~/.dovecot.sieve before you set up managesieve, it will be saved as dovecot.orig when you create a new one.


Thanks to the members of Kubuntuforums, without whom I would never have even heard of sieve rules!

Please leave a comment, I'd love to hear how you're getting on!



Hi Sam,
this is a great guide. I followed it to set up a RPi to accept mail from the local network so I have a central place where all system mail from my various linux devices can go. I followed the guide very closely and only skipped the spamassassin part -- there won't be any spam from within my local network.
Sadly, sieve filtering does not work for me. It's only the filtering part that does not work. Maintining the sieve rules via managesieved works great. And I know that the rules are fine, I checked this with "sieve-filter".
Incoming mail, however, does not get filtered. From '/var/log/mail.log' I can read that the mail gets put into 'INBOX', sieve is never mentioned. Do you have any advice on debugging this? I would really appreciate any help.

Btw, you could have tested your sieve rule for spam easily. It's enoug to add a line 'X-Spam-Flag: YES' when 'composing' a message via telnet.


Thanks for the testing tip, I'll have to play around with that and add it to the tutorial.

I think the reason your mail is not being sieved is because it isn't being passed to LMTP at all.

Can you check that your /etc/postfix/main.cf has this parameter in it?

mailbox_transport = lmtp:unix:private/dovecot-lmtp

That line is what tells postfix to pass email to Dovecot's LMTP for the final stage of delivery, and LMTP is the bit that uses sieve.

Additionally, can you check if your /var/log/mail.log mentions delivering with LMTP?


Hello Sam,

thanks for the fast reply. In '/var/log/mail.log' I can indeed see that the mail is passed to dovecot-lmtp. And I checked once again that lmtp in turn is configured to use sieve. It is.

The error was in fact not in the postfix or dovecot configuration. I just noticed in your screenshots above that you ticked the checkbox next to 'dovecot.orig' in the screenshot showing sieve configuration with KMail. So I went back to that dialogue, ticked the checkbox and voila. I'm a bit embarrassed now. But anyway, it's working now, thanks to your guide.


you've createt a very good tutorial here ! It was because of your work i was able to setup an email server on my Pi B+.

There is just something i wanted to point out.
In "/etc/dovecot/conf.d/90-sieve.conf" is an option "sieve_before". Why do you not use that to have a global handling of spam for all users so one does not have to manually create and set ownership for the ".dovecot.sieve" you provided?

So one could create a ".sieve"-file i.e. "/etc/dovecot/global-sieve/spam.sieve"


require ["fileinto"];
# Move spam to spam folder
if header :contains "X-Spam-Flag" "YES" {
fileinto "Spam";
# Stop here so that we do not reply on spams

to sort spam into the right folder and still be able to have and manage user specific sieves right?

If so do i need to chmod "/etc/dovecot/global-sieve/spam.sieve" to rwxrw-r-- ?


I didn't see that option, thank you for pointing it out, it's a great idea.

As for permissions, I think you want the global sieve to be readable and executable by every user, but since a change would affect all users it should only be writable by root.

So assuming it's owned by root try chmod 755 /path/to/script

Thanks again for the interesting comment!



By default, Dovecot will try to look up “you@yourdomain.com” in your user database, when it should be looking up just the first bit (“you”). This setting instructs Dovecot to strip the domain name before doing the loookup:

auth_username_format = %Ln

This did not work for me "unknown user" but when I altered it to = %n it worked fine?

Hi Alf, sorry for the delay in responding to your comment!

The section you quoted above is true, but not the whole truth - %Ln not only strips the domain name, it converts the username to all lower case letters. Do you have any system usernames called things like "Alf" instead of "alf"? That would explain it!


Hi Sam,

really great tutorial you wrote here. I set up a small PasPi Mailserver according to your tut... It's not completed yet, but it looks good allready. I have a small hint regarding your "To-Do" point 1: When you write a Testmail with any subject and this mail content "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X" (without quotes) the mail will be marked as SPAM with a spam score from about 1000... This is a similiar to the eicar test virus.

Hi and hello

I have a quick question about that pizza send to mailbox thing. I would like to know how to change to delimiter to something nicer like a - or a .? Thank in advance

Hi Benjamin,

Try changing this parameter in /etc/dovecot/conf.d/90-sieve.conf:

recipient_delimiter = .

And in /etc/postfix/main.cf, set:

recipient_delimiter = .



After some time on the internet and you're right on the postfix thing but you can also change it in /etc/dovecot/conf.d/15-lda.conf

Anyway thanks for the tut! I am lovin my new email!

Hi Sam, just wanted to thank you so much for this series of tutorials. I setup all of this (barring spamassassin and webmail) a long time ago on my Raspberry Pi Model B. Since that is better off just running XBMC I have bought the new Model B+ to use as my 'everything' server and your tutorial saved me hours and hours of searching around to try to remember how I got the old one working.

The instructions are clear and I love that you explain the steps so that you can get an understanding of what you are doing instead of just copy/pasting commands. Excellent job, thanks again!

You're very welcome! That's just what I was aiming for. I recently used the tutorials to set up a testing email server and was very grateful to my past self for writing it all down!

Thank you for taking the time to leave a message!


Good day. I'm having issues regarding running the lmtp part.
The socket /var/spool/postfix/private/dovecot-lmtp is missing, even though I've installed dovecot-lmtp. Could you help me out? Thanks

service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp { #missing
#mode = 0666
mode = 0666

I'm assuming you mean the listener is not created, not that that block of code isn't found in the config file - if i'm wrong let me know!

Check you enabled lmtp in /etc/dovecot/dovecot.conf with this line:

protocols = imap lmtp

... then restart dovecot and see if that solves your problem.


Replace "user" with the username of the account whose sieve file you are modifying.


One thing I havent seen in here, which may prove to be useful to me (it's complicated, please don't ask - I move around a lot and still try to run a mail server out of the back of my car at times)

I need to pick up all my mail through a pop3 service and drop it into the mailserver at my end and have it processed at this point.

Do you know of a solution for this?

I think you want fetchmail, but I haven't ever used it myself so unfortunately I can't help you set it up!


Unfortunately fetchmail with this setup doesn't work. I followed the tutorial to install the whole package (except Horde instead of Squirrel), and while sieve will move mail received by postfix, it will not process mail retrieved by fetchmail.

This might be going to your Spam folder, ironically, because my free domain does not allow me to add an SPF record. Oh well. Anyway, I really appreciate all your hard work on this, and your other, tutorial(s). Because of that hard work I have successfully been able to configure my email server on my Raspberry Pi, along with the Owncloud server I've had running for more than a month now. Now all I need are some solar panels, an electric car, a farm, some well water, and I'm on my way to being completely off the grid!

Cheers and Best Regards,
Justin Wingate


first of all thank you, i couldn't have come this far without your guide!

Unfortunatley i cannot solve this problem:
Sieve doesnt file the marked mails into Spam, nor the mails coming to me+something@mydomain.de into the something folder.

in the .dovecote.sieve.log is nothing; mail.log says that for the spam mail:
(i changed server names and ips)

Jun 2 18:54:55 raspberrypi postfix/smtpd[4147]: connect from mail-blubb.net[IP-blubb]
Jun 2 18:54:55 raspberrypi postfix/smtpd[4147]: A30EF42228: client=mail-blubb.net[IP-blubb]
Jun 2 18:54:55 raspberrypi postfix/cleanup[4153]: A30EF42228: message-id=<556DDFDB.9000306@blubb.de>
Jun 2 18:54:55 raspberrypi postfix/qmgr[4140]: A30EF42228: from=, size=9924, nrcpt=1 (queue active)
Jun 2 18:54:55 raspberrypi spamd[2949]: spamd: connection from localhost [] at port 60165
Jun 2 18:54:55 raspberrypi postfix/smtpd[4147]: disconnect from mail-blubb.net[IPblubb]
Jun 2 18:54:55 raspberrypi spamd[2949]: spamd: setuid to debian-spamd succeeded
Jun 2 18:54:55 raspberrypi spamd[2949]: spamd: processing message <556DDFDB.9000306@blubb.de> for debian-spamd:113
Jun 2 18:54:57 raspberrypi spamd[2949]: spamd: identified spam (1000.0/2.0) for debian-spamd:113 in 1.3 seconds, 9648 bytes.
Jun 2 18:54:57 raspberrypi spamd[2949]: spamd: result: Y 1000 - FREEMAIL_FROM,GTUBE,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,T_DKIM_INVALID,T_TO_NO_BRKTS_FREEMAIL scantime=1.3,size=9648,user=debian-spamd,uid=113,required_score=2.0,rhost=localhost,raddr=,rport=60165,mid=<556DDFDB.9000306@blubb.de>,autolearn=no
Jun 2 18:54:57 raspberrypi postfix/pickup[4139]: 2F25442470: uid=113 from=
Jun 2 18:54:57 raspberrypi postfix/cleanup[4153]: 2F25442470: message-id=<556DDFDB.9000306@blubb.de>
Jun 2 18:54:57 raspberrypi postfix/pipe[4154]: A30EF42228: to=, relay=spamassassin, delay=1.6, delays=0.11/0.02/0/1.4, dsn=2.0.0, status=sent (delivered via spamassassin service)
Jun 2 18:54:57 raspberrypi postfix/qmgr[4140]: A30EF42228: removed
Jun 2 18:54:57 raspberrypi postfix/qmgr[4140]: 2F25442470: from=, size=10952, nrcpt=1 (queue active)
Jun 2 18:54:57 raspberrypi dovecot: lmtp(4160): Connect from local
Jun 2 18:54:57 raspberrypi dovecot: lmtp(4160, email): BrfBEOHfbVVAEAAAKv1cSg: sieve: msgid=<556DDFDB.9000306@blubb.de>: stored mail into mailbox 'INBOX'
Jun 2 18:54:57 raspberrypi postfix/lmtp[4159]: 2F25442470: to=, relay=blubbls.de[private/dovecot-lmtp], delay=0.17, delays=0.04/0.02/0.05/0.06, dsn=2.0.0, status=sent (250 2.0.0 BrfBEOHfbVVAEAAAKv1cSg Saved)
Jun 2 18:54:57 raspberrypi dovecot: lmtp(4160): Disconnect from local: Client quit (in reset)
Jun 2 18:54:57 raspberrypi postfix/qmgr[4140]: 2F25442470: removed
Jun 2 18:54:57 raspberrypi spamd[2755]: prefork: child states: II

and that for the something mail:
more or less the same in mail.log
.dovecot.sieve.log says "error: msgid=<1851654fddbaa71eee914d50d631662c@www.something.com>: failed to store into mailbox 'something': Internal error occurred. Refer to server log for more information. [2015-06-02 18:53:40]."
i unfortunately have no idea where to finde server log information?!

the .dovecot.sieve file "-rwxr-xr-x user:user" rights. and contains:

require ["fileinto"];
if header :contains "SPAM" "YES" {
fileinto "Spam";

note that by default it just added "***SPAM***" and i change it to "***SPAM***YES***" in /etc/spamassassin/local.cf

I think the problem is that you changed the sieve rule but you didn't have to. The default one in the tutorial looks for a specific line in the header (which is not the same thing as the subject, which is just one part of the header!) . You've changed the subject line, but the line sieve is looking for should still be the same.

Whichever email client you're using, you should be able to view headers (although some mobile clients don't have this feature). In kmail, you just press v. Here's an example of one of the many spam emails I get daily:

Return-Path: <rwkvpih@bagbulter.com>
Delivered-To: me
Received: from samhobbs.co.uk
	by samhobbs (Dovecot) with LMTP id SBGlJ4e9bVVJdgAA+i6E6g
	for <me>; Tue, 02 Jun 2015 15:28:23 +0100
Received: by samhobbs.co.uk (Postfix, from userid 119)
	id 9F7B5140958; Tue,  2 Jun 2015 15:28:23 +0100 (BST)
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on samhobbs
X-Spam-Flag: YES
X-Spam-Level: *************************
X-Spam-Status: Yes, score=25.2 required=5.0 tests=CK_HELO_DYNAMIC_SPLIT_IP,
	autolearn=spam autolearn_force=no version=3.4.0
	*  0.0 CK_HELO_DYNAMIC_SPLIT_IP Relay HELO'd using suspicious hostname
	*      (Split IP)
	*  0.2 CK_HELO_GENERIC Relay used name indicative of a Dynamic Pool or
	*      Generic rPTR
	*  0.0 TVD_RCVD_IP No description available.
	*  0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked.
	*       See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block
	*      for more information.
	*      [URIs: cc4.co]
	*  1.3 URIBL_MW_SURBL Contains a Malware Domain or IP listed in the MW
	*      SURBL blocklist
	*      [URIs: cc4.co]
	*  1.7 URIBL_WS_SURBL Contains an URL listed in the WS SURBL blocklist
	*      [URIs: cc4.co]
	*  4.5 URIBL_AB_SURBL Contains an URL listed in the AB SURBL blocklist
	*      [URIs: cc4.co]
	*  1.9 URIBL_JP_SURBL Contains an URL listed in the JP SURBL blocklist
	*      [URIs: cc4.co]
	*  1.6 RCVD_IN_BRBL_LASTEXT RBL: No description available.
	*      [ listed in bb.barracudacentral.org]
	*  2.7 RCVD_IN_PSBL RBL: Received via a relay in PSBL
	*      [ listed in psbl.surriel.com]
	*  1.3 RCVD_IN_RP_RNBL RBL: Relay in RNBL,
	*      https://senderscore.org/blacklistlookup/
	*      [ listed in bl.score.senderscore.com]
	*  0.7 RCVD_IN_XBL RBL: Received via a relay in Spamhaus XBL
	*      [ listed in zen.spamhaus.org]
	*  2.4 RCVD_IN_MSPIKE_L5 RBL: Very bad reputation (-5)
	*      [ listed in bl.mailspike.net]
	*  1.2 RCVD_IN_BL_SPAMCOP_NET RBL: Received via a relay in bl.spamcop.net
	*      [Blocked - see ]
	*  0.0 HTML_MESSAGE BODY: HTML included in message
	*  1.1 MIME_HTML_ONLY BODY: Message only has text/html MIME parts
	*  0.0 RCVD_IN_MSPIKE_BL Mailspike blacklisted
	*  3.9 HELO_DYNAMIC_IPADDR2 Relay HELO'd using suspicious hostname (IP addr
	*       2)
	*  0.6 HTML_MIME_NO_HTML_TAG HTML-only message, but there is no HTML tag
Received: from 24-196-69-180.static.mdsn.wi.charter.com (24-196-69-180.static.mdsn.wi.charter.com [])
	by samhobbs.co.uk (Postfix) with SMTP id 65E9D140325
	for <me>; Tue,  2 Jun 2015 15:28:22 +0100 (BST)
From: "Ananya" <Palmer_Jody@avidcanada.com>
Subject: [***** SPAM 25.2 *****] Here you can find some pics
To: me
Date: Tue, 02 Jun 2015 17:25:19 +0200
Mime-Version: 1.0
Content-Type: text/html;
Content-Transfer-Encoding: 7Bit
X-Spam-Prev-Subject: Here you can find some pics

The part that sieve is matching is this:

X-Spam-Flag: YES

...so the default rule from the tutorial should still work.

For the "save into folder" feature, the folder must already exist (and note there's a difference between a folder in your inbox and a top level folder).


I found the mistake for fileinto Spam. I thought header is the same as subject, so i searched for "**SPAM**" for example, which gets written into the subject. I changed that to " the normal check "X-Spam-Flag" "YES" and now it works.
Sorry for the posting of that :)

But the pizza thing still doesn't quite work. Or maybe it supposed to work like that.
If there is no folder the mail from "me+something@domain.de" goes into normal inbox. if i created the folder and added a check to my .dovecot.sieve file like this:
if address :contains "to" "something" {
fileinto "something";

Is this the right way? I thought the +pizza thing is supposed to work automaticaly when the folders exists without the check i wrote?!
Sorry for the long post, and thank you again for this great tutorial, and at least for reading this, if not helping :)

The folder name is case sensitive, could that be it? Can you confirm it's a top level folder (not a folder within inbox)?

Try sending yourself an email like you+Spam@yourdomain.com - it should go into the Spam.

ah it was the case sensitivity! yes it was a toplevel folder. now everything works!
Thank you so much for your help and blazing fast responses!

Excellent :)

I'll add a note about case sensitivity, it's not obvious. Thanks for commenting!


Add new comment