How to Self-Host Maddy Mail Server with Docker
What Is Maddy?
Maddy is a self-hosted, all-in-one email server that replaces Postfix + Dovecot + OpenDKIM + rspamd with a single binary. It handles SMTP (sending and receiving), IMAP (mailbox access), DKIM signing, SPF/DMARC verification, and MTA-STS — all in one process. Written in Go, it uses minimal resources compared to traditional multi-component email stacks. Maddy replaces Gmail, Outlook, and Proton Mail.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 256 MB of free RAM
- 1 GB of free disk space (plus storage for mailboxes)
- A domain name with full DNS control
- Port 25 open (check with your VPS provider — some block it)
- A static IP address with PTR record configured
Docker Compose Configuration
Create a docker-compose.yml file:
services:
maddy:
image: foxcpp/maddy:0.8.2
container_name: maddy
restart: unless-stopped
hostname: mail.example.com
ports:
- "25:25" # SMTP (incoming mail)
- "465:465" # SMTPS (submission over TLS)
- "587:587" # SMTP submission (STARTTLS)
- "993:993" # IMAPS (mailbox access)
environment:
MADDY_HOSTNAME: mail.example.com
MADDY_DOMAIN: example.com
volumes:
- maddy_data:/data # Mailboxes, DKIM keys, databases
- maddy_config:/etc/maddy # Configuration files
- /etc/letsencrypt:/etc/letsencrypt:ro # TLS certificates
healthcheck:
test: ["CMD", "maddy", "version"]
interval: 60s
timeout: 5s
retries: 3
volumes:
maddy_data:
maddy_config:
Important: Replace mail.example.com with your actual mail hostname and example.com with your domain.
Start the stack:
docker compose up -d
Initial Setup
1. Configure maddy.conf
The default configuration works for most setups, but you need to set your hostname and domain. Edit the config inside the container:
docker compose exec maddy sh -c 'cat /etc/maddy/maddy.conf' | head -10
The key settings in maddy.conf:
$(hostname) = mail.example.com
$(primary_domain) = example.com
$(local_domains) = $(primary_domain)
If you need to modify the config, mount it as a bind mount instead of a named volume:
volumes:
- ./maddy.conf:/etc/maddy/maddy.conf:ro
- maddy_data:/data
- /etc/letsencrypt:/etc/letsencrypt:ro
2. TLS Certificates
Maddy requires valid TLS certificates. The recommended approach is using Let’s Encrypt with certbot on the host:
sudo apt install certbot
sudo certbot certonly --standalone -d mail.example.com
The certificates are mounted into the container via the /etc/letsencrypt volume. Update maddy.conf to point to them:
tls file /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/letsencrypt/live/mail.example.com/privkey.pem
3. Create User Accounts
Maddy manages users via CLI commands:
# Create authentication credentials
docker compose exec maddy maddy creds create [email protected]
# Create IMAP mailbox storage
docker compose exec maddy maddy imap-acct create [email protected]
You’ll be prompted for a password. The user can then connect with any IMAP client.
4. DNS Records
Set up all required DNS records. Maddy auto-generates DKIM keys on first start — retrieve them:
docker compose exec maddy maddy dkim ls
Add these records to your domain’s DNS:
| Record | Type | Name | Value |
|---|---|---|---|
| MX | MX | example.com | 10 mail.example.com |
| A | A | mail.example.com | Your server IP |
| SPF | TXT | example.com | v=spf1 ip4:YOUR_IP -all |
| DKIM | TXT | default._domainkey.example.com | From maddy dkim ls output |
| DMARC | TXT | _dmarc.example.com | v=DMARC1; p=quarantine; rua=mailto:[email protected] |
| PTR | PTR | Your IP | mail.example.com (set at VPS provider) |
See the Email Deliverability guide for detailed explanations of each record.
Configuration
Multiple Domains
Add additional domains to $(local_domains):
$(local_domains) = $(primary_domain) seconddomain.com thirddomain.net
Each domain needs its own MX, SPF, DKIM, and DMARC records.
Storage Backend
Maddy uses an embedded database (SQLite) by default for mailbox storage. For larger deployments, it supports PostgreSQL:
storage.imapsql local_mailboxes {
driver postgres
dsn "host=db user=maddy password=secret dbname=maddy"
}
Spam Filtering
Maddy includes basic spam filtering via DNS blacklists (DNSBLs) and SPF/DKIM/DMARC checks. Configure additional DNSBLs in maddy.conf:
check {
dnsbl {
reject_threshold 3
zen.spamhaus.org
bl.spamcop.net
}
spf
dkim
dmarc
}
Rate Limiting
Protect against brute-force and abuse:
limits {
all rate 20 1s # 20 connections per second globally
ip rate 5 1s # 5 connections per second per IP
}
Reverse Proxy
Maddy handles TLS itself — you typically don’t put a reverse proxy in front of mail ports. However, if you want to access the webmail (not included — Maddy is server-only) via a reverse proxy, use a separate webmail client like Roundcube or Snappymail.
For mail ports, expose them directly. See Reverse Proxy Setup for web UI proxying.
Backup
Back up the data volume which contains mailboxes, DKIM keys, and authentication databases:
docker run --rm -v maddy_data:/data -v $(pwd):/backup alpine \
tar czf /backup/maddy-backup-$(date +%Y%m%d).tar.gz /data
Critical files inside /data:
dkim_keys/— DKIM private keys (losing these means regenerating keys and updating DNS)imapsql.db— Mailbox databasecredentials.db— User authentication database
See Backup Strategy for a complete backup approach.
Troubleshooting
Cannot Connect via IMAP Client
Symptom: Thunderbird or other IMAP clients can’t connect. Fix: Ensure port 993 (IMAPS) is open in your firewall. Use the correct settings:
- Server:
mail.example.com - Port: 993
- Security: SSL/TLS
- Authentication: Normal password
Outgoing Mail Lands in Spam
Symptom: Emails you send arrive in recipients’ spam folders. Fix: Verify all DNS records (SPF, DKIM, DMARC, PTR). Test with mail-tester.com. See Self-Hosted Email Going to Spam for a comprehensive fix guide.
”certificate verify failed” in Logs
Symptom: TLS errors in maddy logs.
Fix: Check that your Let’s Encrypt certificate is valid and the paths in maddy.conf are correct. Renew with certbot renew if expired. The maddy container needs read access to the certificate files.
Port 25 Blocked
Symptom: You can send mail but can’t receive. Fix: Many VPS providers block port 25. Check with your provider. See the provider compatibility table for details.
Resource Requirements
- RAM: ~50 MB idle, 100-150 MB under moderate load
- CPU: Very low — single Go binary
- Disk: ~30 MB for the application, plus mailbox storage
Maddy is significantly lighter than mailcow (~1.5 GB RAM) or Mailu (~512 MB RAM). It’s the most resource-efficient self-hosted email option.
Verdict
Maddy is the best choice for self-hosted email if you want minimal resource usage and a simple setup. A single binary replaces the 6+ components that traditional email stacks require. The trade-off: no web UI (you need a separate webmail client like Roundcube), no admin dashboard (manage via CLI), and a smaller community compared to mailcow. Choose maddy for a lightweight, technically elegant email server. Choose mailcow if you want a batteries-included solution with webmail and admin UI. Choose Mail-in-a-Box if you want the simplest possible setup.
Related
- Chasquid vs Maddy: Lightweight Mail Servers Compared
- Stalwart vs Maddy: Lightweight Email Servers
- Best Self-Hosted Email Servers
- mailcow vs Docker Mailserver
- Mailu vs mailcow
- Email Deliverability: SPF, DKIM, DMARC
- Self-Hosted Email Going to Spam: Fixes
- Replace Gmail
- Docker Compose Basics
- Reverse Proxy Setup
- Backup Strategy
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments