--- name: email-server-setup description: Self-host email server (Postfix + Dovecot + DKIM) on Ubuntu. version: 1.0.0 --- # Email Server Setup (Postfix + Dovecot + DKIM) Self-hosted mail server on Ubuntu with virtual mailboxes, SASL authentication, DKIM signing, and IMAPS/SMTPS. ## Architecture ``` Postfix (SMTP:25/465/587) → Dovecot SASL auth → passwd-file → OpenDKIM milter (8891) → DKIM signing Dovecot (IMAP:143/993, POP3:110/995) → Maildir /var/mail/vhosts/ ``` ## Step 1: Install packages ```bash export DEBIAN_FRONTEND=noninteractive sudo debconf-set-selections <<< "postfix postfix/mailname string $DOMAIN" sudo debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'" sudo apt-get update -qq sudo apt-get install -y -qq postfix postfix-pcre dovecot-core \ dovecot-imapd dovecot-pop3d opendkim opendkim-tools certbot ``` ## Step 2: Create vmail user and directories ```bash sudo groupadd -f vmail sudo useradd -g vmail -d /var/mail -s /usr/sbin/nologin vmail sudo mkdir -p /var/mail/vhosts/$DOMAIN sudo chown -R vmail:vmail /var/mail sudo chmod -R 700 /var/mail ``` ## Step 3: Configure Postfix Key settings in `/etc/postfix/main.cf` (see `references/postfix-main.cf` for the full working config): - `virtual_mailbox_domains` — the domain(s) to serve - `virtual_mailbox_base = /var/mail/vhosts` - `virtual_mailbox_maps = hash:/etc/postfix/vmailbox` - SASL via Dovecot: `smtpd_sasl_type = dovecot`, socket at `/var/spool/postfix/private/auth` - TLS certs point to Let's Encrypt path (use self-signed as fallback) - DKIM milter: `smtpd_milters = inet:localhost:8891` Create `/etc/postfix/vmailbox`: ``` user@domain.com domain.com/user/ ``` Hash it: `sudo postmap /etc/postfix/vmailbox` ### Enable submission (587) and SMTPS (465) in master.cf Uncomment the `submission` and `smtps` service blocks. **Replace** the `$mua_client_restrictions`, `$mua_helo_restrictions`, `$mua_sender_restrictions` variables with concrete values — Postfix does not define them by default and `postfix check` will warn endlessly. ## Step 4: Configure Dovecot ### Mail location (`/etc/dovecot/conf.d/10-mail.conf`) ``` mail_location = maildir:/var/mail/vhosts/%d/%n ``` ### Authentication (`/etc/dovecot/conf.d/10-auth.conf`) Disable system auth, enable passwd-file: ``` #!include auth-system.conf.ext !include auth-passwdfile.conf.ext ``` Update `/etc/dovecot/conf.d/auth-passwdfile.conf.ext`: ``` passdb { driver = passwd-file args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/users } userdb { driver = passwd-file args = username_format=%u /etc/dovecot/users } ``` ### Postfix SASL socket (`/etc/dovecot/conf.d/10-master.conf`) Uncomment inside `service auth { }`: ``` unix_listener /var/spool/postfix/private/auth { mode = 0666 } ``` ### Create users ```bash HASH=$(doveadm pw -s SHA512-CRYPT -p 'password') echo "user@domain.com:$HASH::$(id -u vmail):$(id -g vmail)::/var/mail/vhosts/domain.com/user/::" \ | sudo tee -a /etc/dovecot/users sudo chmod 640 /etc/dovecot/users sudo chown root:dovecot /etc/dovecot/users ``` ## Step 5: Configure DKIM ```bash sudo mkdir -p /etc/opendkim/keys/$DOMAIN sudo opendkim-genkey -D /etc/opendkim/keys/$DOMAIN -d $DOMAIN -s mail sudo chown -R opendkim:opendkim /etc/opendkim/keys ``` `/etc/opendkim.conf` (see `references/opendkim.conf`): ``` Socket inet:8891@localhost KeyTable file:/etc/opendkim/KeyTable SigningTable file:/etc/opendkim/SigningTable ``` `/etc/opendkim/KeyTable`: ``` mail._domainkey.domain.com domain.com:mail:/etc/opendkim/keys/domain.com/mail.private ``` `/etc/opendkim/SigningTable`: ``` *@domain.com mail._domainkey.domain.com ``` ## Step 6: SSL certificates ### Temporary: Self-signed ```bash sudo mkdir -p /etc/letsencrypt/live/mail.$DOMAIN sudo openssl req -new -x509 -days 365 -nodes \ -subj "/CN=mail.$DOMAIN" \ -out /etc/dovecot/private/dovecot.pem \ -keyout /etc/dovecot/private/dovecot.key sudo cp /etc/dovecot/private/dovecot.pem /etc/letsencrypt/live/mail.$DOMAIN/fullchain.pem sudo cp /etc/dovecot/private/dovecot.key /etc/letsencrypt/live/mail.$DOMAIN/privkey.pem ``` ### Real: Let's Encrypt (after DNS resolves) ```bash sudo certbot certonly --standalone -d mail.$DOMAIN --agree-tos --email admin@$DOMAIN ``` ## Step 7: DNS records Required records (add at your DNS provider): | Type | Host | Value | Priority | |------|------|-------|----------| | MX | @ | mail.domain.com | 10 | | A | mail | server-ip | — | | TXT | @ | v=spf1 mx ~all | — | | TXT | mail._domainkey | (from /etc/opendkim/keys/domain.com/mail.txt) | — | | TXT | _dmarc | v=DMARC1; p=none; rua=mailto:admin@domain.com | — | Also request **PTR/reverse DNS** from your hosting provider pointing your IP to `mail.domain.com` — critical for deliverability. ## Step 8: Start and verify ```bash sudo systemctl restart dovecot postfix opendkim sudo doveadm auth test user@domain.com 'password' # Must say "succeeded" echo "test" | sudo /usr/sbin/sendmail user@domain.com # Local delivery sudo ss -tlnp | grep -E ':(25|110|143|465|587|993|995)' ``` ## Pitfalls 1. **Postfix `mua_*` warnings** — the submission/smtps blocks in master.cf reference `$mua_client_restrictions` etc. which are not defined by default. Replace them with concrete values or define them in main.cf. Otherwise `postfix check` produces hundreds of warnings. 2. **OpenDKIM restart loop** — OpenDKIM may fail to write its PID file to `/run/opendkim/` due to permissions. This causes systemd to restart it repeatedly. The process still starts and binds port 8891; check with `ss -tlnp | grep 8891` rather than relying on systemd status alone. 3. **Dovecot `ssl = yes` line** — on Ubuntu 22.04, the default `10-ssl.conf` has `ssl = yes` already uncommented. Verify before editing. 4. **Port 25 outbound** — many cloud providers block outbound port 25 by default. Request unblocking from your provider for external delivery. 5. **`/etc/dovecot/users` permissions** — must be `640` and owned by `root:dovecot`. Plain `chmod 600` will cause auth failures. 6. **Maildir vs mbox** — the default Dovecot config on Ubuntu uses mbox format (`mbox:~/mail:INBOX=/var/mail/%u`). You MUST change it to Maildir for virtual mailbox compatibility.