I can tidy it up and send it later but as you know the majority of the talk was about why I think having your own email server is still valuable.
Here is a modififed version of my compose.yaml file.
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
container_name: mailserver
restart: always
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
hostname: mail.homelabbrisbane.com.au # must match your FQDN
ports:
- "25:25"
- "465:465"
- "588:587"
- "993:993"
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/ # Used to inject site specific configuration variables indo DMS
- /etc/localtime:/etc/localtime:ro
# - /etc/letsencrypt:/etc/letsencrypt:ro
environment:
- ENABLE_RSPAMD=1
- ENABLE_CLAMAV=0 # if low on RAM else 1
- ENABLE_FAIL2BAN=1
- SSL_TYPE=letsencrypt # manual
- RELAY_HOST=smtp.mailgun.org
- RELAY_PORT=587
- RELAY_USER=${RELAY_USER}
- RELAY_PASSWORD=${RELAY_PASSWORD}
- DMS_DEBUG=1
# Optional: healthcheck
healthcheck:
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
timeout: 3s
retries: 5
start_period: 10s
cap_add:
- NET_ADMIN # Required for Fail2Ban (iptables manipulation)
# networks:
# - labnet
#networks:
# labnet:
# external: true
As I mentioned I use caddy for my certificates but if it’s standalone letsencrypt “natively” is better.
My compose file has relay details that can be found in the .env file. If you have your domain and DNS records ready to go setting up for receiving email is pretty easy.
I also asked OpenRouter how to do it and this is their more concise reply and goes into the details of dkim, spf and _dmarc.
When you get stuck AI will assist with what to put in /tmp/docker-mailsever and its subdirectories.
HTH.
docker mail server docker-compose.yaml
User - –
docker mail server docker-compose.yaml
Assistant - –
Here is a comprehensive docker-compose.yaml (also called compose.yaml) for docker-mailserver — the most popular, production-ready, fully-featured self-hosted mail server. It uses Postfix + Dovecot + Rspamd + ClamAV + Fail2Ban + OpenDKIM/DMARC.
Project Structure
mailserver/
├── compose.yaml
├── mailserver.env
└── docker-data/
└── dms/
├── mail-data/ # Mailboxes (Dovecot)
├── mail-state/ # State (Fail2Ban, Postgrey DB, etc.)
├── mail-logs/ # Mail logs
└── config/ # DMS config (accounts, DKIM, aliases, etc.)
compose.yaml — Full Production Setup
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
container_name: mailserver
# -------------------------------------------------------
# Replace with your actual FQDN (must match your MX record)
# -------------------------------------------------------
hostname: mail.example.com
ports:
- "25:25" # SMTP — Inbound mail (MTA-to-MTA)
- "143:143" # IMAP — StartTLS
- "465:465" # ESMTP — Implicit TLS (SMTPS)
- "587:587" # SMTP — Submission (StartTLS, for mail clients)
- "993:993" # IMAPS — Implicit TLS
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
# Uncomment if using Let's Encrypt certificates:
# - /etc/letsencrypt:/etc/letsencrypt:ro
env_file:
- mailserver.env
environment:
# --- General ---
- OVERRIDE_HOSTNAME=mail.example.com # Set if hostname above is not FQDN
# --- Security ---
- ENABLE_FAIL2BAN=1
- SPOOF_PROTECTION=1
# --- TLS ---
- SSL_TYPE=letsencrypt # Options: letsencrypt | manual | self-signed | off
# --- Anti-Spam ---
- ENABLE_RSPAMD=1 # Modern spam filter (recommended)
- ENABLE_SPAMASSASSIN=0 # Legacy — disable if using Rspamd
- ENABLE_POSTGREY=0 # Greylisting (optional)
# --- Anti-Virus ---
- ENABLE_CLAMAV=1 # Set to 0 if low on RAM (<2 GB)
# --- Docker networking ---
- PERMIT_DOCKER=network # Allows other containers to send mail
# WARNING: Don't use on open internet without firewall
# --- IMAP/POP3 ---
- ENABLE_POP3=0 # Enable if you need POP3 access
cap_add:
- NET_ADMIN # Required for Fail2Ban (iptables manipulation)
- SYS_PTRACE # Optional: needed for some debugging tools
ulimits:
nofile:
soft: 65536
hard: 65536
restart: always
# Optional: healthcheck
healthcheck:
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
timeout: 3s
retries: 5
start_period: 10s
mailserver.env — Environment Variables File
# General
OVERRIDE_HOSTNAME=mail.example.com
POSTMASTER_ADDRESS=postmaster@example.com
# TLS
SSL_TYPE=letsencrypt
# Security
SPOOF_PROTECTION=1
ENABLE_FAIL2BAN=1
# Anti-Spam
ENABLE_RSPAMD=1
ENABLE_SPAMASSASSIN=0
ENABLE_POSTGREY=0
# Anti-Virus (disable if <2GB RAM)
ENABLE_CLAMAV=1
# Protocols
ENABLE_IMAP=1
ENABLE_POP3=0
# Docker Network
PERMIT_DOCKER=network
Note: Do not quote values in .env files for DMS — it doesn’t support quoted values.
Initial Setup & First Run
Step 1 — Download config files
DMS_URL="https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master"
wget "${DMS_URL}/compose.yaml"
wget "${DMS_URL}/mailserver.env"
Step 2 — Create email accounts
# Add email accounts
docker exec -it mailserver setup email add admin@example.com yourpassword
docker exec -it mailserver setup email add user@example.com yourpassword
# List accounts
docker exec -it mailserver setup email list
Step 3 — Generate DKIM Keys
docker exec -it mailserver setup config dkim
Then find your DKIM public key in:
./docker-data/dms/config/opendkim/keys/example.com/mail.txt
Add it as a TXT DNS record for mail._domainkey.example.com.
Step 4 — Start the server
# Foreground (for debugging)
docker compose up
# Background (production)
docker compose up -d
# View logs
docker compose logs -f mailserver
Required DNS Records
| Type |
Name |
Value |
| A |
mail.example.com |
YOUR_SERVER_IP |
| MX |
example.com |
mail.example.com (priority 10) |
| TXT (SPF) |
example.com |
v=spf1 mx ~all |
| TXT (DKIM) |
mail._domainkey.example.com |
(generated by setup) |
| TXT (DMARC) |
_dmarc.example.com |
v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com |
| PTR (rDNS) |
YOUR_SERVER_IP |
mail.example.com (set at VPS provider) |
Key Notes
Ports 25 & 465/587 must be open in your firewall. Many VPS providers block port 25 by default — request it to be unblocked.
RAM: ClamAV alone uses ~500MB+. Minimum 2 GB RAM recommended; 4 GB for full stack.
SSL: Use Certbot for Let’s Encrypt. DMS auto-reads certs from /etc/letsencrypt/.
Image: ghcr.io/docker-mailserver/docker-mailserver:latest (latest is v15.1.0 as of 2025).