Skip to content

Repository files navigation

Maddy Mail Server - Ansible Deployment

Build Packages Molecule Tests

Ansible role and package build system for Maddy Mail Server - a modern mail server that combines SMTP (MTA), IMAP, DKIM, SPF, DMARC, and ACME in a single daemon.

Документация на русском

Features

  • Automatic .deb and .rpm package builds via GitHub Actions
  • Ansible role for Maddy installation and configuration
  • ACME (Let's Encrypt) support for automatic TLS certificates
  • Automatic DKIM key generation
  • Fail2ban integration for brute-force protection
  • Firewall configuration (UFW/firewalld)

Requirements

For Deployment

  • Ansible 2.14+
  • Target OS: Debian 11/12, Ubuntu 20.04/22.04/24.04

DNS

Before installation, configure DNS records:

  • A/AAAA record for the mail server
  • PTR (reverse DNS) record

Quick Start

1. Clone the Repository

git clone --recursive https://github.com/foxzi/maddy-setup.git
cd maddy-setup

2. Install Ansible Collections

ansible-galaxy collection install -r requirements.yml

3. Configure Inventory

cp inventory/example.yml inventory/production.yml
# Edit inventory/production.yml

4. Run the Playbook

ansible-playbook -i inventory/production.yml site.yml

Configuration

Main Variables

# Maddy version
maddy_version: "0.8.2"

# Domain and host
maddy_hostname: "mx1.example.org"
maddy_primary_domain: "example.org"
maddy_local_domains:
  - "example.org"

# TLS (ACME)
maddy_tls_mode: "acme"
maddy_acme_email: "admin@example.org"

# DKIM
maddy_dkim_selector: "default"
maddy_dkim_key_type: "ed25519"  # or rsa2048

Creating Mail Users

maddy_users:
  - email: "user@example.org"
    password: "secure_password"

Aliases

maddy_aliases:
  postmaster: "admin@example.org"
  abuse: "admin@example.org"

Prometheus Metrics

Enable Prometheus/OpenMetrics endpoint:

maddy_metrics_enabled: true
maddy_metrics_address: "127.0.0.1"  # or "0.0.0.0" for external access
maddy_metrics_port: 9749

Scrape endpoint: http://127.0.0.1:9749/metrics

Secure Metrics with Caddy (optional)

Protect metrics endpoint with TLS and authentication:

maddy_metrics_enabled: true
maddy_caddy_enabled: true
maddy_caddy_metrics_port: 8443

# Option 1: Basic auth
maddy_caddy_auth_mode: "basic"
maddy_caddy_basic_user: "prometheus"
maddy_caddy_basic_password: "secure_password"

# Option 2: IP whitelist
maddy_caddy_auth_mode: "ip"
maddy_caddy_allowed_ips:
  - "10.0.0.0/8"
  - "192.168.1.100"

Secure endpoint: https://mail-server:8443/metrics (self-signed TLS)

Available metrics:

  • maddy_smtp_failed_logins - AUTH failures
  • maddy_smtp_started_transactions - Started SMTP transactions
  • maddy_smtp_completed_transactions - Completed transactions
  • maddy_queue_length - Queued messages
  • maddy_check_reject - Rejected by checks
  • maddy_remote_conns_tls_level - Outbound TLS security level

Outbound Relay

Allow trusted IPs to send mail through this server without authentication:

maddy_relay_enabled: true
maddy_relay_port: 2525

# Trusted networks (CIDR)
maddy_relay_trusted_networks:
  - "10.0.0.0/8"
  - "192.168.1.0/24"

# Optional: limit relay to specific domains
maddy_relay_allowed_domains:
  - "gmail.com"
  - "company.com"

Firewall rules are automatically configured to allow access only from trusted networks.

All Variables

See roles/maddy/defaults/main.yml for the full list of variables.

DNS Records

After installation, configure the following DNS records:

MX Record

example.org. IN MX 10 mx1.example.org.

SPF Record

example.org. IN TXT "v=spf1 mx ~all"

DMARC Record

_dmarc.example.org. IN TXT "v=DMARC1; p=quarantine; ruf=mailto:postmaster@example.org"

DKIM Record

The DKIM public key is generated automatically on first run. After installation, find it in:

/etc/maddy/dkim_dns_records.txt

Or run:

maddyctl creds dkim-key -S default example.org

Building Packages

Pre-built Packages

Pre-built packages for amd64 and arm64 are available on GitHub Releases.

Note: amd64 packages are built with CGO for full SQLite support in auth.pass_table. arm64 packages are built without CGO and use the transpiled SQLite driver (works for storage.imapsql only).

Local Build

# Install dependencies (Debian/Ubuntu)
sudo apt-get install -y golang scdoc libsqlite3-dev

# Install nfpm
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@latest

# Build packages
./build/scripts/build.sh --version 0.8.2

GitHub Actions

Packages are built automatically when a tag is created:

git tag v0.8.3
git push origin v0.8.3

Built packages are published to GitHub Releases.

Management

maddyctl Commands

# List users
maddyctl creds list

# Create user
maddyctl creds create user@example.org

# Change password
maddyctl creds password user@example.org

# Delete user
maddyctl creds remove user@example.org

# Show DKIM key
maddyctl creds dkim-key -S default example.org

Check Status

systemctl status maddy
journalctl -u maddy -f

Testing

Molecule Tests

Run automated tests with Molecule and Docker:

# Install dependencies
pip install molecule molecule-plugins[docker] ansible docker

# Run tests
cd roles/maddy
molecule test

Tests verify:

  • Binary installation
  • Configuration syntax
  • Service startup
  • Port availability (SMTP, IMAP)
  • Metrics endpoint

Dry Run

ansible-playbook -i inventory/production.yml site.yml --check --diff

Mail Server Testing

Send Test Email

# Install swaks
apt install swaks

# Send test email
swaks --to test@gmail.com --from user@example.org --server mx1.example.org

Project Structure

maddy-setup/
├── .github/workflows/    # CI/CD for package builds
├── build/
│   ├── nfpm.yaml        # Package build configuration
│   └── scripts/         # Build scripts
├── maddy/               # Git submodule with Maddy sources
├── roles/maddy/         # Ansible role
│   ├── defaults/        # Default variables
│   ├── handlers/        # Handlers
│   ├── tasks/           # Tasks
│   └── templates/       # Config templates
├── inventory/           # Inventory examples
├── site.yml            # Main playbook
└── requirements.yml    # Ansible Galaxy dependencies

Security

  • All passwords are passed with no_log: true
  • Using Ansible Vault for password storage is recommended
  • Fail2ban protects against brute-force attacks
  • Systemd unit includes strict sandboxing

Using Ansible Vault

# Create encrypted variables file
ansible-vault create inventory/vault.yml

# Run playbook with vault
ansible-playbook -i inventory/production.yml site.yml --ask-vault-pass

Upgrading

Upgrading Maddy

  1. Update the submodule:
cd maddy
git fetch --tags
git checkout v0.8.3
cd ..
  1. Build new packages:
git tag v0.8.3
git push origin v0.8.3
  1. Update maddy_version in inventory and run the playbook.

Troubleshooting

Maddy Won't Start

# Check configuration
maddy -config /etc/maddy/maddy.conf verify

# View logs
journalctl -u maddy -n 100

ACME Not Getting Certificate

  1. Ensure port 443 is open
  2. Verify DNS records
  3. Try staging CA: maddy_acme_staging: true

DKIM Keys Not Generated

Keys are generated on first email send. Send a test email.

License

MIT

Links

About

MTA maddy setup

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages