Tech Digest

Communication

mailcow: dockerized

A complete mail server in 18 containers, with a UI that makes it survivable

CommunicationGPL-3.0 for mailcow's own code; bundled components keep their own licensesMaturearm64 buildsNative OIDC
Last reviewed Profile maintained against the project's own documentation
Operational load
4 / 5
Heavy
Idle memory
3500 MB
typical use ~6144 MB
Moving parts
18
containers in a normal deployment
Backup shape
MySQL dump
needs a dump or a stop

mailcow: dockerized bundles Postfix, Dovecot, Rspamd, SOGo and a web admin UI into one Docker Compose stack. It replaces a Google Workspace or Microsoft 365 mailbox with mail you host on a domain and an IP you control.

Our verdict on mailcow: dockerized#

Self-hosted mail is the highest-risk thing in this whole category and mailcow does not change that. It makes Postfix, Dovecot and Rspamd tractable; it does nothing about the facts that most residential ISPs and several major clouds block outbound port 25 by default, that you cannot set a PTR record for an address you do not control, and that a fresh IP has no sending reputation, so Gmail and Outlook will greylist or bin you for weeks. Answer three questions before you install: can you open outbound 25, can you set reverse DNS to your mail hostname, and is your IP range already listed on the usual blocklists. If any answer is no, either stop or run mailcow with outbound relayed through an SMTP provider and keep only receipt and storage local. Given a clean IP with correct rDNS, this is the best-documented mail stack there is: 18 containers, a documented 6 GiB floor, one update.sh. Receiving mail is easy. Sending it reliably is the entire job.

Who mailcow: dockerized is for#

Choose it if

  • A small business or association on a static IP with a controllable PTR record, leaving Microsoft 365 for cost or jurisdiction reasons.
  • Anyone who wants webmail, calendar and contacts through SOGo from the same box as IMAP, rather than mail alone.
  • Operators happy to relay outbound through Postmark, SES or Mailjet and self-host only receipt, storage and filtering.
  • People who want a UI that manages DKIM keys, aliases, quotas and spam policy instead of hand-editing Postfix maps.

Look elsewhere if

  • You are on a residential connection, a dynamic IP, or a provider that will not unblock port 25. You will not deliver, and no configuration fixes it.
  • You have under 6 GiB of RAM. That is a documented minimum for the default configuration, not a suggestion.
  • Losing three days of mail would be a serious problem and you have never rehearsed a restore of this stack.

What running it actually looks like#

One git clone, generate_config.sh, then docker compose up -d brings up 18 containers: Postfix, Dovecot, Rspamd, ClamAV, SOGo, MariaDB, Redis, nginx, php-fpm, unbound, acme, netfilter, watchdog, dockerapi, memcached, olefy, ofelia and postfix-tlspol. Everything hangs off mailcow.conf, and MAILCOW_HOSTNAME is effectively immutable: changing it after mail exists means reissuing certificates and regenerating DKIM. Before you start, DNS must already have an A record for that hostname, an MX pointing at it, and a PTR record set at your IP provider matching it exactly. Ports 25, 465, 587, 143, 993, 110, 995, 4190, 80 and 443 all need to reach the host, and the acme container wants 80 free. Updates are ./update.sh, which pulls new image tags and restarts the stack.

Resource profile#

Memory
3500 MB idle, around 6144 MB in ordinary use. The docs state a minimum of 6 GiB plus 1 GiB of swap for the default configuration. ClamAV and the Flatcurve full-text index are the greedy components and can both be disabled if you are tight.
CPU and acceleration
1 GHz is the documented floor. Rspamd and ClamAV spike on every inbound message, and the first full-text index build over a freshly migrated mailbox will saturate a core for a while.
Storage growth
20 GiB before any mail, per the docs. The `vmail` volume grows with mailboxes, the Flatcurve index adds a further fraction on top, and Rspamd's Redis-backed Bayes data creeps up slowly.
Operational load
4 of 5, Heavy. Eighteen coupled containers, a MariaDB, a crypt volume that decides whether your backup is readable, and update batches applied through `update.sh`. On top of that sits the permanent external job of keeping DNS, rDNS and IP reputation healthy, which no amount of container tidiness removes.

Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.

Data and backup surface#

helper-scripts/backup_and_restore.sh backup all covers the six components that matter: vmail, crypt, redis, rspamd, postfix and mysql. The crypt volume is the one people forget; it holds the Dovecot mail-crypt keys, and without it your backed-up vmail is unreadable ciphertext, so never capture vmail without crypt from the same moment. Keep mailcow.conf and any data/assets overrides alongside. A live rsync of vmail is acceptable because maildir files are write-once, but MariaDB and Redis need the script's dumps rather than a file copy. ClamAV signatures and the Flatcurve index are regenerable and can be excluded.

Derived backup shape

MySQL dump. mariadb-dump / mysqldump with --single-transaction, then snapshot the dump plus the app's data volume.

Traps and surprises#

The crypt volume decides whether your backup is readable

Dovecot stores mail encrypted and the keys live in the crypt volume, separately from vmail. A backup of vmail alone restores perfectly and gives you a maildir tree nobody can open. The official script backs up crypt as one of its components for exactly this reason. Take vmail and crypt in the same run, verify by restoring to a scratch host and actually opening a message.

MAILCOW_HOSTNAME cannot be changed later

The hostname in mailcow.conf is written into certificates, DKIM records, SOGo configuration and the SMTP banner. Changing it on a running system is not a supported edit; the practical path is a fresh install and a migration of mailboxes. Pick the hostname you will still want in three years, usually mail.yourdomain.tld, before the first generate_config.sh, and set the PTR record to match it exactly.

A Debian 12 to 13 upgrade can steal port 25

The mailcow docs call this out specifically: upgrading the host from Debian 12 to Debian 13 can pull in Exim, which binds port 25 and quietly stops Postfix from accepting inbound mail. The symptom is mail bouncing at other people's servers while your stack looks healthy. Check with ss -lntp | grep :25 after any host distribution upgrade and remove or mask the offending MTA.

PTR is set at your IP provider, not in your DNS zone

Reverse DNS lives with whoever owns the IP block, so it is a control panel setting at Hetzner, OVH, DigitalOcean or your ISP, not a record you add to your domain. The docs require the PTR to match your mailcow hostname's FQDN, for IPv4 and IPv6 both. A missing or mismatched PTR is the single most common reason a technically perfect mailcow gets rejected by Outlook and Gmail.

ClamAV and Flatcurve are why the floor is 6 GiB

The documented minimum of 6 GiB plus 1 GiB swap is mostly those two: ClamAV loads its full signature database into memory, and the Flatcurve full-text search engine indexes every message. Both can be disabled in mailcow.conf if you are running on a 4 GiB box, at the cost of virus scanning and fast IMAP search respectively. Disabling them is a supported choice; running the default stack under-provisioned is not.

Specifications#

Category
Chat, mail and notifications
Licence
GPL-3.0 for mailcow's own code; bundled components keep their own licenses (Copyleft)
Written in
PHP / shell, orchestrating Postfix, Dovecot and Rspamd
First release
2016
Maturity
Mature
Datastore
MariaDB 10.11 plus Redis
Default ports
25, 80, 110, 143, 443, 465, 587, 993, 995, 4190
Ships as
docker compose
arm64 builds
Yes
Identity
Native OIDC. Identity providers are configured in the mailcow UI itself: Keycloak, generic OIDC and LDAP, with automatic user creation on first login; mail clients still need app passwords unless you use the Mailpassword flow.
Replaces
Google Workspace Gmail, Microsoft 365 Exchange Online, Fastmail, Zoho Mail
Project site
mailcow.email
Source
github.com/mailcow/mailcow-dockerized
Documentation
docs.mailcow.email

Alternatives to mailcow: dockerized#

Everything else in chat, mail and notifications, closest in operational weight first.

ToolOps loadIdle RAMLicence
Synapse4, Heavy700 MBAGPL-3.0-only (or a commercial license from Element)
Mattermost3, Moderate500 MBAGPL-3.0 core, Mattermost Source Available License for `server/enterprise/`, Apache-2.0 for webapp and admin tools, MIT for official binaries
Stalwart3, Moderate150 MBAGPL-3.0 for the Community edition, dual licensed with the proprietary Stalwart Enterprise License for gated features
Gotify1, Set and forget20 MBMIT
ntfy1, Set and forget30 MBApache-2.0 (dual licensed with GPL-2.0)

Where mailcow: dockerized comes up elsewhere#

Migration guides

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.