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
- Set up DMARC reporting and read the aggregate reports. See Reading DMARC reports.
- Plan for DKIM key rotation. See DKIM key rotation.
- Monitor your IP reputation. See Your IP got blacklisted.
Also Read
Powered by WHMCompleteSolution