---
name: docker-mailserver-project
description: docker-mailserver recipe for open-forge. MIT production-ready full-stack containerized mail server — Postfix, Dovecot, Rspamd, ClamAV, OpenDKIM, Fail2ban, SpamAssassin. Config-file based (no SQL). Covers the upstream Docker Compose path from https://docker-mailserver.github.io/docker-mailserver/latest/. Handles SMTP (25), SMTPS (465), Submission (587), IMAPS (993).
---

# docker-mailserver

MIT-licensed production-ready full-stack containerized mail server. Ships with Postfix (SMTP), Dovecot (IMAP), Rspamd (spam filtering), ClamAV (antivirus), OpenDKIM, OpenDMARC, Fail2ban, and more — all in one container. Config-file based: no SQL database required. Upstream: <https://github.com/docker-mailserver/docker-mailserver>. Docs: <https://docker-mailserver.github.io/docker-mailserver/latest/>.

## Compatible install methods

| Method | Upstream | First-party? | When to use |
|---|---|---|---|
| Docker Compose | <https://docker-mailserver.github.io/docker-mailserver/latest/usage/> | Yes | Primary path. Single container, config mounted from host. |
| Helm (Kubernetes) | Community | No | Community Helm chart exists; not upstream-maintained. |

---

## Method — Docker Compose

> **Source:** <https://docker-mailserver.github.io/docker-mailserver/latest/usage/>

### Architecture

One container (`mailserver/docker-mailserver:latest`) that runs all mail server components. Configuration lives in bind-mounted files on the host. Users and domains are managed via the `setup` CLI (runs inside the container).

### Inputs to collect

| Phase | Prompt | Notes |
|---|---|---|
| preflight | Mail hostname (FQDN) | e.g. `mail.example.com`. This is the server's FQDN — must match PTR record. |
| preflight | Primary domain | e.g. `example.com`. Separate from the mail hostname. |
| preflight | Install directory | Where to place `docker-compose.yml` and the `docker-data/` config + data tree. |
| dns | PTR / rDNS record | Set at your VPS provider: reverse DNS for the mail server IP → `mail.example.com`. Required or most providers reject your mail as spam. |
| dns | MX record | `example.com. MX 10 mail.example.com.` |
| dns | SPF TXT record | `example.com. TXT "v=spf1 mx ~all"` (at minimum). |
| dns | DKIM TXT record | Generated by the server after setup. See DKIM setup step below. |
| dns | DMARC TXT record | `_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"` (start with `p=none` while monitoring). |
| tls | TLS certificate | LetsEncrypt or bring-your-own. Mount certs and set `SSL_TYPE`. |
| accounts | First email account | `user@example.com` + password. Added via `setup email add`. |

### Setup

```bash
# 1. Create config + data directories
mkdir -p ./docker-data/dms/mail-data
mkdir -p ./docker-data/dms/mail-state
mkdir -p ./docker-data/dms/mail-logs
mkdir -p ./docker-data/dms/config

# 2. Download the official docker-compose.yml
# See: https://docker-mailserver.github.io/docker-mailserver/latest/usage/#get-the-files
curl -L https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/compose.yaml -o docker-compose.yml
curl -L https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.env -o mailserver.env

# 3. Download the setup helper script
curl -L https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh -o setup.sh
chmod +x setup.sh

# 4. Start the container
docker compose up -d mailserver

# 5. Add your first email account
docker exec mailserver setup email add user@example.com 'SecurePassword'

# 6. Generate DKIM keys
docker exec mailserver setup config dkim
# Published the DKIM public key at:
#   ./docker-data/dms/config/opendkim/keys/example.com/mail.txt
# Copy the TXT record value into your DNS.

# 7. Verify setup
docker exec mailserver setup check-port
docker exec mailserver setup debug show-mail-logs
```

### Key environment variables (mailserver.env)

| Variable | Default | Notes |
|---|---|---|
| `HOSTNAME` | (required) | FQDN of the mail server, e.g. `mail.example.com`. |
| `DOMAINNAME` | (required) | Primary mail domain, e.g. `example.com`. |
| `SSL_TYPE` | `letsencrypt` | TLS mode: `letsencrypt`, `manual`, `self-signed`. |
| `ENABLE_RSPAMD` | `1` | Modern spam filter. Enabled by default in recent versions. |
| `ENABLE_SPAMASSASSIN` | `0` | Legacy spam filter. Use Rspamd instead. |
| `ENABLE_CLAMAV` | `0` | ClamAV antivirus scanning. Enable if you want antivirus; uses ~300 MB RAM. |
| `ENABLE_FAIL2BAN` | `0` | Fail2ban brute-force protection. Requires `NET_ADMIN` capability. |
| `SPOOF_PROTECTION` | `0` | Reject mails where the sender domain does not match an authenticated user's domain. |
| `POSTFIX_INET_PROTOCOLS` | `all` | `ipv4` to disable IPv6 SMTP if your server has no IPv6 PTR. |
| `ONE_DIR` | `1` | Keep all state in a single directory (`mail-state`). Recommended. |
| `PERMIT_DOCKER` | `none` | Docker network range to whitelist in Postfix. Usually `none` for internet-facing. |
| `ENABLE_MANAGESIEVE` | (not set) | Enable Sieve script management for Dovecot. |
| `LOGROTATE_INTERVAL` | `weekly` | Log rotation interval. |

### Volumes

| Host path | Container path | Content |
|---|---|---|
| `./docker-data/dms/mail-data/` | `/var/mail` | Mailbox data (Maildir format) |
| `./docker-data/dms/mail-state/` | `/var/mail-state` | Service state (Postfix queues, etc.) |
| `./docker-data/dms/mail-logs/` | `/var/log/mail/` | Mail logs |
| `./docker-data/dms/config/` | `/tmp/docker-mailserver/` | Config files: accounts, aliases, DKIM keys |

### TLS with LetsEncrypt

Mount your certbot/LetsEncrypt directory and set `SSL_TYPE`:

```yaml
volumes:
  - /etc/letsencrypt:/etc/letsencrypt:ro
environment:
  SSL_TYPE: letsencrypt
  HOSTNAME: mail.example.com
```

The cert must exist at `/etc/letsencrypt/live/mail.example.com/`. Renewal is handled outside the container (by certbot on the host or a separate certbot container); DMS watches for cert changes and reloads automatically.

### Managing accounts

```bash
# Add an account
docker exec mailserver setup email add user@example.com 'Password'

# List accounts
docker exec mailserver setup email list

# Change password
docker exec mailserver setup email update user@example.com 'NewPassword'

# Delete account
docker exec mailserver setup email del user@example.com

# Add an alias (forwards alias@example.com to user@example.com)
docker exec mailserver setup alias add alias@example.com user@example.com
```

### Upgrade procedure

```bash
docker compose pull mailserver
docker compose up -d mailserver
```

Check the [changelog](https://github.com/docker-mailserver/docker-mailserver/blob/master/CHANGELOG.md) for breaking changes before upgrading. Major versions may rename env vars.

### Gotchas

- **PTR / rDNS record is mandatory.** Without a matching reverse DNS record, most receiving mail servers will reject your outbound mail or send it to spam. Set rDNS at your VPS provider control panel before sending any mail.
- **Port 25 is blocked by many cloud providers.** AWS, GCP, Azure, Hetzner (sometimes) block outbound port 25 by default. You must request it be unblocked, or relay through an SMTP relay service (Mailgun, Sendgrid, SES) via `RELAY_HOST`.
- **DKIM must be added to DNS before sending.** Generate DKIM keys with `setup config dkim`, then publish the TXT record. Without DKIM, mail will fail DMARC and be treated as spam or rejected.
- **ClamAV needs ~300 MB RAM just for virus definitions.** Only enable it if the host has enough memory. If the container starts but antivirus is not scanning, check that ClamAV finished its initial database update (`docker logs mailserver | grep clamd`).
- **Fail2ban requires `NET_ADMIN` capability.** Add to the Docker Compose service if you enable it: `cap_add: [NET_ADMIN, NET_RAW]`.
- **`SPOOF_PROTECTION=1` can break legitimate relaying.** Only enable after verifying that all sending clients authenticate with a matching domain.
- **Config files are the source of truth.** Do not edit files inside the running container — changes are overwritten on restart. Always edit via `setup` commands or the mounted config files.
- **IPv6 gotcha.** If your server has an IPv6 address but no IPv6 PTR record, set `POSTFIX_INET_PROTOCOLS=ipv4` to avoid sending over IPv6 and getting rejected.

### Links

- Upstream README: <https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md>
- Full documentation: <https://docker-mailserver.github.io/docker-mailserver/latest/>
- Usage guide: <https://docker-mailserver.github.io/docker-mailserver/latest/usage/>
- Environment variables reference: <https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/>
- DKIM setup: <https://docker-mailserver.github.io/docker-mailserver/latest/config/best-practices/dkim_dmarc_spf/>
- Relay configuration: <https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-forwarding/relay-hosts/>
