Maddy is a modern, lightweight email server written in Go that combines SMTP (MTA), IMAP, and security features in a single binary. Unlike traditional setups requiring separate Postfix, Dovecot, and OpenDKIM installations, Maddy provides everything in one compact package with a simple configuration file. This guide covers deployment and configuration for production email hosting.
Why Maddy?
- Single binary — no complex multi-service setup; one process handles SMTP + IMAP
- Modern defaults — TLS, DKIM, SPF, DMARC, and MTA-STS enabled by default
- Low resources — runs comfortably on 512MB RAM VPS
- Simple config — one configuration file instead of multiple service configs
- Written in Go — static binary, no dependencies, fast startup
Prerequisites
- VPS with public IPv4 and ports 25, 143, 465, 587, 993 open
- Domain with DNS control
- Reverse DNS (PTR) set to your mail hostname
Installation
# Download latest release
MADDY_VERSION=0.7.1
wget https://github.com/foxcpp/maddy/releases/download/v${MADDY_VERSION}/maddy-${MADDY_VERSION}-x86_64-linux-musl.tar.zst
tar --zstd -xf maddy-${MADDY_VERSION}-x86_64-linux-musl.tar.zst
sudo cp maddy-${MADDY_VERSION}-x86_64-linux-musl/maddy /usr/local/bin/
sudo cp maddy-${MADDY_VERSION}-x86_64-linux-musl/maddyctl /usr/local/bin/
# Create system user
sudo useradd -r -s /usr/sbin/nologin -d /var/lib/maddy maddy
sudo mkdir -p /etc/maddy /var/lib/maddy /run/maddy
sudo chown maddy:maddy /var/lib/maddy /run/maddy
Configuration
# /etc/maddy/maddy.conf
# Primary domain and hostname
$(hostname) = mail.example.com
$(primary_domain) = example.com
$(local_domains) = $(primary_domain)
# TLS certificates (use certbot or acme.sh)
tls file /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/letsencrypt/live/mail.example.com/privkey.pem
# SMTP listener (receiving mail from other servers)
smtp tcp://0.0.0.0:25 {
hostname $(hostname)
# Security checks
check {
require_mx_record
dkim
spf
dmarc
}
# Deliver to local mailboxes or reject
deliver_to &local_mailboxes
}
# Submission listener (sending mail from clients)
submission tls://0.0.0.0:465 tcp://0.0.0.0:587 {
hostname $(hostname)
auth &local_authdb
# Sign outgoing mail with DKIM
modify {
dkim {
domain $(primary_domain)
selector default
key_path /var/lib/maddy/dkim_keys/$(primary_domain)_default.key
}
}
deliver_to &remote_queue
}
# IMAP listener
imap tls://0.0.0.0:993 tcp://0.0.0.0:143 {
auth &local_authdb
storage &local_mailboxes
}
# Local authentication database
auth.pass_table local_authdb {
table sql_table {
driver sqlite3
dsn /var/lib/maddy/credentials.db
table_name passwords
}
}
# Local mailbox storage
storage.imapsql local_mailboxes {
driver sqlite3
dsn /var/lib/maddy/imapsql.db
}
# Remote delivery queue
target.queue remote_queue {
target &remote_smtp
}
target.smtp remote_smtp {
hostname $(hostname)
# DKIM signing is handled in submission block
}
Generate DKIM Keys
sudo mkdir -p /var/lib/maddy/dkim_keys
sudo maddy certs dkim generate /var/lib/maddy/dkim_keys/example.com_default.key
sudo chown -R maddy:maddy /var/lib/maddy/dkim_keys
# Get the DNS record value
sudo maddy certs dkim dns /var/lib/maddy/dkim_keys/example.com_default.key
# Output: default._domainkey TXT "v=DKIM1; k=ed25519; p=..."
Create User Accounts
# Create a user
sudo maddyctl creds create user@example.com
# Enter password when prompted
# Create IMAP storage for the user
sudo maddyctl imap-acct create user@example.com
# List users
sudo maddyctl creds list
DNS Records
# MX record
example.com. MX 10 mail.example.com.
# A record
mail.example.com. A your-server-ip
# SPF
example.com. TXT "v=spf1 mx -all"
# DKIM (from certs dkim dns output)
default._domainkey.example.com. TXT "v=DKIM1; k=ed25519; p=..."
# DMARC
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com"
# MTA-STS
_mta-sts.example.com. TXT "v=STSv1; id=20250115"
Systemd Service
# /etc/systemd/system/maddy.service
[Unit]
Description=Maddy Mail Server
After=network.target
[Service]
Type=notify
User=maddy
Group=maddy
ExecStart=/usr/local/bin/maddy -config /etc/maddy/maddy.conf
Restart=on-failure
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now maddy
Testing
# Test SMTP
swaks --to test@example.com --from sender@external.com --server mail.example.com
# Test sending
swaks --to external@gmail.com --from user@example.com --server mail.example.com:587 --auth LOGIN --auth-user user@example.com --tls
# Check deliverability
# Send to check@mail-tester.com and review score
Best Practices
- Use SQLite for small deployments (under 50 users); switch to PostgreSQL for larger ones
- Set up log rotation for
/var/log/maddy/ - Monitor mail queue:
maddyctl queue list - Keep Maddy updated — it is actively developed with security fixes
- Use fail2ban or Maddy's built-in rate limiting for brute-force protection
- Back up
/var/lib/maddy/regularly (contains credentials, mail, and DKIM keys)