mailcow is a Docker Compose stack around Postfix, Dovecot, Rspamd, and SOGo, with the best admin interface of any self-hosted mail server. This guide covers a working install and how much memory it really needs.
1. Before you start
Port 25 must be open, reverse DNS must be set, and your DNS must be ready. Our guide on what running your own mail server actually takes covers all of it, and none of this works without it.
You also need Docker installed, version 24.0.0 or newer, which mailcow checks for and refuses to run without.
mailcow's stated minimum is 2.5 GB of RAM, and its setup script prompts you to disable ClamAV on anything at or below that. In practice it is comfortable at 4 to 6 GB. On 2 GB it will install and then swap itself to a standstill.
2. Install
$ cd /opt $ sudo git clone https://github.com/mailcow/mailcow-dockerized $ cd mailcow-dockerized $ sudo ./generate_config.sh
The script asks for your mail hostname, for example mail.example.com, and your timezone. That hostname must match your reverse DNS record. On a machine with 2.5 GB of RAM or less it also offers to set SKIP_CLAMD=y for you.
It writes mailcow.conf, which is where every later setting lives. Then pull and start:
$ sudo docker compose pull $ sudo docker compose up -d
The first pull is large and the first start takes a few minutes. Watch it with sudo docker compose logs -f.
3. Log in and add your domain
Open https://mail.example.com. The default login is admin with the password moohoo.
Change that password immediately. It is published in every mailcow guide including this one.
Then add your domain under Configuration, Mail Setup, Domains, and add your first mailbox. mailcow generates the DKIM key when you add the domain.
4. Publish the DNS records
Under Configuration, Mail Setup, Domains, the DNS button shows every record mailcow expects with a green or red status next to each. That page is the best DNS checklist any of these platforms provide, so work down it until everything is green.
It will include the DKIM record, which only exists after you added the domain.
5. Cut memory use if you need to
If you are on 4 GB rather than 8, disable ClamAV in mailcow.conf:
SKIP_CLAMD=y
$ sudo docker compose down $ sudo docker compose up -d
That frees roughly a gigabyte. Rspamd keeps doing the spam filtering, which is what most servers need.
Do not put mailcow behind your existing Nginx casually
mailcow ships its own web server and expects ports 80 and 443. Putting a proxy in front is supported but it is a documented procedure with specific headers and settings, not a quick proxy_pass. If this VPS also serves websites, read mailcow's reverse proxy documentation before you start, or give mail its own server.
6. Spam filtering
mailcow uses Rspamd for spam scoring, DKIM signing, and antivirus handling. Every message gets a score, and the score decides what happens to it.
The defaults are conservative on purpose:
| Score | Action |
|---|---|
| Below 4 | Delivered |
| 4 and above | Greylisted (sender asked to retry) |
| 6 and above | Delivered to Junk, header added |
| 15 and above | Rejected at SMTP |
Greylisting temporarily refuses a message from an unknown sender. Real mail servers retry a few minutes later; most spam software does not.
To change the thresholds server-wide, edit the actions file and restart Rspamd:
actions {
reject = 15;
add_header = 6;
greylist = 4;
}$ cd /opt/mailcow-dockerized $ sudo docker compose restart rspamd-mailcow
Existing per-user settings are not overwritten by this change.
Per-domain and per-user lists
Domain administrators set a blacklist and whitelist under Configuration, Mail Setup, Domains, Edit Domain, Spam Filter. Individual users get their own Spam filter tab in the mailcow interface, where they can set their own thresholds and lists.
Be aware that a user's own settings override the domain settings, so a user can whitelist a sender you blocked.
The Rspamd interface
Rspamd ships its own web interface with mailcow, showing live scores, the history of recent messages, and which rules fired on each. The password is random until you set one: log in to mailcow as administrator, go to System Configuration, then Access Rspamd UI.
When a message is junked and you do not know why, that history view names the rules that fired on it.
Training the filter
Rspamd learns from what your users do. Moving a message into Junk trains it as spam, moving it out trains it as ham. This feedback improves accuracy more than threshold tuning does, so tell your users to move mail into and out of Junk instead of deleting it.
7. Back it up
mailcow includes a backup script. Point it at a directory outside the install:
$ sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup allThe script creates folders named mailcow_DATE inside that location. Do not rename them, because the restore process looks for that format. Copy the directory off the server, and test a restore once before you rely on it.
8. Updating
$ cd /opt/mailcow-dockerized $ sudo ./update.sh
The update script handles the compose changes between versions. Do not update by pulling images by hand; mailcow's versions are coordinated across containers.
Quick reference
| Task | Command |
|---|---|
| Install | git clone, ./generate_config.sh, docker compose up -d |
| First login | admin / moooo, change it now |
| DNS checklist | Configuration, Mail Setup, Domains, DNS button |
| Save memory | SKIP_CLAMD=y in mailcow.conf |
| Backup | MAILCOW_BACKUP_LOCATION=/path helper-scripts/backup_and_restore.sh backup all |
| Update | ./update.sh |
Where to go next
Work the DNS page until every row is green, then send a test message in each direction. If mail sends but lands in spam, look at DNS and reputation before you look at mailcow.