Maddy Composable Mail Server Installation
Maddy is a lightweight, composable mail server delivered as a single binary with built-in SMTP, IMAP, TLS automation, and DKIM signing. This guide covers installing Maddy on Linux, configuring SMTP/IMAP, automating TLS certificates, setting up DKIM, and managing users.
Prerequisites
- Ubuntu 20.04+/Debian 11+ or CentOS 8+/Rocky Linux 8+
- A domain name pointing to your server
- Ports 25, 465, 587, 993, 143 open in your firewall
- Reverse DNS (PTR) set to match your hostname
- Root or sudo access
- No existing mail server running
Installing Maddy
# Method 1: Download the pre-built binary
VERSION=$(curl -s https://api.github.com/repos/foxcpp/maddy/releases/latest \
| grep '"tag_name"' | cut -d'"' -f4)
wget "https://github.com/foxcpp/maddy/releases/download/${VERSION}/maddy-${VERSION#v}-linux-amd64.tar.zst"
tar -I zstd -xf maddy-*.tar.zst
# Install binary and man pages
sudo install -m 755 maddy /usr/local/bin/maddy
sudo install -m 644 maddy.1 /usr/local/share/man/man1/
# Method 2: Build from source (requires Go 1.21+)
git clone https://github.com/foxcpp/maddy.git
cd maddy
go build ./cmd/maddy
sudo mv maddy /usr/local/bin/
Create a dedicated user and directories:
sudo useradd -r -s /sbin/nologin maddy
sudo mkdir -p /etc/maddy /var/lib/maddy /var/log/maddy
sudo chown maddy:maddy /var/lib/maddy /var/log/maddy
sudo chmod 750 /var/lib/maddy
Configuration Overview
Maddy uses a single configuration file at /etc/maddy/maddy.conf. Configuration uses a block-based syntax where modules are composed together:
# Install the default config template
wget -O /etc/maddy/maddy.conf \
https://raw.githubusercontent.com/foxcpp/maddy/master/maddy.conf
sudo chown maddy:maddy /etc/maddy/maddy.conf
sudo chmod 640 /etc/maddy/maddy.conf
Edit the two key lines at the top of the config:
sudo nano /etc/maddy/maddy.conf
# Replace these with your actual values
$(hostname) = mail.yourdomain.com
$(primary_domain) = yourdomain.com
$(local_domains) = $(primary_domain)
SMTP Configuration
The default config includes SMTP with common settings. Key sections to verify:
# /etc/maddy/maddy.conf - SMTP submission (port 587)
smtp tcp://0.0.0.0:587 {
tls off # STARTTLS is used; TLS upgrade happens after EHLO
auth.plain inbound_sasl
source $(local_domains) {
destination $(local_domains) {
deliver_to &local_mailboxes
}
destination postmaster $(local_domains) {
deliver_to &local_mailboxes
}
default_destination {
modify {
dkim sign {
domains $(primary_domain)
selector default
key_path /var/lib/maddy/dkim_keys/{domain}-{selector}.key
}
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Source domain not accepted"
}
}
# Incoming SMTP (port 25)
smtp tcp://0.0.0.0:25 {
tls off
destination postmaster $(local_domains) {
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User not found"
}
}
IMAP Configuration
# IMAP4 (port 143 with STARTTLS)
imap tcp://0.0.0.0:143 {
tls off # STARTTLS
auth.plain inbound_sasl
storage &local_mailboxes
}
# IMAPS (port 993, implicit TLS)
imap tls://0.0.0.0:993 {
storage &local_mailboxes
auth.plain inbound_sasl
}
TLS Automation
Maddy handles Let's Encrypt automatically:
# In maddy.conf, configure TLS
tls file /etc/maddy/tls/fullchain.pem /etc/maddy/tls/privkey.pem
# Or use automatic ACME (Let's Encrypt)
tls {
loader acme {
email [email protected]
agreed
ca https://acme-v02.api.letsencrypt.org/directory
}
}
For manual certificate management:
# Install Certbot
sudo apt install -y certbot
# Obtain certificates (stop maddy first if it uses port 80)
sudo certbot certonly --standalone -d mail.yourdomain.com \
--non-interactive --agree-tos -m [email protected]
# Create symlinks for maddy
sudo mkdir -p /etc/maddy/tls
sudo ln -s /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem \
/etc/maddy/tls/fullchain.pem
sudo ln -s /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem \
/etc/maddy/tls/privkey.pem
sudo chown -h maddy:maddy /etc/maddy/tls/*.pem
DKIM Signing
Generate DKIM keys:
# Create keys directory
sudo mkdir -p /var/lib/maddy/dkim_keys
sudo chown maddy:maddy /var/lib/maddy/dkim_keys
# Generate a 2048-bit RSA key
sudo -u maddy maddy dkim gen \
--domain yourdomain.com \
--selector default \
--directory /var/lib/maddy/dkim_keys
# View the public key for DNS
cat /var/lib/maddy/dkim_keys/yourdomain.com-default.dns
Add the output as a DNS TXT record:
Name: default._domainkey.yourdomain.com
Value: v=DKIM1; k=rsa; p=MIIBIjAN...
DKIM signing configuration in maddy.conf:
modify.dkim default_dkim {
domains yourdomain.com
selector default
key_path /var/lib/maddy/dkim_keys/{domain}-{selector}.key
sign_all_headers true
}
User Management
Maddy uses a credential store for authentication:
# Start Maddy first to initialize the database
sudo systemctl start maddy
# Add a user
sudo maddy creds create [email protected]
# Prompts for password
# Or specify password directly (be careful with shell history)
echo "SecurePassword123!" | sudo maddy creds create [email protected] --stdin
# Create the mailbox
sudo maddy imap-acct create [email protected]
# List users
sudo maddy creds list
# Change a password
sudo maddy creds password [email protected]
# Remove a user
sudo maddy imap-acct remove [email protected]
sudo maddy creds remove [email protected]
Storage Backends
Maddy uses its own storage format by default. Configure in maddy.conf:
# Default local storage
storage.imapsql local_mailboxes {
driver sqlite3
dsn /var/lib/maddy/imapsql.db
}
# Or use PostgreSQL for larger deployments
storage.imapsql local_mailboxes {
driver postgres
dsn "host=127.0.0.1 port=5432 dbname=maddy user=maddy password=maddy_pass sslmode=disable"
}
Create systemd service:
sudo tee /etc/systemd/system/maddy.service << 'EOF'
[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 run
Restart=on-failure
RestartSec=5
# Allow binding to privileged ports
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now maddy
sudo systemctl status maddy
Troubleshooting
Maddy fails to start - "address already in use":
# Check what's using the ports
ss -tlnp | grep -E ':25|:587|:993|:143'
systemctl stop postfix exim 2>/dev/null
TLS certificate errors:
# Verify certificate files are readable by maddy user
ls -la /etc/maddy/tls/
sudo -u maddy openssl x509 -in /etc/maddy/tls/fullchain.pem -noout -dates
Users can't authenticate:
# Check credential store
sudo maddy creds list
sudo journalctl -u maddy -f | grep -i auth
# Reset a password
sudo maddy creds password [email protected]
Mail not being delivered externally:
# Check mail queue
sudo maddy queue list
# Retry stuck messages
sudo maddy queue retry --all
# Check DKIM signature
sudo journalctl -u maddy | grep -i dkim
Port 25 connection refused by destination:
# Verify your IP isn't blacklisted
host -t A $(dig -x $(curl -s ifconfig.me) +short). zen.spamhaus.org
# Check outbound connectivity
telnet gmail-smtp-in.l.google.com 25
Conclusion
Maddy's single-binary design with composable modules makes it one of the easiest complete mail servers to deploy and maintain. Automatic TLS, built-in DKIM signing, and SQLite-backed storage eliminate most of the complexity found in traditional Postfix/Dovecot stacks. For small to medium deployments with a single domain, Maddy delivers full functionality with minimal operational overhead.


