What mailcow needs from the machine
Memory is the figure that decides the machine. The mailcow documentation lists the floor and three sizing examples; the rest of the stack is light by comparison.
| Item | mailcow documentation |
|---|---|
| CPU | 1 GHz |
| RAM | Minimum 6 GiB + 1 GiB swap, default config |
| Disk | 20 GiB, without emails |
| Architecture | x86_64 or ARM64 |
| Most private installations | 6 GiB RAM + 1 GiB swap |
| About 5 to 10 users | 8 GiB RAM recommended |
| 15 phones on ActiveSync, ~50 concurrent IMAP connections | Plan 16 GiB RAM |
The documentation names the heavy parts. One SOGo worker can grow to about 350 MiB before it is recycled, and the default configuration starts 20 workers, so ActiveSync phones are what push memory up. ClamAV and the Flatcurve full-text search engine are also hungry; SKIP_CLAMD=y and SKIP_FTS=y in mailcow.conf turn them off if memory is short.
mailcow lists as fully compatible: Debian 11 to 13, Ubuntu 22.04 or newer, AlmaLinux 8 and 9, Rocky Linux 9. OpenVZ, Virtuozzo and LXC are not supported. The 20 GiB floor holds no mail, so size the disk from your mailboxes.
Which of our machines fit
Each line in the dedicated server range is one physical box rented whole, root included, on an unmetered port. Our reading of the classes against the figures above:
- Moonshot cartridges (8 to 64 GB on the Atom and Xeon E3 lines, 16 to 128 GB on the Xeon D-1587 line): a private or small-office mail host, one service per box. No cartridge has a hardware RAID controller: most carry a single disk, and the m510, m710x and m750 run software RAID across NVMe drives. Only the m510 and m710x have a console; the others have power control only.
- Single- and dual-socket rack servers: a company mail host with ActiveSync phones. These carry a hardware RAID controller for a mirrored mail store, and an out-of-band console (HPE iLO, Dell iDRAC or a Supermicro BMC). See enterprise dedicated servers for the two-socket class.
- Not the X-Gene ARM cartridge: mailcow supports ARM64, but that cartridge boots Ubuntu 16.04 and 18.04 only, both older than Ubuntu 22.04, where mailcow's compatibility list starts.
A racked machine is handed over in about 30 minutes once payment is approved; a build to order takes 4 to 24 hours. If mailcow is one service among several on the box, file sync for example, the self-hosted Nextcloud page covers sizing for that half.
Port 25, PTR and the address
Postfix needs TCP 25 both ways: inbound to receive, outbound to deliver to other servers. The mailcow documentation also requires the PTR record of your IP address to match the server's FQDN (MAILCOW_HOSTNAME), for IPv4 and for IPv6 if you use it, and says the record is usually set at the provider that leased you the address.
Every server here includes one IPv4 address. IPv6 is in no default build: it is added at checkout at no charge, as individual addresses rather than a routed prefix. Before you order, ask in chat whether outbound 25 is open on the line you have picked and how the PTR for your address is set. This page does not promise either for every line.
No host can promise inbox placement. Large mailbox providers judge an address by its sending history, and a new address has none. Correct PTR, SPF, DKIM and DMARC are the entry ticket, not a guarantee.
DNS records for each mail domain
The A record is set once, for the host name that serves the web UI. Every domain you add to mailcow needs the rest. With mail.example.org as the server and example.org as the domain, the documented minimum plus the authentication records are:
| Name | Type | Value |
|---|---|---|
| A | the server's IPv4 (AAAA for IPv6) | |
| @ | MX 10 | mail.example.org. |
| autodiscover | CNAME | mail.example.org. |
| autoconfig | CNAME | mail.example.org. |
| @ | TXT | v=spf1 mx a -all |
| dkim._domainkey | TXT | the key generated in the mailcow UI |
| _dmarc | TXT | v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.org |
Optional SRV records for IMAP, POP3, submission, Sieve, CalDAV and CardDAV are in the documentation, with checkers such as MX Toolbox and Mail-tester.
Installing mailcow with the admin UI fenced
mailcow starts with the admin login admin and the password moohoo, and nginx publishes ports 80 and 443 on every address. Bind the web ports to loopback for the first start, replace the password, then open them. The documentation notes that an IPv4 bind skips all IPv6 bindings, so this fence covers both families.
- As root, reinstall the OS from the control panel, choosing Ubuntu 22.04 or later, or Debian 12 or 13; mailcow lists both as fully compatible. Set the A record and the PTR first.
timedatectl statusmust report the system clock as synchronized; if it does not,timedatectl set-ntp true. - Confirm nothing else holds a mail port:
ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190'must print nothing. - Install the packages and Docker, then clone and configure mailcow (block below).
generate_config.shasks for the server's FQDN, which is not the mail domain, the time zone and the branch; take the stable master branch. It writesmailcow.confwith mode 600, because the file holds the database and Redis passwords. - Decide IPv6 now, before anything starts. If you added IPv6 at checkout, configure it on the host and give it its own PTR and AAAA records. If you did not, set
ENABLE_IPV6=falseinmailcow.conf. - Fence, start and reset the admin password (second block). Before the reset, run
docker compose psand wait for the mysql-mailcow container to show as running; if the script still says it cannot find MySQL, wait and run it again. It asks for confirmation (answer y) and removes any 2FA on the admin account. The new random password is shown once: put it in your password manager. - Log in at
https://mail.example.org/admin, turn on two-factor authentication, then add the domain and the DKIM key.
apt update
apt install -y git openssl curl gawk coreutils grep jq
curl -sSL https://get.docker.com/ | CHANNEL=stable sh
systemctl enable --now docker
umask 0022
cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
./generate_config.sh
# in /opt/mailcow-dockerized/mailcow.conf, before the first start:
# HTTP_BIND=127.0.0.1
# HTTPS_BIND=127.0.0.1
docker compose pull
docker compose up -d
./helper-scripts/mailcow-reset-admin.sh
# password stored? empty both BIND values again, then:
docker compose down
docker compose up -d
The Let's Encrypt container cannot pass its HTTP check while port 80 is on loopback. Once the ports are public, read docker compose logs acme-mailcow to confirm the certificate was issued.
Firewall and IPv6
The mailcow documentation is blunt here: running it on a host with ufw or firewalld enabled causes several problems, and because the services are published by Docker, rules in the INPUT chain do not restrict access to them. Its advice is to disable those tools and keep your ruleset in the DOCKER-USER chain, which a Docker restart does not clear, persisted with iptables-persistent.
- The mail and web ports stay open to the world.
- MariaDB (13306), Redis (7654) and doveadm (19991) are published on 127.0.0.1 only by default. Leave them that way.
- SSH is a host service, so INPUT rules still apply to it: keys only, no password logins.
- An iptables ruleset covers IPv4 alone. With IPv6 on, write the matching rules with ip6tables, or the v6 side stays open.
Since the 2025-09 release, ENABLE_IPV6 in mailcow.conf defaults to true when the host has IPv6, and the generated config warns that a faulty host IPv6 setup can turn the server into an open relay. Hence the IPv6 step in the install, ahead of the first start. To change it later, edit the value and recreate the stack with docker compose down then docker compose up -d.
Backups and updates
mailcow's own helper-scripts/backup_and_restore.sh backs up vmail, crypt, redis, rspamd, postfix and mysql, or all of them, into dated mailcow_DATE folders. Setting the location lets it run from cron without stopping to ask for one, and --delete-days prunes old sets. The documentation's own crontab example backs up mysql crypt redis to /mnt/mailcow_backups with --delete-days 3. The file below is our /etc/cron.d version of it. Because it is a cron.d file it needs the root user field, and it backs up all rather than the docs' mysql crypt redis:
mkdir -p /opt/backup
# /etc/cron.d/mailcow-backup
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
5 4 * * * root cd /opt/mailcow-dockerized/; MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all --delete-days 30
A backup on the same disk dies with the disk: copy /opt/backup to another machine after each run. Updates go through ./update.sh in the mailcow directory; ./update.sh --check shows what would change first.
When this is the wrong place for your mail
- You need a contractual inbox-placement figure. Nobody running their own MTA has one.
- Nobody on your side will read the mail queue, patch the stack or answer a blocklist notice. Engineer time inside your own software is bought as a management tier; it is not part of the included support.
- Your mail outgrows one machine's disks with no plan to split it. Size storage before you start: moving a mail store later costs a cutover.
Questions
How much memory does a mailcow install really need?
The documented minimum is 6 GiB of RAM plus 1 GiB of swap with the default configuration, which the docs call fine for most private installations. They recommend 8 GiB for about 5 to 10 users and 16 GiB for a company with 15 ActiveSync phones and around 50 concurrent IMAP connections. Disabling ClamAV and full-text search lowers the load.
Which ports does mailcow open, and which must be public?
Postfix publishes 25, 465 and 587; Dovecot publishes 143, 993, 110, 995 and 4190; nginx publishes 80 and 443. All of them face the internet on a normal install. MariaDB, Redis and doveadm are bound to 127.0.0.1 by default.
What is the mailcow admin login after install, and when do I change it?
A fresh install answers at /admin with the user admin and the password moohoo. Change it before the web ports are reachable from outside: bind HTTP and HTTPS to 127.0.0.1 for the first start, run the admin reset script, then open the ports. Turn on two-factor authentication at the first login.
Can I run mailcow without IPv6?
Yes. Set ENABLE_IPV6=false in mailcow.conf and recreate the stack with docker compose down and up -d. On our servers IPv6 is only present if you added it at checkout.
Will mail from a new mailcow server reach the inbox?
Not guaranteed. Correct PTR, SPF, DKIM and DMARC records are required, but large providers also weigh an address's sending history, which a new address does not have. Test with the checkers the mailcow documentation lists before moving real users.
Does mailcow run on ARM servers?
mailcow lists ARM64 as supported. Our X-Gene ARM cartridge is not a fit, though, because it boots only Ubuntu 16.04 and 18.04, older than Ubuntu 22.04, the first Ubuntu release mailcow lists as supported.