Email Server - Part 1
More Raspberry Pi Projects
Project 2 — Email Server Part 1: Postfix, Dovecot & Roundcube
Before You Begin — Important Checks
Running your own mail server is genuinely one of the hardest self-hosting projects. Before spending time on it, work through this checklist:
Test it: from outside your network (e.g. your mobile data connection), run:
telnet mail.yourdomain.com 25If it times out, your ISP is blocking port 25. Contact them to request it be opened — some providers will do this for business accounts. Alternatively, use a mail relay service like Mailgun or SendGrid for outbound delivery only, and receive mail at port 587 instead.
How a Mail Server Works
A mail server is not one piece of software — it is a stack of components, each with a specific job:
MUA (phone/desktop client) connects to Dovecot (IMAP) to read mail. Postfix handles all sending and receiving between servers.
| Component | Role | Analogy |
|---|---|---|
| Postfix | MTA — Mail Transfer Agent. Accepts incoming SMTP connections from other mail servers and delivers outgoing mail. | The postal sorting office |
| Dovecot | IMAP server. Stores mail in mailboxes on disk and serves it to email clients (Outlook, Thunderbird, iPhone Mail). | Your personal PO Box |
| Roundcube | Webmail. A web-based email client that talks to Dovecot via IMAP, letting you read and send mail in a browser. | The post office counter |
What You Will Need
Ports to forward on your router
| Port | Protocol | Service | Purpose |
|---|---|---|---|
| 25 | TCP | Postfix SMTP | Receiving email from other mail servers |
| 587 | TCP | Postfix Submission | Sending email from your devices (authenticated) |
| 143 | TCP | Dovecot IMAP | Reading email (unencrypted — Part 2 replaces with 993) |
| 993 | TCP | Dovecot IMAPS | Reading email over TLS (set up in Part 2) |
| 443 | TCP | Apache HTTPS | Roundcube webmail |
Step 1 — Set Your Hostname
Your Pi's hostname must match the domain you will use for email. Mail servers check the hostname of connecting servers — a mismatch triggers spam filters.
pi@raspberrypi:~$ sudo hostnamectl set-hostname mail.yourdomain.com pi@raspberrypi:~$ sudo nano /etc/hosts
Ensure this line is present (replace with your Pi's local IP and domain):
192.168.1.42 mail.yourdomain.com mail
pi@raspberrypi:~$ hostname --fqdn mail.yourdomain.com ← should return your full domain
Step 2 — Add DNS Records
Log in to your domain registrar and add these records before installing anything. DNS changes take time — doing this first means they will be propagated by the time you need them.
| Type | Name | Value | Purpose |
|---|---|---|---|
| A | mail | Your public IP address | Points mail.yourdomain.com to your Pi |
| MX | @ (root domain) | mail.yourdomain.com (priority 10) | Tells other mail servers where to deliver email for your domain |
Verify propagation (wait at least 15 minutes after adding):
pi@raspberrypi:~$ dig MX yourdomain.com +short 10 mail.yourdomain.com. pi@raspberrypi:~$ dig A mail.yourdomain.com +short 203.0.113.42
Step 3 — Install Postfix
pi@raspberrypi:~$ sudo apt update pi@raspberrypi:~$ sudo apt install -y postfix postfix-mysql mailutils
During installation a blue configuration dialog will appear:
- General type of mail configuration: choose Internet Site
- System mail name: enter
yourdomain.com(notmail.yourdomain.com— this is the domain part of your email addresses)
Verify Postfix is running:
pi@raspberrypi:~$ sudo systemctl status postfix ● postfix.service - Postfix Mail Transport Agent Active: active (running)
Step 4 — Configure Postfix
The main Postfix configuration file is /etc/postfix/main.cf. Edit it carefully — Postfix is sensitive to whitespace and syntax errors:
pi@raspberrypi:~$ sudo nano /etc/postfix/main.cf
Set or confirm these values (add any that are missing):
# ── Identity ──────────────────────────────────────────────────── myhostname = mail.yourdomain.com mydomain = yourdomain.com myorigin = $mydomain # ── What to receive mail for ──────────────────────────────────── mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain # ── Which networks can relay through us ───────────────────────── mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 # ── Where to store mail ───────────────────────────────────────── home_mailbox = Maildir/ mailbox_size_limit = 0 # no size limit # ── Network settings ──────────────────────────────────────────── inet_interfaces = all inet_protocols = ipv4 # change to "all" if you have IPv6 # ── Submission port (for sending from devices) ────────────────── # (configured in master.cf — see next step) # ── SASL authentication (for sending) ─────────────────────────── smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous # ── Restrictions ──────────────────────────────────────────────── smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
Enable the submission port (587)
Port 587 is used by email clients (Outlook, Thunderbird, iPhone) to submit outgoing mail. Enable it in Postfix's master process config:
pi@raspberrypi:~$ sudo nano /etc/postfix/master.cf
Find the submission line and uncomment it along with its options (remove the leading #):
submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
pi@raspberrypi:~$ sudo systemctl restart postfix
Step 5 — Install and Configure Dovecot
Dovecot handles IMAP — it stores your mail on disk and serves it to email clients:
pi@raspberrypi:~$ sudo apt install -y dovecot-core dovecot-imapd dovecot-lmtpd
Configure mail storage
pi@raspberrypi:~$ sudo nano /etc/dovecot/conf.d/10-mail.conf
Find and set mail_location:
mail_location = maildir:~/Maildir
Configure authentication
Dovecot handles authentication for both its own IMAP connections and for Postfix SASL (so users can send authenticated mail):
pi@raspberrypi:~$ sudo nano /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = no # allow for now — Part 2 enforces TLS auth_mechanisms = plain login
Expose the auth socket to Postfix
pi@raspberrypi:~$ sudo nano /etc/dovecot/conf.d/10-master.conf
Find the service auth block and add the Postfix socket (uncomment and edit the existing lines):
service auth { # ...existing content... unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } }
Configure IMAP to listen on all interfaces
pi@raspberrypi:~$ sudo nano /etc/dovecot/conf.d/15-lda.conf
postmaster_address = postmaster@yourdomain.com
pi@raspberrypi:~$ sudo systemctl restart dovecot pi@raspberrypi:~$ sudo systemctl status dovecot ● dovecot.service - Dovecot IMAP/POP3 email server Active: active (running)
Step 6 — Create Mail Users
In this setup, email accounts map to Linux user accounts. Each user needs a system account and a Maildir folder:
# Create a new mail user (e.g. for philip@yourdomain.com) pi@raspberrypi:~$ sudo useradd -m -s /usr/sbin/nologin philip pi@raspberrypi:~$ sudo passwd philip New password: Retype new password: passwd: password updated successfully # Create the Maildir structure pi@raspberrypi:~$ sudo -u philip maildirmake.dovecot /home/philip/Maildir # If maildirmake.dovecot is not found: pi@raspberrypi:~$ sudo mkdir -p /home/philip/Maildir/{cur,new,tmp} pi@raspberrypi:~$ sudo chown -R philip:philip /home/philip/Maildir
-s /usr/sbin/nologin flag prevents the mail user from logging in via SSH — they can only authenticate for email. This is a security best practice for mail-only accounts.
Step 7 — Install Roundcube Webmail
Roundcube is a polished, browser-based email client that talks to Dovecot over IMAP. Install it and its database:
pi@raspberrypi:~$ sudo apt install -y roundcube roundcube-mysql
The installer will ask:
- Configure database with dbconfig-common? → Yes
- Database password for roundcube: → enter a strong password
Configure Roundcube to connect to Dovecot
pi@raspberrypi:~$ sudo nano /etc/roundcube/config.inc.php
Set these values:
// IMAP server — points to Dovecot running locally $config['imap_host'] = 'tls://localhost:143'; // change to ssl://localhost:993 in Part 2 // SMTP server — points to Postfix running locally $config['smtp_host'] = 'localhost'; $config['smtp_port'] = 587; $config['smtp_user'] = '%u'; // use the IMAP username for SMTP auth $config['smtp_pass'] = '%p'; // use the IMAP password for SMTP auth // Default domain filled in on login screen $config['username_domain'] = 'yourdomain.com'; // Product name shown in the browser tab $config['product_name'] = 'My Mail';
Configure Apache to serve Roundcube
pi@raspberrypi:~$ sudo nano /etc/apache2/sites-available/roundcube.conf
<VirtualHost *:80> ServerName webmail.yourdomain.com DocumentRoot /var/lib/roundcube/public_html Redirect permanent / https://webmail.yourdomain.com/ </VirtualHost> <VirtualHost *:443> ServerName webmail.yourdomain.com DocumentRoot /var/lib/roundcube/public_html SSLEngine on SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem <Directory /var/lib/roundcube/public_html> Options -Indexes AllowOverride All Require all granted </Directory> </VirtualHost>
pi@raspberrypi:~$ sudo a2ensite roundcube.conf pi@raspberrypi:~$ sudo certbot --apache -d webmail.yourdomain.com pi@raspberrypi:~$ sudo systemctl reload apache2
Step 8 — Send and Receive a Test Email
Test sending from the command line
pi@raspberrypi:~$ echo "Test email body" | mail -s "Test from Pi" philip@yourdomain.com # Send a test to an external address to verify outbound delivery: pi@raspberrypi:~$ echo "Hello from my Pi mail server" | mail -s "Pi test" your.gmail@gmail.com
Watch the mail log in real time
pi@raspberrypi:~$ sudo tail -f /var/log/mail.log Jun 11 10:23:41 mail postfix/smtp[1234]: A1B2C3D4: to=<your.gmail@gmail.com>, relay=gmail-smtp-in.l.google.com[142.250.x.x]:25, status=sent (250 2.0.0 OK)
Test receiving via Roundcube
Open https://webmail.yourdomain.com in a browser. Log in with the mail user's credentials (username: philip, password: the one you set in Step 6). Send yourself an email from Gmail or any other account. It should arrive in your inbox within a minute.
Test IMAP from a desktop client
In Outlook, Thunderbird, or iPhone Mail, add a new account manually with these settings:
| Setting | Value |
|---|---|
| Incoming (IMAP) server | mail.yourdomain.com, port 143 (993 after Part 2) |
| Outgoing (SMTP) server | mail.yourdomain.com, port 587 |
| Username | philip (the Linux username) |
| Password | the password set in Step 6 |
| Authentication | Normal password |
Troubleshooting Common Problems
sudo ss -tlnp | grep :25; (3) your firewall isn't blocking it: sudo ufw status. If your ISP is blocking port 25, you cannot receive external email without their cooperation.sudo tail -50 /var/log/mail.log. If you see "SASL authentication failed", verify Dovecot's auth socket is running: ls -la /var/spool/postfix/private/auth.id philip; (2) the password is correct: su - philip (this will fail with nologin shell but confirms the password); (3) Dovecot's auth log: sudo journalctl -u dovecot | grep auth | tail -20. If you see "unknown user", the username field in your mail client should be just philip (not philip@yourdomain.com).sudo systemctl status dovecot; (2) the IMAP host in config.inc.php is set to tls://localhost:143 (not an external hostname); (3) Roundcube's error log: sudo tail -f /var/log/roundcube/errors.log. A common issue is PHP's OpenSSL extension not trusting the local certificate — temporarily set $config['imap_host'] = 'localhost' (no TLS) to confirm connectivity, then add the SSL config back.ping 8.8.8.8; (2) DNS resolution works: dig MX gmail.com; (3) if inet_protocols = ipv6 is set but your network doesn't have IPv6, change it to ipv4 and restart Postfix. View the queue: mailq. Force a retry: sudo postfix flush.sudo postfix check — it will show exactly which line has a problem. The most common mistake is a line that accidentally has no value (just the key followed by =) or an extra blank line with leading whitespace (Postfix treats indented lines as continuations).What's Next — Part 2
At this point you have a functional mail server, but it has two significant weaknesses:
- No TLS encryption — passwords and email content are sent in plaintext between your devices and the server
- No email authentication — other mail servers have no way to verify that mail claiming to be from your domain actually came from your Pi, so most of it will land in spam
Part 2 fixes both of these. It covers:
- Enabling TLS for Postfix (STARTTLS on port 587) and Dovecot (IMAPS on port 993)
- Adding an SPF DNS record — tells other servers which IPs are allowed to send from your domain
- Setting up DKIM signing with OpenDKIM — cryptographically signs every outgoing email
- Adding a DMARC DNS record — tells receiving servers what to do with mail that fails SPF/DKIM
- Installing SpamAssassin to filter incoming junk
Quick Reference
| Task | Command |
|---|---|
| View mail queue | mailq |
| Flush (retry) mail queue | sudo postfix flush |
| Watch mail log live | sudo tail -f /var/log/mail.log |
| Test Postfix config | sudo postfix check |
| Reload Postfix config | sudo postfix reload |
| Restart Postfix | sudo systemctl restart postfix |
| Restart Dovecot | sudo systemctl restart dovecot |
| Send a test email | echo "body" | mail -s "subject" user@domain.com |
| Check Dovecot auth log | sudo journalctl -u dovecot -f |
| List mail users' Maildirs | ls ~/Maildir/new/ |
| Check DNS MX record | dig MX yourdomain.com +short |
| Check if port 25 is open | sudo ss -tlnp | grep :25 |
| Postfix main config | /etc/postfix/main.cf |
| Postfix process config | /etc/postfix/master.cf |
| Dovecot config directory | /etc/dovecot/conf.d/ |
| Roundcube config | /etc/roundcube/config.inc.php |
| Roundcube error log | /var/log/roundcube/errors.log |
| Test MX / mail health | mxtoolbox.com |