The server: a fresh Ubuntu 22.04+ or Debian 11+ VPS, root or passwordless
sudo over SSH, 1 GB RAM (2 GB if you enable ENABLE_ANTIVIRUS).
Outbound TCP 25 must be unblocked by your provider, check this before starting.
| Application | Role |
|---|---|
| Postfix | MTA. Virtual domains, submission on 587/465, sender-login enforcement |
| Dovecot | IMAP, optional POP3, Maildir, LMTP delivery, file-backed users |
| rspamd | DKIM signing and verification, spam scoring, SPF, DMARC |
| Redis | Backs rspamd's Bayes classifier, greylisting and rate limits |
| ClamAV | Antivirus scanning, driven by rspamd (optional, off by default) |
| certbot | TLS, one certificate per mail host, selected by SNI |
| nginx | Autoconfig/Autodiscover and optional MTA-STS (optional, default on) |
| fail2ban | Mail jails (optional, default on) |
| UFW | Mail port rules, added only when UFW is already active |
git clone https://github.com/crnst8/mailstack.git
cd mailstack
ln -s "$PWD/bin/mailstack" /usr/local/bin/mailstack # optionalmailstack initWrites mailstack.conf in the current directory.
mailstack checkVerifies: server reachable and supported, outbound port 25, hostname A record, reverse DNS, inbound ports, and warns if a domain still routes mail elsewhere.
mailstack dns show # print the records
mailstack dns apply # or let Cloudflare do it
mailstack dns verify # confirm they resolveEach domain needs an A record for its mail host, MX, SPF, DMARC, a DKIM
key, and optional SRV records for autodiscovery.
Reverse DNS is not managed in this setup. Set the PTR for your server's IP to your mail hostname in your VPS provider's control panel.
mailstack deployInstalls packages, writes configuration, generates DKIM keys, requests certificates, and starts everything. Run this again when changing configs.
mailstack user add you@example.comPoint a mail client at mail.example.com, IMAP 993 and SMTP 465.
mailstack status # services, certificates, queue
mailstack audit # full health and deliverability check
mailstack user list
mailstack user passwd you@example.com
mailstack domain add example.org && mailstack deploy
mailstack cert renewmailstack audit checks for open relay, TLS posture, DKIM/DNS agreement,
certificate expiry, blocklist listings, PTR mismatch, and a stuck queue. Exits
non-zero on anything blocking, so it works in cron.
mailstack migrate --from root@old-serverSnapshots the old server (delivery pauses briefly), provisions the new one from your config, and restores mailboxes, credentials, DKIM keys and certificates. DNS is unchanged and nothing on the old server is deleted.
Snapshot and restore separately:
mailstack snapshot backup.tar.gz
mailstack restore backup.tar.gz # --full for a byte-for-byte cloneRestore brings across state only (mail, passwords, keys, certs) leaving the new host's freshly generated configuration in place.
Everything lives in mailstack.conf. See
examples/mailstack.conf for the full list, or run
mailstack config to print current values.
| Setting | Default | |
|---|---|---|
DOMAINS |
— | Space-separated mail domains |
MAIL_HOSTNAME |
— | SMTP hostname; needs a matching PTR |
DNS_PROVIDER |
manual |
manual or cloudflare |
ENABLE_ANTIVIRUS |
no |
Adds ClamAV, which needs ~1.1 GB RAM on its own |
HOST_MODE |
coexist |
coexist or dedicated |
DMARC_POLICY |
none |
none, quarantine or reject |
Create a token at dash.cloudflare.com → My Profile → API Tokens with Zone → DNS → Edit and Zone → Zone → Read, scoped to the zones you manage. Put it in the config or, better, keep it out:
CLOUDFLARE_API_TOKEN_CMD=pass show cloudflare/dns
With Cloudflare configured, certificates use the DNS-01 challenge, so renewal works even if port 80 is closed.
- Snapshots contain mailbox password hashes, private DKIM keys, TLS keys and all
mail. They are mode
0600and not encrypted, encrypt them at rest. - The Cloudflare token is copied to the server only when the ACME DNS challenge needs it.
- mailstack does not enable UFW. It only adds rules to an already-active UFW.
- System accounts cannot log in to IMAP; Dovecot uses a dedicated passwd-file.
Mail goes to spam. Run mailstack audit first. Usually PTR, DKIM, or a new
IP with no sending reputation.
Certificates fail to issue. The name must resolve to the server and port 80 must be reachable, or use Cloudflare DNS-01.
outbound TCP 25 is blocked. Ask your provider to open it.
rspamd will not start. Check rspamadm configtest, then the journal. A
common cause is a config file the daemon cannot read: it runs as _rspamd,
while configtest runs as root and will pass files the daemon cannot open. Set
ENABLE_ANTIVIRUS=no, then mailstack deploy.
See docs/troubleshooting.md and
docs/deliverability.md for more.
MIT.