Knowledgebase › Postfix + Dovecot from scratch — production-grade mail on a LYLIX VPS

Postfix + Dovecot from scratch — production-grade mail on a LYLIX VPS

Running your own mail server is harder than it was in 2005 but it's still very doable. This article walks the full Postfix + Dovecot setup on a current Debian VPS, with the authentication, TLS, and anti-spam controls that you actually need in 2026.

Before you start

Make sure you have:

  • A static IPv4 address with reverse DNS (PTR) set to your mail server's hostname. See Reverse DNS.
  • Forward DNS — A record pointing your hostname to the IP, MX record for the domain pointing at the hostname.
  • SPF, DKIM, and DMARC records ready to add. See Mail prerequisites.
  • Outbound port 25 reachable from your VPS. Confirm with telnet smtp.gmail.com 25 — if it connects, you're good.
  • Realistic expectations about reputation for a new IP. See Sender reputation warm-up.

Install

apt update
apt install postfix postfix-pcre dovecot-imapd dovecot-pop3d \
            dovecot-lmtpd opendkim opendkim-tools \
            certbot mailutils

The postfix installer asks for a config type — pick "Internet Site" and enter your mail hostname (e.g., mail.example.com).

TLS certificates

Use Let's Encrypt:

certbot certonly --standalone -d mail.example.com

Or use DNS-01 if port 80 is awkward (see ACME DNS-01).

Postfix configuration

/etc/postfix/main.cf:

# Hostname & domain
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain

# What we accept locally
mydestination = localhost
local_recipient_maps = unix:passwd.byname $alias_maps

# Network & relay policy
mynetworks = 127.0.0.0/8 [::1]/128
inet_interfaces = all
inet_protocols = all

# Virtual mailboxes (we'll define users below)
virtual_mailbox_domains = example.com
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_alias_maps = hash:/etc/postfix/virtual

# Use Dovecot's LMTP for delivery
virtual_transport = lmtp:unix:private/dovecot-lmtp

# TLS — outbound (sending) and inbound (receiving) on the
# standard SMTP port
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_loglevel = 1

smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_loglevel = 1

# Submission (587) — for authenticated SMTP from clients
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $mydomain

# Restrictions — anti-relay, anti-spam basics
smtpd_helo_required = yes
smtpd_relay_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination
smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    reject_invalid_helo_hostname,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain

# DKIM signing (set up below)
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891

Submission port (587)

Edit /etc/postfix/master.cf — uncomment the submission block:

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Mailbox users

Create system user and dir:

groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail/vhosts -m
mkdir -p /var/mail/vhosts/example.com
chown -R vmail:vmail /var/mail/vhosts

Mailbox map /etc/postfix/vmailbox:

alice@example.com    example.com/alice/
bob@example.com      example.com/bob/

Alias map /etc/postfix/virtual:

postmaster@example.com  alice@example.com
abuse@example.com       alice@example.com

Hash them:

postmap /etc/postfix/vmailbox
postmap /etc/postfix/virtual

Dovecot configuration

/etc/dovecot/dovecot.conf:

protocols = imap pop3 lmtp

mail_location = maildir:/var/mail/vhosts/%d/%n
mail_uid = vmail
mail_gid = vmail

namespace inbox {
    inbox = yes
}

passdb {
    driver = passwd-file
    args = scheme=ARGON2I username_format=%u /etc/dovecot/users
}

userdb {
    driver = static
    args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}

service auth {
    unix_listener /var/spool/postfix/private/auth {
        mode = 0660
        user = postfix
        group = postfix
    }
}

service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
        mode = 0600
        user = postfix
        group = postfix
    }
}

ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_min_protocol = TLSv1.2

Users password file

# Generate hash
doveadm pw -s ARGON2I -p "user-password-here"
# Returns something like {ARGON2I}$argon2i$...

# /etc/dovecot/users
alice@example.com:{ARGON2I}$argon2i$...
bob@example.com:{ARGON2I}$argon2i$...

OpenDKIM

Generate keys:

mkdir -p /etc/opendkim/keys/example.com
cd /etc/opendkim/keys/example.com
opendkim-genkey -s mail -d example.com
chown -R opendkim:opendkim /etc/opendkim/keys

This creates mail.private (the key) and mail.txt (the DNS record to publish).

/etc/opendkim.conf additions:

Domain                  example.com
KeyFile                 /etc/opendkim/keys/example.com/mail.private
Selector                mail
Socket                  inet:8891@127.0.0.1

Publish the DNS TXT record from mail.txt at mail._domainkey.example.com.

Start everything

systemctl enable --now postfix dovecot opendkim
systemctl restart postfix dovecot opendkim

Test

# Send a test mail
echo "Test body" | mail -s "Test" -aFrom:alice@example.com someone@gmail.com

# Watch the log
tail -f /var/log/mail.log

Check the inbox at the receiving end. View the email headers — look for:

  • SPF: pass
  • DKIM: pass
  • DMARC: pass

If all three pass, the receiving server thinks your mail is legitimate. You're set.

Next steps

Also Read

« « Back

Powered by WHMCompleteSolution