A complete guide to running Mailcow on your own server — from bare VPS to fully operational mail server.
Mailcow (stylized mailcow: dockerized) is an open-source mail server suite that bundles every component you need into a set of Docker containers managed by Docker Compose. Instead of painstakingly installing and wiring together Postfix, Dovecot, Rspamd, ClamAV, SOGo, and a dozen other pieces by hand, Mailcow gives you a single docker compose up command that starts the entire stack.
Under the hood you get:
Everything is containerized, which makes upgrades and rollbacks far easier than a traditional bare-metal setup. That said, running a production mail server is a serious operational commitment. This guide walks you through the full setup correctly.
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB SSD | 50+ GB SSD |
| OS | Ubuntu 22.04 LTS | Ubuntu 22.04 / Debian 12 |
| IPv4 | 1 dedicated IP | 1 dedicated IP |
mail.yourdomain.comdocker.io apt package; install from Docker's official repodocker compose plugin, not the standalone docker-compose v1)DNS is the most consequential part of email setup. Misconfigured DNS causes mail to be rejected or silently dropped — often without any bounce or error message to the sender. Configure all of the following records before you install Mailcow, and allow up to 24–48 hours for full propagation.
Point your mail hostname to the server's IP:
| Name | Type | Value |
|---|---|---|
mail.yourdomain.com | A | your.server.ip.address |
mail.yourdomain.com | AAAA | your::ipv6 (if available) |
The MX record tells other mail servers where to deliver mail for your domain:
| Name | Type | Priority | Value |
|---|---|---|---|
yourdomain.com | MX | 10 | mail.yourdomain.com |
SPF authorizes your server to send mail on behalf of your domain. Add a TXT record on the root of your domain:
| Name | Type | Value |
|---|---|---|
yourdomain.com | TXT | v=spf1 mx ~all |
The mx mechanism automatically includes whatever is in your MX record. ~all is a soft fail (recommended while testing); change to -all (hard fail) once everything is working.
DMARC tells receivers what to do with mail that fails SPF/DKIM. Start in monitor mode:
| Name | Type | Value |
|---|---|---|
_dmarc.yourdomain.com | TXT | v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com |
After a few weeks of reviewing aggregate reports, tighten to p=quarantine then p=reject.
These records let mail clients (Outlook, Thunderbird) configure themselves automatically:
| Name | Type | Value |
|---|---|---|
autoconfig.yourdomain.com | CNAME | mail.yourdomain.com |
autodiscover.yourdomain.com | CNAME | mail.yourdomain.com |
Verify all records propagate before proceeding:
dig MX yourdomain.com
dig A mail.yourdomain.com
dig TXT yourdomain.com
apt update && apt upgrade -y
apt install -y curl git openssl python3
The server's hostname must match your mail FQDN. This is critical — Postfix uses it in HELO/EHLO handshakes:
hostnamectl set-hostname mail.yourdomain.com
Edit /etc/hosts and ensure the line reads:
127.0.1.1 mail.yourdomain.com mail
Verify:
hostname -f
# should output: mail.yourdomain.com
Do not use the distro-packaged docker.io. Install from Docker's official repository:
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
Verify:
docker --version
docker compose version
Ubuntu ships with postfix or exim4 sometimes pre-installed. They will conflict with Mailcow's containerized Postfix on port 25:
systemctl stop postfix exim4 2>/dev/null
systemctl disable postfix exim4 2>/dev/null
ss -tlnp | grep -E ':25|:80|:443|:465|:587|:993|:995'
Nothing should be listening on those ports before you install Mailcow. If something is, identify and stop it.
ClamAV alone can use over 1 GB of RAM. Adding swap prevents OOM kills:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
./generate_config.sh
The script will ask for your mail hostname (e.g. mail.yourdomain.com). It creates mailcow.conf with randomized passwords and a unique installation ID.
mailcow.confnano mailcow.conf
Key settings to review:
MAILCOW_HOSTNAME — must match your FQDN exactlyHTTP_PORT / HTTPS_PORT — default 80/443; change only if you have a reverse proxySKIP_LETS_ENCRYPT — leave as n for automatic TLSSKIP_CLAMD — set to y if you have less than 3 GB RAM to disable ClamAVTZ — set your timezone, e.g. America/New_Yorkdocker compose pull
docker compose up -d
First startup takes several minutes as images download and containers initialize. ClamAV in particular takes 2–5 minutes to load its virus database before it is ready.
Watch progress:
docker compose logs -f
Press Ctrl+C once things settle. Check all containers are running:
docker compose ps
All containers should show Up or healthy. If any are in Restarting, check their logs:
docker compose logs <container-name>
Mailcow uses acme.sh to automatically obtain Let's Encrypt certificates. It does this via HTTP-01 challenge on port 80. For this to work:
mail.yourdomain.com must already point to this serverCheck the acme container logs to confirm certificate issuance:
docker compose logs acme-mailcow
You should see a line like:
[...] Certificate issued successfully for mail.yourdomain.com
If it fails (e.g. rate-limited or DNS not yet propagated), the container will retry every hour. You can force a retry:
docker compose restart acme-mailcow
Certificates are stored at data/assets/ssl/. Dovecot and Postfix reload them automatically via a watcher container.
Use ufw to open only what is needed:
ufw allow 22/tcp # SSH — do this first or you'll lock yourself out
ufw allow 25/tcp # SMTP (inbound from other mail servers)
ufw allow 80/tcp # HTTP (Let's Encrypt + redirect)
ufw allow 443/tcp # HTTPS (admin UI + autodiscover)
ufw allow 465/tcp # SMTPS (mail client submission, legacy)
ufw allow 587/tcp # Submission (mail client submission, modern)
ufw allow 993/tcp # IMAPS
ufw allow 995/tcp # POP3S (optional)
ufw enable
Verify:
ufw status verbose
Navigate to https://mail.yourdomain.com. The default credentials are:
adminmoohooyourdomain.comalice) and set a passwordThe full email address will be alice@yourdomain.com.
| Protocol | Server | Port | Security |
|---|---|---|---|
| IMAP | mail.yourdomain.com | 993 | SSL/TLS |
| SMTP | mail.yourdomain.com | 587 | STARTTLS |
| SMTP (alt) | mail.yourdomain.com | 465 | SSL/TLS |
Username is the full email address (alice@yourdomain.com).
You set up SPF and DMARC in the DNS section. DKIM is generated by Mailcow and must be added to DNS manually.
The key is typically named dkim._domainkey.yourdomain.com (the selector prefix may vary):
| Name | Type | Value |
|---|---|---|
dkim._domainkey.yourdomain.com | TXT | (paste the full value from the admin UI) |
DKIM public keys routinely exceed 255 characters — the maximum length for a single DNS TXT string. Most DNS providers silently truncate or reject oversized values without any error. The fix is to split the key into multiple quoted strings within the same TXT record, but the exact syntax varies by provider. In BIND zone file format it looks like "part1" "part2"; in web UIs you may need to paste each chunk on its own line. Always verify with dig TXT dkim._domainkey.yourdomain.com and confirm the full key is returned before assuming it worked.
Send a test email to mail-tester.com. A score of 10/10 means SPF, DKIM, and DMARC are all passing. Aim for at least 9/10 before going live.
You can also verify individual records:
# SPF
dig TXT yourdomain.com | grep spf
# DKIM
dig TXT dkim._domainkey.yourdomain.com
# DMARC
dig TXT _dmarc.yourdomain.com
A PTR record maps your IP address back to your hostname. Many receiving mail servers — including Gmail and Outlook — reject or silently discard mail from IPs without a matching PTR record.
PTR records are controlled by whoever owns the IP address — your VPS provider, not your DNS registrar. Log into your VPS provider's control panel, find the networking or IP settings for the server, and look for "Reverse DNS" or "PTR record". Set it to mail.yourdomain.com. Every major provider supports this, but the UI location varies — search your provider's documentation if you can't find it.
mail.yourdomain.comVerify (after propagation):
dig -x your.server.ip.address
# should return: mail.yourdomain.com
Mailcow uses Rspamd for spam filtering. It works well out of the box, but understanding the basics helps you tune it.
Access the Rspamd web UI at https://mail.yourdomain.com/rspamd. The password is in mailcow.conf under RSPAMD_PASSWORD.
Rspamd improves with training. Move messages to/from your Junk folder to train the Bayesian filter — Mailcow automatically feeds these moves to Rspamd via the Dovecot sieve script.
Rspamd includes greylisting: it temporarily rejects mail from unknown senders, forcing a retry. Legitimate mail servers always retry; spam bots usually do not. This is enabled by default and significantly reduces spam volume.
Every delivered message has an X-Spamd-Result header showing its Rspamd score and which checks fired. Inspect these headers in your mail client to understand why a message was marked as spam or passed through.
Self-hosting means you are responsible for your backups. Mailcow has no built-in automated backup system. If your disk fails, if you accidentally run docker compose down -v, or if your VPS provider loses your data, recovery depends entirely on what you've backed up and how recently. Build your backup process before you start using the server for real mail.
/opt/mailcow-dockerized/mailcow.conf — contains all secrets and configuration/opt/mailcow-dockerized/data/assets/ssl/ — TLS certificatesmailcowdockerized_vmail-vol-1 (mailboxes), mailcowdockerized_mysql-vol-1 (database)# Stop containers first for a consistent backup
docker compose down
# Back up the mail volume
docker run --rm \
-v mailcowdockerized_vmail-vol-1:/data \
-v /backup:/backup \
alpine tar czf /backup/vmail-$(date +%F).tar.gz /data
# Back up the MySQL database
docker compose up -d mysql-mailcow
docker compose exec mysql-mailcow mysqldump \
--all-databases \
-u root \
-p"$(grep DBROOT mailcow.conf | cut -d= -f2)" \
> /backup/mysql-$(date +%F).sql
docker compose up -d
Storing backups on the same server is not a backup — it protects only against accidental deletion, not hardware failure or provider outage. Use rclone to sync backups to an S3-compatible object store, Backblaze B2, or another remote location:
rclone copy /backup remote:mailcow-backups --progress
# View Postfix queue
docker compose exec postfix-mailcow mailq
# Flush queue (retry all deferred messages)
docker compose exec postfix-mailcow postqueue -f
# All container logs
docker compose logs -f --tail=100
# Postfix SMTP logs only
docker compose logs postfix-mailcow -f
# Dovecot IMAP logs
docker compose logs dovecot-mailcow -f
Run the built-in update script. Read the release notes on GitHub first — occasionally there are breaking changes:
cd /opt/mailcow-dockerized
./update.sh
The script pulls new images, recreates containers, and applies any necessary database migrations. It takes a few minutes and causes brief downtime.
Getting blacklisted after a clean start is more common than it sounds — a forwarded spam message, a compromised account, or simply sharing a subnet with a bad actor can land your IP on a list. Each blocklist has its own delisting process, some automated, some manual with no guaranteed turnaround. Run this check monthly:
Mail accumulates. Check disk usage regularly:
df -h /
docker system df
telnet smtp.gmail.com 25 — if it hangs, your provider is blocking itdocker compose exec postfix-mailcow mailqdig MX yourdomain.comdocker compose logs acme-mailcowdocker compose ps # identify the container
docker compose logs <container> --tail=50 # read the error
Common causes: port conflict with a host service, insufficient RAM (especially ClamAV), misconfigured mailcow.conf.
# Reset admin password from the host
docker compose exec mysql-mailcow mysql -u root \
-p"$(grep DBROOT mailcow.conf | cut -d= -f2)" mailcow \
-e "UPDATE administrator SET password = '{SSHA256}$(echo -n 'newpassword' | openssl dgst -sha256 -binary | base64)' WHERE username = 'admin';"
Mailcow supports hosting mail for as many domains as you like. Each domain needs its own MX, SPF, DKIM, and DMARC records — repeat the DNS setup from section 3 for each one. In the admin UI, go to Configuration → Mail Setup → Domains and add each domain. After adding a new domain, generate a DKIM key for it under Configuration → ARC/DKIM Keys — keys are per-domain, not shared.
A domain alias makes every mailbox on one domain receive mail sent to the same local part on another domain. For example, if you alias oldcompany.com to newcompany.com, mail to alice@oldcompany.com is automatically delivered to alice@newcompany.com. Add domain aliases under Configuration → Mail Setup → Domain aliases. You still need correct DNS (MX record) for the aliased domain.
Individual address aliases redirect one address to another. Go to Configuration → Mail Setup → Aliases. Common uses: info@, support@, noreply@ all routed to a real mailbox. You can also set a catch-all by using @yourdomain.com as the alias address — any address at that domain not matching a real mailbox will land in the catch-all destination. Be cautious with catch-alls; they attract significant spam.
Forwarding copies or redirects incoming mail to an external address. Configure it per-mailbox under Configuration → Mail Setup → Mailboxes → Edit. Be aware that forwarding to Gmail or Outlook is a deliverability minefield: if you forward a spam message that passes your server's checks, the receiving server may blacklist your IP for relaying spam. Use Sieve filters (next section) for finer control.
Sieve is a scripting language for mail filtering that runs server-side inside Dovecot, before mail reaches your inbox. It can sort, reject, forward, and auto-reply to messages based on any header or content. Mailcow exposes Sieve through two interfaces: a simple rules UI in the user panel, and a raw script editor for advanced use.
Each mailbox has its own login at https://mail.yourdomain.com. Log in with the full email address and mailbox password. Under Filters, you can create basic rules without writing Sieve directly.
For anything beyond basic sorting, write the script yourself. In the user panel, go to Filters → Sieve script. Example — move newsletter mail to a folder:
require ["fileinto", "imap4flags"];
if header :contains "List-Unsubscribe" "" {
fileinto "Newsletters";
stop;
}
Auto-reply (vacation responder):
require ["vacation"];
vacation
:days 7
:subject "Out of office"
:addresses ["alice@yourdomain.com"]
"I'm away until 15 March. I'll reply when I'm back.";
Reject mail from a specific address:
require ["reject"];
if address :is "from" "spammer@example.com" {
reject "Not accepted.";
stop;
}
Admins can apply a Sieve script to all mailboxes on the server. This is useful for global spam handling or compliance archiving. Edit data/conf/dovecot/global_sieve_before (runs before user filters) or data/conf/dovecot/global_sieve_after (runs after). Restart Dovecot after changes:
docker compose restart dovecot-mailcow
Mailcow ships with Fail2ban pre-configured as a container (netfilter-mailcow). It monitors logs from Postfix, Dovecot, SOGo, and the admin UI, and automatically bans IPs that exceed login failure thresholds using nftables rules on the host.
docker compose exec netfilter-mailcow fail2ban-client status
docker compose exec netfilter-mailcow fail2ban-client status postfix-sasl
If you lock yourself out:
docker compose exec netfilter-mailcow fail2ban-client set postfix-sasl unbanip YOUR.IP.ADDRESS
Add trusted IPs to the whitelist in mailcow.conf:
SNAT_TO_SOURCE=
IPV4_NETWORK=172.22.1
# Add to SKIP_LETS_ENCRYPT section:
SPAMHAUS_DQS_KEY=
More precisely, edit data/conf/fail2ban/custom.conf and add:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 YOUR.TRUSTED.IP.HERE
Then restart the netfilter container:
docker compose restart netfilter-mailcow
Default ban threshold is 10 failures in 10 minutes, ban duration 1 hour. To adjust, create data/conf/fail2ban/custom.conf:
[DEFAULT]
maxretry = 5
findtime = 600
bantime = 86400
Passing SPF, DKIM, and DMARC is necessary but not sufficient for reliable delivery to Gmail and Outlook. Both providers maintain their own reputation systems that take weeks or months of consistent sending to warm up. Setting up their postmaster tools gives you visibility into how they see your server.
The dashboard shows your domain reputation (Bad / Low / Medium / High), IP reputation, spam rate, delivery errors, and DKIM/DMARC compliance. A "Low" or "Bad" reputation means Gmail is silently sending your mail to spam or rejecting it. There is no quick fix — only sustained good sending behaviour over time improves it.
For deliverability to Outlook, Hotmail, and Live accounts:
If your IP is already blocked by Microsoft (you'll see 550 5.7.606 or SC-001 in bounce messages), submit a delisting request at sender.office.com.
Large providers are suspicious of IPs that appear from nowhere and immediately send high volumes. If you're sending bulk mail or newsletters, start with small daily volumes and ramp up gradually over several weeks. For transactional mail (receipts, notifications), the volume is typically low enough that warm-up isn't an issue, but reputation still builds slowly.
MTA-STS (Mail Transfer Agent Strict Transport Security) is a standard that lets you publish a policy telling other mail servers that they must use TLS when delivering to you, and that they should validate your certificate. Without it, a man-in-the-middle can downgrade an inbound connection to plaintext. With a modern TLS certificate (which Mailcow provides via Let's Encrypt), enabling MTA-STS is straightforward.
You need to serve a policy file over HTTPS at exactly https://mta-sts.yourdomain.com/.well-known/mta-sts.txt, and add a DNS TXT record to activate it.
The policy file content:
version: STSv1
mode: enforce
mx: mail.yourdomain.com
max_age: 604800
Host this file on any web server (a Cloudflare Worker, a GitHub Pages site, or a separate nginx on your mail server all work). Then add two DNS records:
| Name | Type | Value |
|---|---|---|
mta-sts.yourdomain.com | CNAME or A | (wherever you're hosting the policy file) |
_mta-sts.yourdomain.com | TXT | v=STSv1; id=20240101000000 |
The id value is a change indicator — update it (any string works, a timestamp is conventional) whenever you modify the policy file so sending servers know to re-fetch it.
Also add a TLS Reporting record so you receive reports when other servers fail to establish TLS with you:
| Name | Type | Value |
|---|---|---|
_smtp._tls.yourdomain.com | TXT | v=TLSRPTv1; rua=mailto:tlsrpt@yourdomain.com |
DANE (DNS-based Authentication of Named Entities) publishes your TLS certificate fingerprint in DNS as a TLSA record, secured by DNSSEC. This lets sending servers verify your certificate without relying on the certificate authority system. It requires DNSSEC to be enabled on your domain — without DNSSEC, DANE records are not trustworthy and will be ignored. If your domain registrar supports DNSSEC, generate the TLSA record from your certificate:
# Get the fingerprint from your current certificate
openssl x509 -in /opt/mailcow-dockerized/data/assets/ssl/cert.pem \
-pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| xxd -p -c 256
The resulting hash is the value for a TLSA record at _25._tcp.mail.yourdomain.com with parameters 3 1 1. DANE adoption among sending servers is limited but growing — worth setting up if you already have DNSSEC.
By default, IMAP search in Dovecot can only search message headers and metadata. Searching the body of messages (as mail clients do when you use their search box) requires Dovecot to open and scan every message individually — which is slow on large mailboxes.
Mailcow includes an optional Solr container for full-text indexing. It is disabled by default because it consumes significant RAM (512 MB minimum, more with large mailboxes).
In mailcow.conf, set:
SKIP_SOLR=n
Then recreate the affected containers:
docker compose up -d
Solr starts indexing existing mail in the background. Depending on mailbox size, initial indexing can take hours. Monitor progress:
docker compose logs solr-mailcow -f
If a mailbox becomes out of sync:
docker compose exec dovecot-mailcow doveadm fts rescan -u alice@yourdomain.com
docker compose exec dovecot-mailcow doveadm index -u alice@yourdomain.com -q INBOX
If you're moving from another mail provider, imapsync is the standard tool for copying mail between IMAP servers. It preserves folder structure, flags (read/unread, starred), and message dates.
apt install -y imapsync
imapsync \
--host1 imap.oldprovider.com --ssl1 \
--user1 alice@olddomain.com --password1 'oldpassword' \
--host2 mail.yourdomain.com --ssl2 \
--user2 alice@yourdomain.com --password2 'newpassword' \
--automap \
--skipcrossduplicates \
--useuid
--automap handles common folder name differences (e.g. "Sent Items" vs "Sent"). --skipcrossduplicates avoids copying messages that already exist in the destination. --useuid uses IMAP UIDs for deduplication, which is more reliable than message-ID matching.
For large mailboxes, run imapsync in a screen or tmux session so a disconnected SSH session doesn't interrupt it:
screen -S migration
imapsync [options]
# Ctrl+A then D to detach; screen -r migration to reattach
Run imapsync multiple times before your DNS cutover. The first run copies everything (slow). Subsequent runs only sync new and changed messages (fast). On cutover day: update your MX record, wait for propagation, then do one final sync to catch mail that arrived at the old server during the transition.
Mail is only part of it. If your old provider supports CardDAV/CalDAV export, export your contacts as a .vcf file and your calendar as an .ics file, then import them into SOGo via the webmail interface at https://mail.yourdomain.com.
Having backups is only half of it. Know your restore procedure before you need it.
# Stop Mailcow
cd /opt/mailcow-dockerized
docker compose down
# Remove the existing volume (destructive — only do this if restoring fully)
docker volume rm mailcowdockerized_vmail-vol-1
# Recreate the volume and restore
docker volume create mailcowdockerized_vmail-vol-1
docker run --rm \
-v mailcowdockerized_vmail-vol-1:/data \
-v /backup:/backup \
alpine tar xzf /backup/vmail-2024-01-01.tar.gz -C /
# Start only MySQL
docker compose up -d mysql-mailcow
# Restore the dump
docker compose exec -T mysql-mailcow mysql \
-u root \
-p"$(grep DBROOT mailcow.conf | cut -d= -f2)" \
< /backup/mysql-2024-01-01.sql
# Start the rest
docker compose up -d
If the original server is gone:
git clone https://github.com/mailcow/mailcow-dockerized /opt/mailcow-dockerizedmailcow.conf from your backup to /opt/mailcow-dockerized/mailcow.confdocker compose up -dThe Let's Encrypt certificate will be re-issued automatically once DNS propagates. Expect 15–60 minutes before HTTPS is fully working on the new server.
ClamAV is the largest consumer of RAM in a default Mailcow installation. Its virus database loads entirely into memory and occupies roughly 700 MB–1 GB. If you're running on a server with less than 6 GB RAM and don't need virus scanning (e.g. the server is for personal use and you trust your senders), disable it in mailcow.conf:
SKIP_CLAMD=y
Then recreate the containers: docker compose up -d. Rspamd will still filter spam; you simply lose the virus scanning layer.
For servers with more RAM, increase the InnoDB buffer pool to allow more of the database to be cached in memory. Create data/conf/mysql/custom.cnf:
[mysqld]
innodb_buffer_pool_size = 256M
innodb_buffer_pool_instances = 1
innodb_log_file_size = 64M
max_connections = 50
Restart MySQL: docker compose restart mysql-mailcow. For most personal and small-team setups the defaults are fine; this matters at higher mailbox counts.
Rspamd uses a normal worker for scanning and a controller for the web UI. On a high-volume server, you can increase the number of scanner workers. Create data/conf/rspamd/local.d/worker-normal.inc:
count = 4;
Restart Rspamd: docker compose restart rspamd-mailcow. Each worker uses roughly 100–200 MB RAM.
The default per-IP connection limit in Dovecot is 10. Mail clients that open many IMAP connections (some versions of Outlook, or mobile clients syncing multiple folders simultaneously) can hit this. Raise it in data/conf/dovecot/extra.conf:
mail_max_userip_connections = 30
Restart Dovecot: docker compose restart dovecot-mailcow.
Docker container logs are written to /var/lib/docker/containers/ and are not rotated by default. On a busy server they can fill your disk over weeks or months. Configure log rotation in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
Restart Docker: systemctl restart docker. Then restart Mailcow: docker compose up -d. Note that this applies to new containers — existing containers need to be recreated to pick up the new log settings.