41 checks per scan34 blacklistsSPF, DKIM, DMARC, MTA-STS, BIMINo signup
← The AcademyIntermediate · 8h · free

Mailcow: Self-Hosted Mail Server Mastery

12-module Mailcow course: install, configure, harden, scale, and troubleshoot the Dockerized Postfix + Dovecot + Rspamd + SOGo mail stack.

12Modules
26Lessons
8hEstimated
$0Cost

Module index

Module 013 lessons

Introduction to Mailcow

What Mailcow is, why it exists, and how the Dockerized stack fits together.

01

What is Mailcow: Dockerized?

Mailcow is an open-source, Dockerized mail server suite that bundles Postfix, Dovecot, Rspamd, ClamAV, SOGo, ACME, Redis, MariaDB, Solr, Watchdog, and Nginx into a single, self-contained stack.

Why Mailcow exists:

  • A modern alternative to monolithic builds (iRedMail, Mail-in-a-Box) and ancient hand-rolled Postfix/Dovecot setups.
  • Each component runs in its own container, so upgrades are atomic and reproducible.
  • Maintained by a small German team (Servercow / The Infrastructure Company GmbH) with an active community.

The container map:

Container Role
postfix-mailcow SMTP MTA — accepts and relays mail on 25/465/587
dovecot-mailcow IMAP/POP3/Sieve/LMTP — mailbox storage and delivery
rspamd-mailcow Content filtering, DKIM signing, greylisting
nginx-mailcow TLS termination + reverse proxy for UI and webmail
sogo-mailcow Groupware webmail + CalDAV/CardDAV/ActiveSync
mysql-mailcow Account metadata, ACLs, transport maps
redis-mailcow Caching for Rspamd, fail2ban, sessions
acme-mailcow Let's Encrypt cert automation
watchdog-mailcow Internal health probe + Telegram/email alerts
clamd-mailcow Optional virus scanning

Two flavors: Mailcow ships in a single docker-compose.yml and a config file (mailcow.conf) generated by ./generate_config.sh. There is no enterprise edition — everything is GPLv3.

Key takeaways
  • Mailcow is a Docker bundle of Postfix, Dovecot, Rspamd, SOGo and friends
  • Each role runs in its own container — atomic upgrades and clear blast radius
  • All configuration lives in mailcow.conf + data/ volumes — easy to back up
02

When (and when not) to self-host with Mailcow

Self-hosting mail in 2026 is harder than ever. Inbox providers (Gmail, Microsoft 365, Yahoo) are aggressive about new senders, dynamic IPs, and weak authentication. Mailcow makes the operations easy — it does not solve deliverability for you.

Good fits for Mailcow:

  • Internal mail for a team of 5–500 with a clean static IP and reverse DNS.
  • Custom-domain mailboxes for an agency or boutique hosting product.
  • High-control workloads (legal, healthcare) where you cannot send through a third party.

Poor fits:

  • Bulk marketing or cold outreach — use a dedicated sending platform like KumoMTA or a relay.
  • Residential or dynamic IPs — most providers refuse mail from these ranges.
  • Anyone who cannot keep a server patched within 48 hours of a CVE.

Cost reality check (2026):

Item Typical monthly cost
VPS (4 vCPU / 8 GB / 80 GB SSD) $20–$40
Static IPv4 with clean reputation included or +$2
Backup storage (S3-compatible) $1–$5
Your time (4 hrs/month maintenance) priceless

🎯 Rule of thumb: self-hosting beats $6/user/month Google Workspace once you cross ~10 mailboxes — but only if you actually maintain it.

Key takeaways
  • Mailcow solves ops, not deliverability — IP reputation is still on you
  • Need a static IP with clean rDNS and PTR; residential ranges won't deliver
  • Budget time for monthly upgrades and patching, not just dollars
03

Mailcow vs Mail-in-a-Box vs iRedMail

Three popular self-hosted suites — pick by team size and how much control you need.

Feature Mailcow Mail-in-a-Box iRedMail
Packaging Docker compose Bare-metal Ubuntu installer Bare-metal installer
Admin UI Modern React-style Minimal Basic + paid Pro
Webmail SOGo Roundcube Roundcube
DKIM Auto via Rspamd Auto via OpenDKIM OpenDKIM
Anti-spam Rspamd (modern) SpamAssassin SpamAssassin/Amavis
Multi-domain Excellent Limited Good
HA / clustering Possible, manual No Pro only
Updates ./update.sh OS package Manual
License GPLv3 CC0 GPL + paid extras

Choose Mailcow when: you want the modern stack, comfortable with Docker, need multi-domain and rich admin UI.

Choose Mail-in-a-Box when: you want one mailbox for one domain on one tiny VPS and never touch it again.

Choose iRedMail when: you specifically need Amavis/SpamAssassin compatibility or already manage Postfix by hand.

Key takeaways
  • Mailcow's edge: Docker isolation, Rspamd, multi-domain, polished UI
  • Mail-in-a-Box wins on simplicity for a single domain
  • iRedMail is the bare-metal traditionalist's choice
Module 023 lessons

Server Prerequisites & DNS

What to provision before you install: OS, IP, rDNS, firewall, DNS records.

04

VPS sizing and OS choice

Minimum for ~50 mailboxes: 2 vCPU, 6 GB RAM, 50 GB SSD. Bump to 4 vCPU / 8 GB / 80 GB for 200+.

OS: Debian 12 (bookworm) is the most-tested target. Ubuntu 22.04/24.04 LTS also works. Avoid CentOS Stream and RHEL derivatives — Docker support is messier and the official docs don't track them.

Filesystem: ext4 or xfs. ZFS works but the docs assume bind-mounted directories — skip overlays.

Swap: keep 2 GB; Rspamd and ClamAV occasionally spike.

Outbound port 25: verify the provider does not block it. Hetzner, OVH, Vultr (with request), and DigitalOcean (with request) all allow it. AWS EC2 and Google Cloud block 25 by default with no easy exception.

Key takeaways
  • Debian 12 + ext4 is the safest baseline
  • Confirm outbound port 25 is open before you order the VPS
  • Size for Rspamd headroom: 6 GB minimum on small setups
05

DNS records every Mailcow host needs

Set these BEFORE the first install so Let's Encrypt and DKIM succeed on first run.

Record Example Purpose
A / AAAA mail.example.com → 198.51.100.10 Hostname for UI, SMTP, IMAP
PTR (rDNS) 198.51.100.10 → mail.example.com Required by Gmail/M365
MX example.com MX 10 mail.example.com Inbound routing
SPF v=spf1 mx ~all (initially) Authorize Mailcow's IP
DKIM TXT at dkim._domainkey.example.com Added after Mailcow generates the key
DMARC v=DMARC1; p=none; rua=mailto:dmarc@example.com Reports first, enforce later
MTA-STS _mta-sts.example.com TXT + policy file Modern TLS enforcement
TLS-RPT _smtp._tls.example.com TXT TLS failure reports
Autodiscover autodiscover.example.com CNAME mail.example.com Outlook auto-config
Autoconfig autoconfig.example.com CNAME mail.example.com Thunderbird auto-config

⚠️ PTR is non-negotiable. Gmail and Microsoft 365 both reject mail from IPs without matching forward+reverse DNS. Most VPS providers set this in their control panel, not your DNS host.

Key takeaways
  • Set A, PTR, and MX before the install — Let's Encrypt needs A working
  • DKIM is added AFTER Mailcow generates the key (next module)
  • PTR must match the HELO/EHLO hostname, not just any reverse
06

Firewall and fail2ban basics

Mailcow expects these inbound ports open from the internet:

Port Service
25 SMTP (inbound MX)
465 SMTPS submission
587 Submission with STARTTLS
993 IMAPS
995 POP3S
4190 ManageSieve
80 HTTP (ACME challenge + redirect)
443 HTTPS (admin UI + webmail)

UFW example:

ufw allow 25,80,443,465,587,993,995,4190/tcp

fail2ban inside Mailcow: the netfilter-mailcow container already runs fail2ban-style banning. Don't run host-level fail2ban on the same ports — it fights the container's NFTables rules.

Block port 110 and 143 (cleartext IMAP/POP3). Mailcow leaves them open by default for legacy clients; close them at the firewall unless you need them.

Key takeaways
  • Open 25, 80, 443, 465, 587, 993, 4190 — close 110/143 if not needed
  • Let Mailcow's netfilter-mailcow container handle ban lists
  • Don't double-ban with host fail2ban — picks fights with the container
Module 032 lessons

Installation & First-Run Configuration

Clone, generate config, bring the stack up, and finish the web installer.

07

Installing Docker and Mailcow

Step 1 — Install Docker Engine (NOT Docker Desktop):

curl -sSL https://get.docker.com | sh

This pulls Docker CE plus the compose plugin (docker compose, no hyphen).

Step 2 — Clone Mailcow:

cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized

/opt/mailcow-dockerized is the documented home. Stick to it — every doc, support thread, and update script assumes this path.

Step 3 — Generate config:

./generate_config.sh

You'll be asked for MAILCOW_HOSTNAME — this MUST be a real DNS A record (e.g. mail.example.com) and MUST be a subdomain, not the bare domain.

Step 4 — Pull and start:

docker compose pull
docker compose up -d

First run takes 5–10 minutes while images download and DBs initialize. Tail logs with docker compose logs -f to spot issues early.

Key takeaways
  • Install Docker Engine via get.docker.com, not Desktop
  • MAILCOW_HOSTNAME must be a subdomain with a working A record
  • Always install in /opt/mailcow-dockerized
08

First login and admin setup

Open https://mail.example.com and log in with the default credentials:

  • User: admin
  • Password: moohoo

First three things to do — in order:

  1. Change the admin password. Settings → Access → Administrators.
  2. Add your domain. Email → Configuration → Domains → Add. Enable "Relay non-existing mailboxes via..." only if you bridge to another MTA.
  3. Create your first mailbox. Email → Configuration → Mailboxes → Add. Pick a quota (5–10 GB is typical).

Verify HTTPS: Mailcow runs the acme-mailcow container which auto-requests a Let's Encrypt cert for MAILCOW_HOSTNAME + any autodiscover. / autoconfig. hostnames it detects. Check docker compose logs acme-mailcow if the cert isn't issued within 5 minutes.

Test sending: from the new mailbox, send to check-auth@verifier.port25.com — you'll get an authentication report back showing SPF/DKIM/DMARC alignment.

Key takeaways
  • Default admin:moohoo — change immediately
  • Add domain → add mailbox → verify TLS in that order
  • port25.com's check-auth is the fastest end-to-end test
Module 042 lessons

Domains, Mailboxes, Aliases & ACLs

Multi-domain hosting, aliases, mailbox quotas, and per-user permissions.

09

Managing multiple domains

Mailcow is designed for multi-tenant hosting. Each domain has independent quotas, DKIM keys, ACLs, and relay rules.

Per-domain settings that matter:

Setting What it does
Default mailbox quota Cap for new mailboxes (override per user)
Domain quota Total disk usage across all mailboxes
Max number of mailboxes Hard cap for the domain
Relay all recipients Bridge mode — Mailcow accepts and forwards on
Relay non-existing mailboxes Forward to upstream for unknown locals
Backup MX Spool mail when the primary is offline

DKIM per domain: Email → Configuration → ARC/DKIM keys → Add. Always generate a 2048-bit RSA key with a selector like mailcow or s2025. Mailcow displays the DNS TXT — paste it into your zone before sending. DKIM signing only happens once the public key is verifiable.

Key takeaways
  • Each domain has its own DKIM key, quotas, and ACLs
  • Use 2048-bit RSA keys; rotate selectors yearly
  • Mailcow won't sign until it can verify the public key in DNS
10

Aliases, catch-alls, and ACLs

Aliases (Email → Configuration → Aliases):

  • sales@example.com → alice@example.com, bob@example.com (group alias)
  • @example.com → catch@example.com (catch-all — use sparingly, spam magnet)
  • Aliases can target external addresses; SPF/DMARC alignment will break unless you enable Rspamd's SRS rewriting (on by default in modern Mailcow).

Mailbox ACLs: Each mailbox can be granted permission to:

  • Spam-train / ham-train messages
  • Manage app passwords (for IMAP clients that don't speak OAuth)
  • Edit their own Sieve filters
  • Modify TLS policy
  • Use SMTP from external IPs

Strip permissions you don't want users to touch — this prevents accidental TLS downgrades that nuke deliverability.

🎯 Tip: for shared mailboxes (support@), create a real mailbox and grant other users Shared folder access via SOGo. Avoid forwarding-to-many aliases for inbound — replies break threading.

Key takeaways
  • Catch-all aliases are spam magnets — use specific aliases instead
  • ACLs let you lock down dangerous per-user toggles like TLS policy
  • For team inboxes, prefer a shared mailbox over a fan-out alias
Module 052 lessons

Rspamd Tuning

How Rspamd scores mail and how to safely adjust thresholds, modules, and maps.

11

Rspamd architecture inside Mailcow

Rspamd replaces SpamAssassin + Amavis in the Mailcow stack. It's faster (event-driven Lua) and more accurate (gradient-boosted Bayes + DNS lookups).

Score thresholds (default):

Score Action
< 4 Pass (no action)
4 ≤ s < 6 Greylist (defer once)
6 ≤ s < 15 Add spam header, deliver to Junk
≥ 15 Reject at SMTP time

Modules to know:

Module What it does
rbl Queries DNSBLs (Spamhaus, SURBL, etc.)
dkim_signing Outbound DKIM with keys from the UI
arc ARC sealing on forwarded mail
multimap User-supplied allow/block lists
bayes Per-user content classification
neural Optional ML model on top of Bayes
fuzzy_check Hash-based recurring-spam detection

Access the Rspamd UI at https://mail.example.com/rspamd/ — username/password are in mailcow.conf (RSPAMD_UI_PASSWORD).

Key takeaways
  • Default reject threshold is 15.0 — don't lower it blindly
  • Bayes learns per-user; the more 'Junk' moves, the smarter it gets
  • The Rspamd UI is your live diagnostic — bookmark /rspamd/
12

Safe customization with local.d overrides

Never edit files in /override/rspamd/override.d/ — those are version-pinned. Use /data/conf/rspamd/local.d/ for additions and /override.d/ for full replacements.

Common edits:

local.d/actions.conf — adjust thresholds:

reject = 18;
add_header = 7;
greylist = 5;

local.d/multimap.conf — block a sender domain:

SENDER_BLOCKLIST {
  type = "from";
  filter = "email:domain";
  map = "/etc/rspamd/custom/sender_blocklist.map";
  symbols_set = ["BAD_DOMAIN"];
  score = 15.0;
}

local.d/dkim_signing.conf — add an ARC sign requirement for forwarded mail.

After editing, restart:

docker compose restart rspamd-mailcow

⚠️ Don't disable greylisting. It's the single biggest spam reducer in the stack — Mailcow's implementation only defers once and remembers good senders.

Key takeaways
  • Edit local.d/ — never the shipped configs
  • Restart only the rspamd-mailcow container after edits
  • Greylisting is a feature, not a bug — leave it on
Module 062 lessons

SOGo, ActiveSync & Mobile Clients

Webmail, calendar sync, contacts, and mobile device provisioning.

13

SOGo as the webmail and groupware layer

SOGo provides webmail, calendar (CalDAV), contacts (CardDAV), and Microsoft ActiveSync — all bound to the Mailcow user database.

Reach it at: https://mail.example.com/SOGo/

What SOGo gives you:

  • Roundcube-style webmail with conversation view
  • Personal and shared calendars
  • Personal and shared address books
  • ActiveSync (EAS) endpoint for iOS Mail and outdated Outlook

ActiveSync limits: SOGo's EAS implementation is solid for iOS but not recommended for Outlook 2019+. For Outlook, use IMAP + CalDAV (via Outlook CalDav Synchronizer) or move that user to native IMAP.

Disable SOGo entirely by editing mailcow.conf:

SKIP_SOGO=y

Then docker compose down && docker compose up -d. Frees ~500 MB RAM if you only need IMAP.

Key takeaways
  • SOGo = webmail + CalDAV + CardDAV + ActiveSync, all in one
  • iOS works great with EAS; modern Outlook struggles
  • SKIP_SOGO=y disables it cleanly if you don't need groupware
14

Mobile autoconfig and app passwords

Autodiscover/Autoconfig: add CNAME records pointing autodiscover.example.com and autoconfig.example.com at MAILCOW_HOSTNAME. Mailcow ships XML responses for both Apple, Thunderbird, and Outlook clients — users only type their email and password.

App passwords: when 2FA is on (TOTP), users need a per-device app password for IMAP/SMTP clients. Generate in SOGo → Preferences → Mail or via Mailcow UI → Mailbox details. Store the app password in the device keychain — it can't be recovered.

iOS quirks:

  • iOS Mail will sometimes pick POP over IMAP from autodiscover — force IMAP manually if you see it.
  • "Mail Days to Sync" defaults to No Limit and will pull GBs on first sync. Set to 1 month for fresh devices.

Android quirks:

  • Gmail app does not honor autoconfig. Users must add via "Other (IMAP)" manually.
  • K-9 Mail / FairEmail work flawlessly.
Key takeaways
  • Autodiscover + Autoconfig CNAMEs save 90% of provisioning pain
  • App passwords are required when TOTP is on — generate per-device
  • iOS Mail and Apple Calendar are the smoothest path for end users
Module 072 lessons

Backups, Snapshots & Disaster Recovery

What to back up, how, and how to test restores before you need them.

15

What Mailcow stores and where

All persistent state lives in two places:

  1. /opt/mailcow-dockerized/data/ — bind-mounted volumes (configs, certs, Rspamd state, Redis dumps).
  2. Docker named volumes — the heavy stuff (Vmail, MariaDB, Postfix queue, Solr index).

Critical volumes:

Volume What's inside
vmail-vol-1 All mailbox content (the biggest one)
mysql-vol-1 Account DB, ACLs, transports
redis-vol-1 Bayes tokens, fail2ban state
postfix-vol-1 Queue (transient but worth grabbing)
crypt-vol-1 Certificates + DKIM keys

Don't try to back up live Docker volume directories. Use Mailcow's built-in helper:

./helper-scripts/backup_and_restore.sh backup all

It pauses writers, snapshots everything to /var/backup/mailcow_backup_<ts>/, and restarts. Run nightly via cron, then push to S3 / B2 / Wasabi with rclone.

Key takeaways
  • Use Mailcow's backup_and_restore.sh — don't tarball live volumes
  • vmail-vol-1 is the biggest; size your offsite storage for it
  • Keep crypt-vol-1 — losing DKIM keys breaks signing history
16

Restore drills and PITR

Backup ≠ restore. Test the restore path before an incident.

Quarterly drill:

  1. Spin up a fresh VPS with the same hostname (or use /etc/hosts overrides on a workstation).
  2. Install Mailcow as if new.
  3. Run ./helper-scripts/backup_and_restore.sh restore and point at your snapshot.
  4. Log in, verify mailboxes mount, send a test message in and out.

Point-in-time recovery options:

  • ZFS / BTRFS snapshots at the host level — atomic, hourly, near-zero cost.
  • MariaDB binlogs — set MYSQL_BINLOG=y in mailcow.conf to replay individual transactions.
  • S3 versioning on rclone-pushed backups — ransomware insurance.

RTO/RPO targets:

Setup RPO RTO
Daily rclone push 24 h 1–2 h
Hourly ZFS + nightly off-host 1 h 30 min
Synchronous DRBD/replicated vmail Seconds 5 min

For most small teams, daily off-host with quarterly drills is plenty.

Key takeaways
  • An untested backup is a wish — drill quarterly
  • Combine local snapshots (ZFS) with off-host objects (S3)
  • MYSQL_BINLOG=y unlocks transaction-level PITR for the account DB
Module 082 lessons

Upgrades, Migrations & Version Pinning

Stay current without breaking your stack, and move Mailcow between hosts.

17

The update.sh workflow

Mailcow ships update.sh in the repo root. It pulls new container tags, regenerates configs, and brings the stack up.

Standard upgrade:

cd /opt/mailcow-dockerized
./update.sh -c     # show changelog only
./update.sh        # actually upgrade

What it does:

  1. git fetch and check for new commits on master.
  2. Diff your mailcow.conf against mailcow.conf.example and warn about new variables.
  3. docker compose pull new image versions.
  4. docker compose down and up -d.

Best practices:

  • Read ./update.sh -c BEFORE running. Breaking changes are flagged with !!!.
  • Take a backup snapshot first (backup_and_restore.sh backup all).
  • Upgrade in a low-traffic window — there's typically 30–90 s of downtime.
  • After upgrade, check docker compose ps — every container should be healthy or running.
Key takeaways
  • Always run update.sh -c first to read the changelog
  • Back up before upgrading — broken upgrades happen rarely but happen
  • Plan for 30–90 s of mail downtime during the container restart
18

Migrating Mailcow to a new server

Same-IP migration (simplest):

  1. Backup on old: backup_and_restore.sh backup all → push to S3.
  2. Provision new host, install Docker + clone Mailcow.
  3. Copy mailcow.conf over.
  4. Pull the same Mailcow git tag.
  5. Restore from snapshot.
  6. Move the IP (or update DNS).

IP-change migration:

  • Schedule a maintenance window during low traffic.
  • Update DNS A and MX TTLs to 300 s 48 hours before the move.
  • Cut over DNS, then PTR on the new IP, then start the new Mailcow.
  • Expect 1–4 h of soft mail delays as senders retry against the old IP.

Don't mix git tags. Restoring a snapshot taken on version 2025.06 into a 2026.01 install will fail Bayes/DB migrations. Always restore into the same tag, then run update.sh.

Key takeaways
  • Lower DNS TTLs days before an IP-change migration
  • Restore into the same git tag the backup came from, then upgrade
  • PTR change is on the provider — request it before the cutover
Module 092 lessons

Monitoring (Watchdog, Netdata, Prometheus)

Built-in Watchdog plus external metrics, alerts, and dashboards.

19

Watchdog: the built-in health probe

watchdog-mailcow runs continuous internal checks: container health, queue size, certificate expiry, Rspamd response, MariaDB replication lag.

Configure alerts in mailcow.conf:

WATCHDOG_NOTIFY_EMAIL=alerts@yourdomain.com
WATCHDOG_NOTIFY_WEBHOOK=https://hooks.slack.com/services/...
WATCHDOG_NOTIFY_BAN=y           # alert on fail2ban hits
WATCHDOG_SUBJECT="Mailcow alert"

Tuneables:

Variable Purpose Sane default
WATCHDOG_NOTIFY_START Notify when watchdog itself starts n
WATCHDOG_EXTERNAL_CHECKS Probe via 3rd-party DNS resolver y
WATCHDOG_MYSQL_REPLICATION_CHECKS Only if you replicate n

After editing, recreate the container:

docker compose up -d watchdog-mailcow
Key takeaways
  • Watchdog covers 80% of monitoring out of the box
  • Route alerts to email AND a webhook — emails sometimes loop back
  • Restart watchdog-mailcow specifically after editing its env vars
20

External metrics: Netdata, Prometheus, Grafana

For graphs and historical trends, layer something on top.

Netdata (easiest):

bash <(curl -sSL https://my-netdata.io/kickstart.sh)

Auto-discovers Docker containers and exposes per-container CPU/RAM/network. Free cloud dashboard at netdata.cloud.

Prometheus + Grafana (more control):

  • Add the Mailcow postfix-exporter and rspamd-exporter as community containers.
  • Scrape Postfix queue stats, Rspamd action breakdown, MariaDB metrics.
  • Use the community Grafana dashboard ID 17442 as a starting point.

Key SLOs to alert on:

Metric Warning Page
Deferred queue size > 50 > 500
Rspamd reject rate > 30% > 70%
TLS cert expiry < 14 d < 3 d
Disk free on vmail < 20% < 5%
Backup success age > 36 h > 48 h
Key takeaways
  • Netdata is the fast win; Prometheus is the long-term play
  • Alert on backup age, not just backup success
  • Deferred queue > 500 is almost always an outbound delivery problem
Module 102 lessons

Anti-Spam + SPF/DKIM/DMARC in Mailcow

Outbound authentication done right: signing, alignment, and ARC for forwards.

21

Outbound DKIM with Rspamd

Rspamd signs every outbound message that matches a configured DKIM key for the From domain.

Setup flow:

  1. UI → Email → Configuration → ARC/DKIM keys → Add (domain, selector, 2048-bit).
  2. Mailcow shows the DNS TXT to publish. Paste into your DNS host.
  3. Wait for propagation (1–10 min for most providers).
  4. Send a test to check-auth@verifier.port25.com and confirm dkim=pass.

Common gotchas:

  • Selector mismatch: the selector in DNS (s2025._domainkey) must match the one in Mailcow exactly.
  • Multiple keys: Rspamd will sign with all matching keys. Rotate by adding new selector first, waiting 48 h, then removing old.
  • Long DNS records: 2048-bit keys exceed 255 chars and must be split into multiple strings — most DNS hosts handle this automatically.

ARC sealing: enable dkim_signing.conf with sign_authenticated = true; arc = true; so forwarded mail keeps its original auth chain.

Key takeaways
  • Always 2048-bit RSA — 1024 is below modern best practice
  • Rotate keys by adding a new selector first, then removing the old
  • Enable ARC so forwarded mail still validates downstream
22

DMARC enforcement progression

Roll DMARC out in three phases — never jump straight to p=reject.

Phase 1 — Monitor (week 1–4):

v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; fo=1;

Collect aggregate reports. MailVitals' DMARC monitoring parses them automatically; otherwise use Postmark or DMARC.org's parser.

Phase 2 — Quarantine (week 5–8):

v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@yourdomain.com;

Quarantine 25% of failing mail. Read reports for unexpected senders — usually a marketing platform that needs SPF/DKIM added.

Phase 3 — Reject (week 9+):

v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; ruf=mailto:dmarc@yourdomain.com;

Once aggregate reports are clean for 14 days, go to p=reject. Stay there forever.

Subdomain policy: add sp=reject to prevent attackers from using unused subdomains.

Key takeaways
  • Always start at p=none and watch aggregate reports for 2–4 weeks
  • Use pct= to dial in quarantine before jumping to reject
  • sp=reject covers attacks via unused subdomains
Module 112 lessons

Hardening, fail2ban, TLS, MTA-STS

Lock down the server against brute-force, downgrades, and weak ciphers.

23

Tightening fail2ban and rate limits

netfilter-mailcow ships sensible defaults but every public-facing mail server should review them.

UI: Access → Fail2ban tab. Tunables:

Setting Default Recommended
Max attempts 5 3
Find time (s) 600 600
Ban time (s) 1800 86400 (24 h)
Permanent ban after 5 short bans 3

Rate limit submission (per UI → Configuration → Rate limits):

Limit Sane value
Per mailbox / hour 200
Per domain / hour 2000
Per IP / hour (smtp) 100

Block prevention beats cleanup — once an IP hits a Spamhaus listing, you're delisting for weeks.

Key takeaways
  • Tighten fail2ban max attempts to 3 and ban time to 24 h
  • Apply per-mailbox sending rate limits to contain account compromises
  • Permanent bans for repeat offenders cost nothing and stop scanners
24

TLS hardening and MTA-STS

TLS profile (UI → Configuration → Configuration → TLS):

  • Set minimum to TLSv1.2. Disable TLSv1.0/1.1.
  • Cipher suites: pick Intermediate (Mozilla SSL Config Generator profile). Modern breaks too many older relays.

MTA-STS (RFC 8461): advertises that your domain requires TLS for inbound mail. Two pieces:

  1. DNS TXT at _mta-sts.example.com:
v=STSv1; id=20260101;

Bump id= whenever you change the policy.

  1. Policy file at https://mta-sts.example.com/.well-known/mta-sts.txt:
version: STSv1
mode: enforce
mx: mail.example.com
max_age: 604800

TLS-RPT complements MTA-STS — DNS TXT at _smtp._tls.example.com pointing at a reporting mailbox. You'll get JSON reports when remote senders fail TLS to you.

Start mode: testing for two weeks, then switch to enforce once TLS-RPT shows zero failures.

Key takeaways
  • Minimum TLS 1.2 and Intermediate cipher profile — Modern breaks too much
  • Roll out MTA-STS in testing mode first, then enforce after 2 weeks
  • Bump the policy id= every time you change anything
Module 122 lessons

Scaling, Multi-Server & Troubleshooting Playbook

Beyond a single host, plus the diagnostic flow when things go sideways.

25

Scaling patterns for Mailcow

Mailcow is single-host by design. Once you exceed it, three patterns work:

1. Vertical first. A 16 vCPU / 64 GB host handles ~2000 active mailboxes comfortably. Cheaper and simpler than splitting.

2. Split inbound vs storage. Front Mailcow with a dedicated MX relay (Postfix or HAProxy → Mailcow) to handle bursty inbound traffic. Mailcow still owns mailboxes.

3. Multiple Mailcow installs sharded by domain. Each domain lives on one host; a DNS-level MX pool routes per-domain. Simple, robust, doesn't need true clustering.

True HA? Mailcow doesn't ship clustering. DRBD-replicated vmail + MariaDB Galera + keepalived works but you own the integration. Most teams that "need HA" actually need faster recovery — invest in tested restore drills first.

🌍 Geo-redundancy: stand up a passive Mailcow in a second region, rsync vmail and mysqldump nightly. When the primary dies, switch DNS — recovery in <30 min.

Key takeaways
  • Scale vertically as long as you can — clustering is a maintenance tax
  • Sharding by domain across multiple Mailcows is the easiest horizontal path
  • A passive geo replica beats hand-rolled active/active for most teams
26

Diagnostic flow when delivery breaks

When mail stops flowing, walk this checklist in order. Don't restart things first — read logs first.

1. Where is it stuck?

docker compose exec postfix-mailcow postqueue -p | tail -50

Queue empty? Look outbound. Queue piling up? Inbound or remote refusal.

2. What does the bounce say?

docker compose logs --tail=200 postfix-mailcow | grep "status="
  • bounced → remote rejected, read the diagnostic.
  • deferred → temporary, will retry.
  • sent → delivered, check Rspamd/Dovecot.

3. Is Rspamd rejecting?

docker compose logs --tail=200 rspamd-mailcow

Look for action=reject with the matching message-id.

4. Authentication issue? Send to check-auth@verifier.port25.com and read the full report.

5. Listed somewhere? Use MailVitals' blacklist checker for the IP and HELO hostname.

Common root causes (ranked by frequency):

  1. Outbound IP on a DNSBL (Spamhaus, UCEPROTECT).
  2. Reverse DNS broken after VPS reboot.
  3. Let's Encrypt cert expired (ACME container stuck).
  4. Disk full on vmail volume.
  5. mailcow.conf mismatch after manual edit.
Key takeaways
  • Read logs before you restart anything — restarts hide root causes
  • postqueue -p tells you whether it's an in or out problem in one command
  • Most outbound failures trace to IP reputation or rDNS

Now check it against your own domain

The course explains the theory. The scan tells you which parts you have actually got right.

Run a free scan