Raspberry Pi Email Server Part 1: Postfix

Postfix Logo

This is the first 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 Postfix, the Mail Transfer Agent.

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

Installing Postfix

Note: While you are setting up the mail server on the Pi, it’s a good idea to turn off port forwarding rules for email to the Pi in your router’s firewall. If you don’t have any port forwarding rules now, that’s great, don’t worry – I’ll prompt you to set them up later.

First, log into your Pi with a SSH session and install postfix:

sudo apt-get update
sudo apt-get install postfix

You will see a menu with some choices. Select “Internet Site” and then set the mail name to your domain name, not including www. (e.g. samhobbs.co.uk).

The setup script will then do some automatic configuration for you. The output will look something like this:

Selecting previously unselected package postfix.                                              
(Reading database ... 67653 files and directories currently installed.)                       
Unpacking postfix (from .../postfix_2.9.6-2_armhf.deb) ...                                    
Processing triggers for man-db ...
Setting up postfix (2.9.6-2) ...
Adding group `postfix' (GID XXX) ...
Adding system user `postfix' (UID XXX) ...
Adding new user `postfix' (UID XXX) with group `postfix' ...
Not creating home directory `/var/spool/postfix'.
Creating /etc/postfix/dynamicmaps.cf
Adding tcp map entry to /etc/postfix/dynamicmaps.cf
Adding sqlite map entry to /etc/postfix/dynamicmaps.cf
Adding group `postdrop' (GID XXX) ...
setting myhostname: samhobbs
setting alias maps
setting alias database
changing /etc/mailname to samhobbs.co.uk
setting myorigin
setting destinations: samhobbs.co.uk, samhobbs, localhost.localdomain, localhost
setting relayhost: 
setting mynetworks: [::ffff:]/104 [::1]/128
setting mailbox_size_limit: 0
setting recipient_delimiter: +
setting inet_interfaces: all
/etc/aliases does not exist, creating it.
WARNING: /etc/aliases exists, but does not have a root alias.

You can edit all of this later.

You may also get some warnings like this:

postmulti: warning: inet_protocols: disabling IPv6 name/address support: Address family not supported by protocol

IPv6 is a new type of IP address that was introduced because we’re running out of the “old” IPv4 addresses. Not many ISPs support IPv6 yet, so you probably don’t need it. Unless you fix the warning, you’ll see it every time.

Change directory into the postfix configuration folder:

cd /etc/postfix/

Edit /etc/postfix/main.cf with your favourite command line text editor (e.g. sudo nano main.cf) and add inet_protocols = ipv4 to the end of the file.

Now is also a good time to check that your hostname is specified properly in /etc/postfix/main.cf. The setup script takes the hostname of the server and uses that, but it may not be in the right format, i.e. “samhobbs” instead of “samhobbs.co.uk”. Find the line that begins myhostname = and make sure it is your fully qualified domain name. This is important because your server will use this to talk to other mail servers, and some will reject your emails if you don’t use a fully qualified domain name to say hi! This is covered in more detail in the helo access restrictions later.

Restart postfix and you shouldn’t see the warnings any more:

sudo service postfix restart

Testing and Configuration

Before you start, it’s probably worth backing up the configuration files in their current state. This way, you’ll have something to compare to if you’re ever trying to work out which bits were defaults and which bits you changed yourself:

cd /etc/postfix
sudo cp main.cf main.cf.BAK
sudo cp master.cf master.cf.BAK

Mailbox Setup

There are a couple of different types of mailbox you can use, I’ve chosen to use a “Maildir” rather than “mbox” configuration. For users with “real” UNIX accounts on the system (like the one you’re using to log in), Maildir creates a folder in the user’s home directory and places emails inside it, one file for each email.

I prefer this to the alternatives, because it’s easier to see and understand: you can rummage around in your home folder and see all your emails as individual files.

To tell Postfix to use the Maildir format, add the following lines to /etc/postfix/main.cf:

home_mailbox = Maildir/
mailbox_command =

If there's already a line with mailbox_command, comment it out by adding a # at the start of the line.

We also need to create the mail directory and its subfolders for existing users, and add some things to /etc/skel (the template for new users) so that if you create a new account this will be done automatically.

These commands are part of Dovecot, so first we need to install it:

sudo apt-get update
sudo apt-get install dovecot-common dovecot-imapd

You will get a lot of output: some other dovecot packages will automatically be installed and the config files will be created. You will also see some errors – don’t worry about those for now, I’ll explain how to deal with them in part 2, later.

Now we can create those mail folders. Run the following commands to create the template files:

sudo maildirmake.dovecot /etc/skel/Maildir
sudo maildirmake.dovecot /etc/skel/Maildir/.Drafts
sudo maildirmake.dovecot /etc/skel/Maildir/.Sent
sudo maildirmake.dovecot /etc/skel/Maildir/.Spam
sudo maildirmake.dovecot /etc/skel/Maildir/.Trash
sudo maildirmake.dovecot /etc/skel/Maildir/.Templates

Next, copy the files over to existing users’ home directories, and change the ownership and permissions for privacy (replace USER with the username you are doing this for, and repeat for all existing usernames):

sudo cp -r /etc/skel/Maildir /home/USER/
sudo chown -R USER:USER /home/USER/Maildir
sudo chmod -R 700 /home/USER/Maildir

Initial Testing

Now, the best way to test Postfix during configuration is to use Telnet, because it is such a simple way of communicating between programs and there’s less to go wrong and get confused about.

First, install telnet:

sudo apt-get install telnet

Now, still inside the SSH session to your pi, type this command. It will connect you to port 25 on the Pi:

telnet localhost 25

You can now test sending an email using SMTP. Here are the steps:

  1. send an ehlo command to tell the server who you are, and it will tell you its capabilities
  2. use the mail from command to say who the email is from. If you are sending it from an address that exists on the server, you needn’t include the domain name (i.e. user instead of user@yourdomain.com)
  3. use the rcpt to command to tell the server where to send the email
  4. Use the data command to tell the server that you’re about to start giving it the message you want to send
  5. Type Subject: YOUR SUBJECT then enter to set a subject
  6. Type the body of your email. Once you’re done, press ENTER, then ., then ENTER again.
  7. Type quit to exit

Here’s an example:

telnet localhost 25
Connected to localhost.
Escape character is '^]'.
220 samhobbs.co.uk ESMTP Postfix (Debian/GNU)
ehlo foobar
250-SIZE 10240000
250 DSN
mail from: me        
250 2.1.0 Ok
rcpt to: me@outsideemail.com
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
Subject: test
This is a test email
250 2.0.0 Ok: queued as A639C3EE6D
221 2.0.0 Bye

Some Access Restrictions

Add the following to /etc/postfix/main.cf to restrict who can send emails to external mail servers:

smtpd_recipient_restrictions =

Reload postfix:

sudo service postfix reload
  • Line 1 begins the list of restrictions.
  • Line 2 permits users who have authenticated with Simple Authentication and Security Layer (SASL) to send email to any destination (this is part of the Dovecot config in Part 2, later).
  • Line 3 will let users send emails to any destination if they have connected from an IP address defined in mynetworks.
  • Line 4 will reject the email if none of the above conditions have been met unless the “rcpt to” address is one of the addresses that your server is accepting email to (as defined in main.cf with the mydestination parameter).

In its present state, the email server will allow you to send external emails because the connection is originating from the Pi itself (you are logged in via SSH) and not an unknown computer. Addresses of “trusted” computers are listed under the mynetworks setting in main.cf, e.g.

mynetworks = [::ffff:]/104 [::1]/128

Try sending an external email again, using telnet as before. You should be able to do so without any issues.

Now we want to see what kind of response someone would get if they were connecting from outside of the IP range defined in mynetworks, to make sure Pi won’t allow everyone to send outgoing emails from your server. To simulate this we can comment out permit_mynetworks under smtpd_recipient_restrictions:

smtpd_recipient_restrictions =
#       permit_mynetworks,

Now reload the postfix configuration:

sudo service postfix reload

This will let you see what kind of response you would get if you weren’t sending the email from mynetworks. Try sending again, and you should receive an error “554: Relay access denied“:

admin@samhobbs /etc/postfix $ telnet localhost 25
Connected to localhost.
Escape character is '^]'.
220 samhobbs.co.uk ESMTP Postfix (Debian/GNU)
ehlo samhobbs.co.uk
250-SIZE 10240000
250 DSN
mail from: USER
250 2.1.0 Ok
rcpt to: me@externalemail.com
554 5.7.1 <me@externalemail.com>: Relay access denied
221 2.0.0 Bye
Connection closed by foreign host.

Perfect. Leave permit_mynetworks commented out in your smtpd_recipient_restrictions (you'll see why in part 2).

Helo access restrictions

Helo access restrictions can be a very useful way of blocking spam.

Note that we’re not talking about unauthorised people being able to send email outside your network any more (that’s taken care of with the smtpd_recipient_restrictions); we’re now talking about stopping spammers from sending incoming mail to your email address.

Spammers try to conceal their identity so that they don’t end up on block lists, so they rarely use helo hostnames that could identify them – these hostnames are written to the mail log files. As a result, they often make up a random string or use an IP address instead of a domain name.

Luckily, these are easily taken care of.

Add the following to /etc/postfix/main.cf:

smtpd_helo_required = yes
smtpd_helo_restrictions =
  • Line 1 requires people and programs to identify themselves when they send email, using the helo or ehlo commands I mentioned earlier.
  • Line 2 starts the list of restrictions.
  • Line 3 accepts any old rubbish in the ehlo if it comes from an IP address defined in mynetworks. If the connection isn’t connecting from an IP address in mynetworks, then the helo hostname is checked against the rest of the list.
  • Line 4 accepts any helo hostname if the client is authenticated with SASL (I added this to the tutorial recently after troubleshooting problems some people had in the comments – it allows you to connect from any network and still send messages through your Pi. Mobiles will usually work without this because most providers pass mail through their own proxies, so your Pi receives a connection from the proxy – which has a valid hostname – and not from the mobile, which may be called something like “android-b627cfe2efea7e67″).
  • Line 5 rejects connection attempts when the HELO hostname syntax is invalid.
  • Line 6 rejects non-fully qualified domain names (for example, foobar instead of foobar.com). This will also block those random strings, e.g. “kjhrsbvks”.
  • Line 7 rejects the helo hostname if it that domain doesn’t have a valid DNS A or MX record. For example, someone spamming you could make up a domain like theflyingspaghettimonster.com. If that domain doesn’t actually exist and have the right records, then your server won’t accept it as a hostname, and the email will be rejected.

If the helo hostname gets past line 7 and hasn’t been denied, it is accepted. You’d be surprised how much spam these helo access restrictions will block on their own (looking through my log files, I can see numerous spam scripts that have attempted to ehlo with my IP address), but there’s an extra step we can add in here to help:

Blocking people claiming to be your domain name

Many spammers try to send email to you after helo’ing with your own domain name. Since postfix doesn’t check whether or not they’re lying about their helo hostname, this will usually work.

But, since we’ve put permit_mynetworks at the top of the list, anyone actually sending an email from your domain will be accepted already. Anyone using your hostname who isn’t in mynetworks is an imposter.

So, add one more line to the end of the restrictions list:

smtpd_helo_restrictions =
        check_helo_access hash:/etc/postfix/helo_access

That last line checks a file for custom rules you’ve built in. Create the file:

sudo nano /etc/postfix/helo_access

Add the following lines, edited for your domain:

samhobbs.co.uk          REJECT          Get lost - you're lying about who you are
mail.samhobbs.co.uk      REJECT          Get lost - you're lying about who you are

Now tell postfix to map the file, and restart postfix:

sudo postmap /etc/postfix/helo_access
sudo service postfix restart

Now anyone who tries to ehlo with one of the hostnames you defined in that file gets rejected, and sees the “get lost” message. Your legitimate servers won’t have that problem, because they will already have been accepted higher up the list.

Neat, right? I found that little nugget of wisdom at unixwiz.net.

Moving on…

We’re almost done with Postfix now, athough there are a few bits of configuration that we’ll want to do once we’ve set up SASL with Dovecot, which I’ve chosen to lump in with the Dovecot tutorial.

In Raspberry Pi Email Server Part 2: Dovecot, we’ll set up Dovecot to provide SASL authentication and IMAP capability.

Please leave a comment if you’re having trouble with anything in this tutorial, and I’ll try and help you out!



You want the chown command ("change ownership"):

sudo chown -R  pi:pi /media/usb/pi

sudo chown -R testuser:testuser /media/usb/testuser


sorry, i forgot to mention that this usb drive is already mounted with permissions in fstab like this:
UUID=DC72-0315 /media/usbdrive auto nofail,uid=33,gid=33,umask=0027,dmask=0027,noatime 0 0
(got this one from a owncloud tutorial)

What kind of filesystem is it? FAT doesn't support linux permissions, you need to use EXT2/3/4 or some other "proper" filesystem ;)

Hi Sam,
Thank you for the awesomely written tutorial. I am very new to the coding and RPi world. I use ngrok to host my webpage and was trying to do this tutorial using my ngrok setup and I am not receiving any mail. Therefore, I figured maybe it is not possible to use ngrok. I am not sure how to use namecheap or noip and so hence my reasoning to try ngrok. If you wouldn't mind telling me whether my idea makes sense or not I would really appreciate it.
Many thanks

From the quick look I just had, ngrok is a reverse tunnel. That probably won't work for an email server, because the client MTAs (other servers) need to be able to connect to your server directly to send it mail, but if you're using ngrok your server is behind a firewall. I guess if your server is connected to ngrok constantly, and your MX record points to whatever.ngrok.com then they could still act as a go-between. Lots of problems there to do with certificates etc. though - I wouldn't bother.

If you're hosting at home you should register a domain name (they are really cheap - mine was £15 for 5 years with namecheap), and then set up DNS records as per the dns tutorial to point to your IP address (with dynamic dns if necessary), and then forward the necessary ports on your router to your pi.

It's worth checking if your ISP blocks port 25 before you register a domain name though, a lot of people have problems with that.


So when I fist got my raspberry pi 3 I tried to install postfix but at the start the page that pops up in terminal where you are meant to select "Internet Site" I could select it and I ended up removing it. It didn't completely remove it and when I tried to host a website on my pi the screen came up and I could select "Internet Site" but I didn't get the page to choose the domain name so I've tried to remove Postfix so I can re-download it. But when I try to remove it (sudo apt-get remove postfix) and re-download it the screen where you select Internet Site isn't appearing.


To run the installation wizard again, you need:

sudo dpkg-reconfigure postfix

Let me know if that doesn't work.


Hello Sam
First thank you very much for the valuable tutorials you've put on the internet, I think without them, it would have taken me months to fairly use this little tiny computer.
This is my first week with my Raspberry Pi 3, I am trying to configure Postfix by following the steps in part 1 of your tutorials.
Two questions about myhostname and the fully qualified domain name:
I imagine I have to register my domain name with a Registrar? IF YES .... then
Do I have to host my domain name somewhere like goDaddy or will it be hosted on the Raspberry Pi?
Thank you very much

Yes, but don't use GoDaddy, they're a horrible company! I'd recommend namecheap instead.

Namecheap will do two things: register the domain and provide DNS. The DNS is just a signpost to your IP address, the rest is hosted on your own server (the Pi).

DNS basics tutorial


Hi Sam,

I have been having problems with mail not received. The sender claims it has arrived. Eventually, looking at mail.info I find:

connect from telth18.voiptalk.org[]
postfix/smtpd[22444]: NOQUEUE: reject: RCPT from telth18.voiptalk.org[]: 450 4.7.1 <mx00.voipworld.org>: Helo command rejected: Host not found; from=<info@draytel.org> to=<jmn@xxxxxx.co.uk> proto=ESMTP helo=<mx00.voipworld.org>
disconnect from telth18.voiptalk.org[]

In postifx.main.cf I changed: smtpd_helo_required = no and commented out #smtpd_helo_restrictions = etc.

Using "dig" I could not see what is wrong with telth18.voiptalk.org, now mx00.voipworld.org .

Which "host not found" does this refer to?
Is there a way to get round this problem?


I just did a DNS test - mx00.voipworld.org does not have an MX or DNS A record (see here DNS test - apologies for the image, I'm not at my linux box). The test is done against the helo provided by the client, not the reverse DNS lookup on the IP (telth18.voiptalk.org)

Tell the postmaster for that domain to add a DNS A record for that server, or if you want to remove this restriction, remove reject_unknown_helo_hostname from the helo restrictions.


Hi Sam,

Hmmm. When I do " dig mx00.voipworld.org" I see both A and MX records. But there is no IP address specified. Is this what is wrong?


jmnpi2:~> dig mx00.voipworld.org

; <<>> DiG 9.9.5-9+deb8u5-Raspbian <<>> mx00.voipworld.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 14449
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

; EDNS: version: 0, flags:; udp: 4096
;mx00.voipworld.org. IN A

voipworld.org. 1817 IN SOA ns.hosteurope.com. hostmaster.voipworld.org. 2012022401 86400 3600 1209600 14400

;; Query time: 45 msec
;; WHEN: Thu Sep 08 16:41:21 BST 2016
;; MSG SIZE rcvd: 111

jmnpi2:~> dig mx00.voipworld.org MX

; <<>> DiG 9.9.5-9+deb8u5-Raspbian <<>> mx00.voipworld.org MX
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 24359
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

; EDNS: version: 0, flags:; udp: 4096
;mx00.voipworld.org. IN MX

voipworld.org. 1811 IN SOA ns.hosteurope.com. hostmaster.voipworld.org. 2012022401 86400 3600 1209600 14400

;; Query time: 60 msec
;; WHEN: Thu Sep 08 16:41:27 BST 2016
;; MSG SIZE rcvd: 111

That's not actually a record, it's information about the query, the server that was queried and the server responsible for that domain etc.

If you want to remove those sections from the result you can use the option +noall to remove all parts of the query and then +answer to add the answer back in (or +short if you just want the IP address).


Hi Sam,

Thanks for helo information. While looking through mail.info I found another puzzle. There are several instances of these examples:

Sep 5 09:06:13 jmnpi2 postfix/smtpd[21889]: NOQUEUE: reject: RCPT from unknown[]: 504 5.5.2 <XL-20160228UDLC>: Helo command rejected: need fully-qualified hostname; from=<lort@demon.co.uk> to=<xiaonanzi11165@vip.163.com> proto=ESMTP helo=<XL-20160228UDLC>
Sep 5 09:06:14 jmnpi2 postfix/smtpd[21889]: NOQUEUE: reject: RCPT from unknown[]: 504 5.5.2 <XL-20160228UDLC>: Helo command rejected: need fully-qualified hostname; from=<jlp@demon.co.uk> to=<xiaonanzi11165@vip.163.com> proto=ESMTP helo=<XL-20160228UDLC>

Does this mean that an external source is attempting to send mail via my server? Am I a relay server?


That's probably a spam bot, and yes they are trying to relay through your server. They happen to have been stopped by the helo restrictions first, but if you didn't have those they would have got a different error (relay access denied).

You're not an open relay unless you've changed the relay restrictions list.


I´m trying to figure out how to add an alias. The postfix manual says something about virtual_alias_maps, which I tried and then nothing worked anymore...so I just changed everything back to working state. But I really would like an alias....any idea?

Hey, it's in the cacert tutorial.

Just edit the file /etc/aliases and then run sudo newaliases and reload postfix.


That did it. I have letsencrypt so I passed on the Cacert-tut. But it's a piece of cake.

First of all, thank you for all of the wonderful tutorials you have made available. I am running on a Raspberry Pi model B with 512 MB of memory. I'm using No-IP to assign a domain name because I'm connecting to the internet through Comcast which uses dynamic IP addressing. I have been able to follow your tutorial on installing an email server on my Pi until I get to the point of testing it with telnet. I'm connecting to my Pi using an SSH client. I have download and installed telnet. when I invoke telnet and try to run any of the commands in your tutorial I get back the message "command not found". Do you have any ideas what I may be doing wrong?

Thanks, in advance for any help you can provide.

Del Thompson


Just want to check you're running telnet from within the SSH session, and telnet is installed on the server?


Weird, I don't understand why you're getting command not found if telnet is installed (unless you mistyped telnet?)

Can you try the telnet test and copy and paste the output please?


When I issue the telnet localhost 25 command i get the following.;

telnet: Unable to connect to remote host: Connection refused

could it be possible that the pi has a firewall setting that blocks access to port 25? I connect to port 22 when I use telnet.
here is what I get when I issue the telnet localhost 22 command along with apt-cache policy command:
pi@raspberrypi:~ $ telnet localhost 22
Trying ::1...
Connected to localhost.
Escape character is '^]'.
SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3
apt-cache policy telnet
Protocol mismatch.
Connection closed by foreign host.

OK so that's not the same as the "command not found" error, telnet is installed fine.

Looks like postfix isn't running, what do you get from:

sudo service postfix status

BTW you can't use shell commands from within telnet which is what you were doing in the last part of the output you posted, you need to quit telnet first (CTRL + C always works, or if you want to exit cleanly you can use "a logout" for IMAP, "quit" for SMTP).


her is what I get when I invoke the command sudoservice postfix status:
sudo service postfix status
● postfix.service - LSB: Postfix Mail Transport Agent
Loaded: loaded (/etc/init.d/postfix)
Drop-In: /run/systemd/generator/postfix.service.d
Active: active (exited) since Fri 2016-09-23 19:00:17 PDT; 3h 49min ago
Process: 1734 ExecStart=/etc/init.d/postfix start (code=exited, status=0/SUCCESS)

Sep 23 19:00:14 raspberrypi systemd[1]: Starting LSB: Postfix Mail Transpor....
Sep 23 19:00:15 raspberrypi postmulti[1743]: fatal: /etc/postfix/main.cf, l..."
Sep 23 19:00:16 raspberrypi postfix[1734]: Starting Postfix Mail Transport ....
Sep 23 19:00:17 raspberrypi systemd[1]: Started LSB: Postfix Mail Transport....
Hint: Some lines were ellipsized, use -l to show in full.

OK so postfix hasn't started properly, unfortunately the log lines included in the output are truncated so you can't see the useful part. What do you get from:

tail /var/log/mail.err



Add new comment