Email Server - Part 1

More Raspberry Pi Projects

Project 2 — Email Server Part 1: Postfix, Dovecot & Roundcube

1/2
Part 1 — Installing and configuring the mail stack Part 2 covers TLS, SPF, DKIM, DMARC and spam filtering
What you will build A working mail server that can send and receive email, with a web-based inbox via Roundcube
Difficulty Advanced — the most complex project in this course
Time to complete 2 – 4 hours (Parts 1 and 2 together)
Recommended hardware Raspberry Pi 4 (2 GB+ RAM), reliable internet connection with static or DDNS hostname

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:

Port 25 may be blocked by your ISP. Many residential broadband providers block inbound connections on port 25 (the standard SMTP port) to prevent customers from running mail servers. Check this before anything else — if port 25 is blocked, you cannot receive email from the outside world without a workaround.

Test it: from outside your network (e.g. your mobile data connection), run:
telnet mail.yourdomain.com 25

If 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.
Dynamic IP addresses cause deliverability problems. Many email servers check that the sending IP has a matching reverse DNS (PTR) record. Residential IP addresses rarely have this. If deliverability matters (and it does!), you either need your ISP to set a PTR record for your IP, or you need a static IP. Part 2 covers how to minimise this with SPF, DKIM, and DMARC even on a dynamic IP — but it remains an uphill battle on residential connections.

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:

The mail stack you will build
📧 Another mail server Gmail, Outlook, etc.
──►
📮 Postfix MTA — receives & sends mail port 25 (in) / 587 (submit)
──►
📬 Dovecot IMAP — stores & serves mail port 143 / 993 (TLS)
──►
🌐 Roundcube Webmail UI via Apache / HTTPS

MUA (phone/desktop client) connects to Dovecot (IMAP) to read mail. Postfix handles all sending and receiving between servers.

ComponentRoleAnalogy
PostfixMTA — Mail Transfer Agent. Accepts incoming SMTP connections from other mail servers and delivers outgoing mail.The postal sorting office
DovecotIMAP server. Stores mail in mailboxes on disk and serves it to email clients (Outlook, Thunderbird, iPhone Mail).Your personal PO Box
RoundcubeWebmail. 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

🌐 A domain name You must own a domain to run a mail server. You cannot use a DDNS subdomain (like duckdns.org) — you need full DNS control to set MX, SPF, and DKIM records.
📡 Port 25 not blocked Confirm your ISP allows inbound connections on port 25 before starting. See the warning above.
🔀 Router port forwarding Ports 25, 587, 143, and 993 must be forwarded to your Pi. Port 80 and 443 for Roundcube webmail.
🍓 Raspberry Pi 4 2 GB RAM minimum. The mail stack itself is lightweight but Roundcube + Apache + MariaDB add up.
🔧 Apache + MariaDB Already installed if you completed Course 1 Project 1 or Course 2 Project 1 (Nextcloud).
📋 Patience DNS changes take time to propagate. Some steps require waiting 30–60 minutes before they can be tested. Don't rush.

Ports to forward on your router

PortProtocolServicePurpose
25TCPPostfix SMTPReceiving email from other mail servers
587TCPPostfix SubmissionSending email from your devices (authenticated)
143TCPDovecot IMAPReading email (unencrypted — Part 2 replaces with 993)
993TCPDovecot IMAPSReading email over TLS (set up in Part 2)
443TCPApache HTTPSRoundcube 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.

TypeNameValuePurpose
AmailYour public IP addressPoints 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
Don't continue until both DNS records resolve correctly. Postfix will use these records when identifying itself to other mail servers, and getting them wrong at this stage causes hard-to-diagnose problems later.

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 (not mail.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
The -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:

SettingValue
Incoming (IMAP) servermail.yourdomain.com, port 143 (993 after Part 2)
Outgoing (SMTP) servermail.yourdomain.com, port 587
Usernamephilip (the Linux username)
Passwordthe password set in Step 6
AuthenticationNormal password

Troubleshooting Common Problems

⚠ Outbound email to Gmail/Outlook arrives in spam
This is expected at this stage — you haven't set up SPF, DKIM, or DMARC yet. Part 2 addresses this. The mail is being delivered; check the spam/junk folder to confirm. Also check that your sending IP is not on a blocklist at mxtoolbox.com/blacklists.aspx.
⚠ "Connection refused" when testing port 25 externally
Either your ISP is blocking port 25, or port forwarding is not set up. Confirm: (1) port 25 is forwarded in your router to the Pi's local IP; (2) Postfix is listening: 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.
⚠ Postfix reports "Relay access denied" when sending
You are trying to send without authentication (or authentication is failing). Ensure the email client is configured to use SMTP port 587 with username/password authentication, not port 25. Check the Postfix log: 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.
⚠ Dovecot IMAP login fails — "Authentication failed"
The username or password is wrong, or the user doesn't exist as a Linux user. Verify: (1) the user exists: 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).
⚠ Roundcube shows "Connection to storage server failed"
Roundcube cannot connect to Dovecot. Check: (1) Dovecot is running: 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.
⚠ Mail is queued but not delivered — "Network is unreachable"
Postfix can't reach external mail servers. Check: (1) the Pi has internet access: 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.
⚠ "Fatal: main.cf: missing setting" on Postfix start
A syntax error in main.cf. Check the config before restarting: 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

TaskCommand
View mail queuemailq
Flush (retry) mail queuesudo postfix flush
Watch mail log livesudo tail -f /var/log/mail.log
Test Postfix configsudo postfix check
Reload Postfix configsudo postfix reload
Restart Postfixsudo systemctl restart postfix
Restart Dovecotsudo systemctl restart dovecot
Send a test emailecho "body" | mail -s "subject" user@domain.com
Check Dovecot auth logsudo journalctl -u dovecot -f
List mail users' Maildirsls ~/Maildir/new/
Check DNS MX recorddig MX yourdomain.com +short
Check if port 25 is opensudo 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 healthmxtoolbox.com