Mail Server
InitialMailcow setup
Dockerized Setup Guide (Home Server + VPS Proxy + Tailscale)
This guide installs Mailcow on your home server (behind CGNAT), with your VPS handling public-facing TLS/proxying via Caddy (web UI/webmail) and HAProxy (raw mail ports), connected over Tailscale. Sourced from the current official Mailcow docs (docs.mailcow.email) as of mid-2026.
1
-0. Updates
Architecture overview
sudoInternet
apt│
update▼
&&VPS sudo(public aptIP)
upgrade├── -yCaddy → HTTPS termination for mail.seabee.me (webmail/admin UI)
│ reverse_proxy → home server Tailscale IP:8005 (plain HTTP)
└── HAProxy → raw TCP forward for 25 / 465 / 587 / 993 / 995 / 4190
→ home server Tailscale IP (same ports)
│
▼ (Tailscale tunnel)
Home server (behind CGNAT)
└── Mailcow (Docker): Postfix, Dovecot, SOGo, Rspamd, ClamAV, etc.
Mailcow itself runs entirely on your home server. The VPS never stores mail — it just forwards TLS/TCP traffic through the Tailscale tunnel. This means Mailcow's own built-in Let's Encrypt client should be disabled, since Caddy on the VPS is what the outside world actually talks to on 443.
2
-1. InstallPrerequisites
dockerHardware (home server)
- 2+ CPU cores (4+ for production/multiple domains)
- 6 GB RAM minimum + 1 GB swap (8 GB recommended if running ClamAV/full-text search)
- 20 GB disk minimum, more for mailbox storage
If your home server is resource-constrained, you can disable ClamAV and dockerfull-text composesearch in mailcow.conf (SKIP_CLAMD=y, SKIP_FTS=y) to run comfortably on ~2 GB RAM.
OS support
Mailcow supports KVM/ESXi/Hyper-V VMs and bare metal. It explicitly does not support Synology/QNAP NAS devices, OpenVZ, or LXC containers — only real Docker hosts.
Required packages
# Install required packages
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Set up the stable repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update
sudo apt install -y docker-cegit docker-ce-cliopenssl containerd.iocurl gawk coreutils grep jq
(jq is a fairly recent addition to Mailcow's requirements — make sure it's installed.)
Docker (latest engine, not distro package)
curl -sSL https://get.docker.com/ | CHANNEL=stable sh
sudo systemctl enable --now docker
Docker Compose plugin (v2.0+)
sudo apt install docker-compose-plugin
# Add your user to docker group
sudo usermod -aG docker $USER
Confirm:
3docker compose version # must be >= 2.0
Debian 13 (trixie) note
docker compose version # must be >= 2.0
If you upgraded from Debian 12→13, a package called exim may get pulled in and bind port 25 on the host, blocking Mailcow's Postfix container from using it. Remove it first:
sudo apt remove --purge Createexim4 exim4-base exim4-config -y
2. DNS Recordsrecords to configure
Set these at your domain registrar (Namecheap → Advanced DNS), pointing at your VPS's public IP (since that's what actually receives inbound connections):
# Name Type Value
mail IN A 203.57.114.42
autodiscover IN CNAME mail.seabee.me.
autoconfig IN CNAME mail.seabee.me.
@ IN MX 10 mail.seabee.me.
In Namecheap specifically
-
The A record
:and CNAME records go under Advanced DNS → Host Records (Add New Record). -
The MX record goes under Advanced DNS → Mail Settings → Custom MX, filled in as:
Field Value Type MX Record Host @Value mail.seabee.me.Priority 10TTL Automatic -
Important: the MX record only resolves correctly if the
mailA record above already exists — MX points to a hostname, not an IP directly. -
Check Host Records for a stray CNAME on
@(bare domain) — Namecheap gives CNAME priority over MX on the same host, which silently breaks mail delivery if one exists.
SPF, DKIM, DMARC
@ IN TXT "v=spf1 mx a -all"
_dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:postmaster@seabee.me"
Both go under Host Records as TXT records. DKIM is generated inside the Mailcow admin UI after install (Configuration → ARC/DKIM keys) — you'll copy that TXT record in afterward, also as a Host Record.
Reverse DNS (PTR)
Critical for deliverability: your VPS provider, not your DNS zone, controls this — PTR belongs to whoever owns the IP block. The PTR record needs to resolve 203.57.114.42 back to mail..yourdomain.comseabee.me
On Binary Lane: mPanel → Network/IP settings → the "Reverse DNS" field next to your public IPv4 address. Edit it and set it to mail.seabee.me. Binary Lane uses a 12-hour TTL for PTR records, so allow up to half a day for it to propagate. Set the mail A record first — forward and reverse should match, and some providers expect the forward record to already resolve.
Verify once propagated:
dig +short mail.seabee.me # → your serverVPS IP
MXdig record:+short @-x 203.57.114.42 # → mail.seabee.me.
Changing the PTR only affects reverse lookups on that IP — any other hostnames already pointing at the same IP (e.g. a mail.yourdomain.comvps.seabee.me(priorityyou 10)use
v=spf1working mxexactly ~all4 - InstallPTR and configureA mailserver
records are independent.
Optional but recommended: autoconfig SRV records
Full zone-file form:
#_autodiscover._tcp CreateIN directorySRV 0 1 443 mail.seabee.me.
_imaps._tcp IN SRV 0 1 993 mail.seabee.me.
_pop3s._tcp IN SRV 0 1 995 mail.seabee.me.
_submission._tcp IN SRV 0 1 587 mail.seabee.me.
_submissions._tcp IN SRV 0 1 465 mail.seabee.me.
_sieve._tcp IN SRV 0 1 4190 mail.seabee.me.
In Namecheap, SRV records use separate fields rather than one string. Add each one under Host Records → Add New Record → SRV Record:
| Service | Protocol | Priority | Weight | Port | Target |
|---|---|---|---|---|---|
_autodiscover |
_tcp |
0 | 1 | 443 | mail.seabee.me |
_imaps |
_tcp |
0 | 1 | 993 | mail.seabee.me |
_pop3s |
_tcp |
0 | 1 | 995 | mail.seabee.me |
_submission |
_tcp |
0 | 1 | 587 | mail.seabee.me |
_submissions |
_tcp |
0 | 1 | 465 | mail.seabee.me |
_sieve |
_tcp |
0 | 1 | 4190 | mail.seabee.me |
These aren't required for mail to function — the autodiscover/autoconfig CNAMEs above already cover most clients — but they add broader compatibility (notably Outlook via _autodiscover). Fine to add later once core mail delivery is confirmed working.
3. Install Mailcow on the home server
umask 0022
mkdir -p ~/mailserverhome/conor/Docker
cd ~/mailserverhome/conor/Docker
#git Download docker-compose.yml and .env template
wgetclone https://raw.githubusercontent.github.com/docker-mailserver/docker-mailserver/master/compose.yamlmailcow/mailcow-dockerized wgetmailcow
https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.envcd # Rename for easier use
mv compose.yaml docker-compose.yml
mv mailserver.envmailcow
.env/generate_config.sh
Note: Mailcow's own docs default to /opt/mailcow-dockerized, but any path works — Mailcow doesn't hardcode /opt anywhere; it just uses whatever directory you run generate_config.sh and docker compose from. The only requirement is that the user running these commands has read/write access to the directory and to the Docker socket (i.e. is in the docker group, or you run the commands with sudo).
5
You'll -be Editprompted .env variablesfor:
- Mailcow hostname → enter
(must match your DNSHOSTNAME=mailmail.seabee.meA/MXrecords) DOMAINNAME=yourdomain.comOVERRIDE_HOSTNAME=mail.yourdomain.comENABLE_SPAMASSASSIN=1ENABLE_CLAMAV=1ENABLE_FAIL2BAN=1SSL_TYPE=letsencrypt(ormanualif you have your own certs)TimezoneACCOUNT_PROVISIONER=FILE
This creates mailcow.conf.
6
-4. Edit mailcow.conf — key settings for your proxy setup
nano mailcow.conf
mailcow.conf — key settings for your proxy setupnano mailcow.conf
Change/confirm these values:
MAILCOW_HOSTNAME=mail.seabee.me
# Disable Mailcow's built-in Let's Encrypt — Caddy on the VPS handles TLS instead
SKIP_LETS_ENCRYPT=y
# Skip the public-IP-matches-DNS check, since this host is behind CGNAT
SKIP_IP_CHECK=y
# Disable IPv6 — nothing in the VPS→Tailscale→home path uses it, and half-enabling
# it (detected but not configured in Docker's daemon.json) just invites confusion
ENABLE_IPV6=false
# nginx (webmail/admin UI) — scoped to this host's Tailscale IP only.
# This is the ONLY service where mailcow.conf controls the bind address directly;
# Postfix/Dovecot ports have no _BIND variable and are handled in the Docker
# Compose override file instead (see Section 5).
HTTP_BIND=100.64.0.3
HTTP_PORT=8005
HTTPS_BIND=100.64.0.3
HTTPS_PORT=8443
# Adjust if these clash with anything else on your Docker host
IPV4_NETWORK=172.22.1
Replace 100.64.0.3 with your home server's actual Tailscale IP (tailscale ip -4).
Why SKIP_LETS_ENCRYPT=y: Mailcow's ACME client tries to prove domain ownership by responding on port 443 with the host's own certificate. Since the VPS (via Caddy) is what the public internet actually reaches, Mailcow's own ACME client would fail here anyway — Caddy already handles issuance/renewal for mail.seabee.me.
Why HTTP_PORT=8005 rather than the Mailcow default of 8080: purely to avoid clashing with anything else already using 8080 on this host — pick whatever's free on your system, just make sure the same number is used consistently in the Caddy reverse_proxy line in Section 8.
5. Bind Postfix/Dovecot ports to Tailscale only
By default, Mailcow's docker-compose.yml binds mail ports to all interfaces (0.0.0.0). Since your home server sits behind CGNAT, nothing on the raw internet can reach these ports regardless — but leaving them on 0.0.0.0 still means every device on your home LAN and every peer on your tailnet can connect to them directly, bypassing HAProxy entirely. Scoping to the Tailscale IP limits that down to just the VPS.
Only Postfix and Dovecot need this override. nginx (webmail/admin UI) is already scoped via HTTP_BIND/HTTPS_BIND in mailcow.conf (Section 4) — that's the only service where the base docker-compose.yml exposes a bind-address variable. Postfix and Dovecot's port lines have no such variable, so a Compose override is the only way to restrict them.
Get your home server's Tailscale IP:
tailscale ip -4
Create docker-compose.override.yml in the same folder as docker-compose.yml (never edit docker-compose.yml directly — ./update.sh overwrites it on every update, but the override file is preserved):
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
container_name: mailserver
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
hostname: mx.home.conorbriggs.com.au
env_file: .env
# More information about the mail-server ports:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/postfix-mailcow:
ports:
- "100.64.0.3:25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
- "143:143" # IMAP4 (explicit TLS => STARTTLS)100.64.0.3:465:465"
- "465:465"100.64.0.3:587:587"
#dovecot-mailcow:
ESMTP (implicit TLS)ports:
- "587:587" # ESMTP (explicit TLS => STARTTLS)100.64.0.3:993:993"
- "993:993" # IMAP4 (implicit TLS)
volumes:100.64.0.3:995:995"
- ./mail-data/:/var/mail/
- ./mail-state/:/var/mail-state/
- ./mail-logs/:/var/log/mail/
- ./config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
restart: always
stop_grace_period: 1m
# Uncomment if using `ENABLE_FAIL2BAN=1`:
cap_add:
- NET_ADMIN
healthcheck:
test: "ss --listening --ipv4 --tcp | grep --silent ':smtp' || exit 1"
timeout: 3s
retries: 0
networks:
- mailserver-network
networks:
mailserver-network:
driver: bridge100.64.0.3:4190:4190"
Replace 100.64.0.3 with your home server's actual Tailscale IP. If you also want LAN clients (phones/laptops on your home WiFi) to connect directly without going through the VPS, add a second binding per port using your LAN IP instead of, or alongside, the Tailscale one.
Apply it:
Connecting
IMAP (Incoming):
Server:mx.home.conorbriggs.com.auPort:993(IMAPS with SSL/TLS)Security: SSL/TLSAuthentication: Normal password
SMTP (Outgoing):
Server:mx.home.conorbriggs.com.auPort:587(with STARTTLS) or465(with SSL/TLS)Security: STARTTLS (port 587) or SSL/TLS (port 465)Authentication: Normal password
Container Management
Basic Container Operations
Start the mailserver
docker compose up -d
docker compose ps
Compose
recreatesStoponly the mailserver
containers dockerwhose composeconfig downchanged.
RestartVerify the mailserver
bindings dockerlanded composeon restartthe View logs (live)
docker compose logs -fView logs (last 100 lines)interface:
docker compose logs --tail=100Check container status
docker ps -aCheck container health
docker inspect mailserver | grep -A 10 Health
Email Account Management
Create Email Accounts
Create a new email account (will prompt for password)
docker exec -it mailserver setup email add user@home.conorbriggs.com.au
Create account with password in command
docker exec -it mailserver setup email add user@home.conorbriggs.com.au password123
Create account with quota (e.g., 500MB)
docker exec -it mailserver setup email add user@home.conorbriggs.com.au password123 500M
List Email Accounts
List all email accounts
docker exec mailserver setup email list
View the accounts file directly
docker exec mailserver cat /tmp/docker-mailserver/postfix-accounts.cf
Update/Change Passwords
Update password for existing account
docker exec -it mailserver setup email update user@home.conorbriggs.com.au new_password
Change password (alternative method - will prompt)
docker exec -it mailserver setup email update user@home.conorbriggs.com.au
Delete Email Accounts
Delete an email account
docker exec -it mailserver setup email del user@home.conorbriggs.com.au
Delete account and remove mailbox data
docker exec mailserver setup email del user@home.conorbriggs.com.au
rm -rf ./mail-data/home.conorbriggs.com.au/user
Alias Management
Create Aliases
Create an alias (forward emails from alias to recipient)
docker exec mailserver setup alias add alias@home.conorbriggs.com.au recipient@home.conorbriggs.com.au
Create alias with multiple recipients
docker exec mailserver setup alias add sales@home.conorbriggs.com.au "user1@home.conorbriggs.com.au,user2@home.conorbriggs.com.au"
List Aliases
List all aliases
docker exec mailserver setup alias list
View aliases file
docker exec mailserver cat /tmp/docker-mailserver/postfix-virtual.cf
Delete Aliases
Delete an alias
docker exec mailserver setup alias del alias@home.conorbriggs.com.au recipient@home.conorbriggs.com.au
Quota Management
Set Quotas
Set quota for a user (e.g., 1GB)
docker exec mailserver setup quota set user@home.conorbriggs.com.au 1G
Set unlimited quota
docker exec mailserver setup quota set user@home.conorbriggs.com.au 0
Check Quotas
Check quota for specific user
docker exec mailserver setup quota get user@home.conorbriggs.com.au
List all quotas
docker exec mailserver setup quota list
Check quota usage
docker exec mailserver doveadm quota get -u user@home.conorbriggs.com.au
Delete Quotas
Remove quota (sets to default)
docker exec mailserver setup quota del user@home.conorbriggs.com.au
DKIM (Email Signing)
Generate DKIM Keys
Generate DKIM key for domain
docker exec mailserver setup config dkim
Generate for specific domain
docker exec mailserver setup config dkim domain home.conorbriggs.com.au
Generate with custom key size
docker exec mailserver setup config dkim keysize 2048
View DKIM Public Key
Show DKIM DNS record
docker exec mailserver setup config dkim help
View the public key directly
docker exec mailserver cat /tmp/docker-mailserver/opendkim/keys/home.conorbriggs.com.au/mail.txt
Fail2Ban (Security)
Fail2Ban Status
Check fail2ban status
docker exec mailserver setup fail2ban status
Check banned IPs
docker exec mailserver setup fail2ban
Unban an IP address
docker exec mailserver setup fail2ban unban <IP_ADDRESS>
Ban an IP address
docker exec mailserver setup fail2ban ban <IP_ADDRESS>
Debugging & Diagnostics
Service Status
Check all listening ports
docker exec mailserver ss -tlnp
Check specific service status
docker exec mailserver supervisorctl status
Check Postfix status
docker exec mailserver postfix status
Check Dovecot status
docker exec mailserver doveadm service status
Mail Queue
View mail queue
docker exec mailserver postqueue -p
Flush mail queue (retry sending)
docker exec mailserver postqueue -f
Delete all queued mail
docker exec mailserver postsuper -d ALL
Delete specific message from queue
docker exec mailserver postsuper -d <QUEUE_ID>
Logs
View mail logs
docker exec mailserver tail -f /var/log/mail/mail.log
View mail errors
docker exec mailserver tail -f /var/log/mail/mail.err
View specific log files
docker exec mailserver ls -la /var/log/mail/
Search logs for specific email
docker exec mailserver grep "user@domain.com" /var/log/mail/mail.log
Test Email Delivery
Test SMTP connection
docker exec mailserver nc -zv localhost 25
Send test email from command line
echo "Test email body" | docker exec -i mailserver sendmail test@home.conorbriggs.com.au
Test with swaks (if installed)
docker exec mailserver swaks --to user@home.conorbriggs.com.au --from test@home.conorbriggs.com.au
Connection Testing
Test IMAP connection
docker exec mailserver nc -zv localhost 143
docker exec mailserver nc -zv localhost 993
Test SMTP connection
docker exec mailserver nc -zv localhost 25
docker exec mailserver nc -zv localhost 587
docker exec mailserver nc -zv localhost 465
Check TLS/SSL certificates
docker exec mailserver openssl s_client -connect localhost:993 -showcerts
docker exec mailserver openssl s_client -connect localhost:465 -showcerts
Configuration Management
Reload Configuration
Reload postfix configuration
docker exec mailserver postfix reload
Reload dovecot configuration
docker exec mailserver doveadm reload
Restart all services
docker compose restart
View Configuration
View postfix configuration
docker exec mailserver postconf
View dovecot configuration
docker exec mailserver doveconf
View specific postfix setting
docker exec mailserver postconf | grep smtp_tls
Check all environment variables
docker exec mailserver envtlpn | grep -E '(SMTP|IMAP|SSL|TLS)'25|465|587|993|995|4190|8005|8443'
You should see 100.64.0.3 (not 0.0.0.0) next to each port.
Backup
Configuration
6. Home server firewall
Restrict inbound access on these ports to just your VPS's Tailscale IP, since that's the only legitimate source of forwarded traffic:
Backup all mail data
tarsudo -czfufw mailserver-backup-$(dateallow +%Y%m%d).tar.gzfrom ./mail-data<VPS_TAILSCALE_IP> ./mail-stateto ./configany port 25,465,587,993,995,4190,8005,8443 proto tcp
(Adjust
forBackupiptables/nftables justif configuration
tar -czf mailserver-config-$(date +%Y%m%d).tar.gz ./config
Backup specific user'that's mailbox
what taryou're -czfrunning user-backup-$(date +%Y%m%d).tar.gz ./mail-data/home.conorbriggs.com.au/user instead.)
7. Start Database/User Management
User Database
List all users in Dovecot
Mailcow
docker exec mailserver doveadm user '*'
Check if user exists
docker exec mailserver doveadm user user@home.conorbriggs.com.au
View user's mailbox location
docker exec mailserver doveadm mailbox status -u user@home.conorbriggs.com.au all '*'
Mailbox Management
List mailboxes for user
docker exec mailserver doveadm mailbox list -u user@home.conorbriggs.com.au
Create mailbox for user
docker exec mailserver doveadm mailbox create -u user@home.conorbriggs.com.au Folder.Name
Delete mailbox
docker exec mailserver doveadm mailbox delete -u user@home.conorbriggs.com.au Folder.Name
Rebuild mailbox index
docker exec mailserver doveadm force-resync -u user@home.conorbriggs.com.au INBOX
Performance & Monitoring
Check Resource Usage
Check container stats
docker stats mailserver
Check disk usage
docker exec mailserver df -h
Check memory usage
docker exec mailserver free -h
Check mail directory size
du -sh ./mail-data/*
Connection Monitoring
Show active connections
docker exec mailserver ss -tn | grep -E ':(25|587|465|143|993)'
Count connections by port
docker exec mailserver ss -tn | grep -E ':(25|587|465|143|993)' | wc -l
Show who's connected to IMAP
docker exec mailserver doveadm who
SSL/TLS Certificate Management
Check Certificates
Check SSL certificate expiry
docker exec mailserver openssl x509 -in /etc/letsencrypt/live/mx.home.conorbriggs.com.au/fullchain.pem -noout -dates
View certificate details
docker exec mailserver openssl x509 -in /etc/letsencrypt/live/mx.home.conorbriggs.com.au/fullchain.pem -noout -text
Test SSL/TLS for SMTP
openssl s_client -connect mx.home.conorbriggs.com.au:465 -showcerts
Test STARTTLS for SMTP
openssl s_client -connect mx.home.conorbriggs.com.au:587 -starttls smtp
Troubleshooting
Common Issues
Check if services are running
docker exec mailserver supervisorctl status
Restart specific service
docker exec mailserver supervisorctl restart postfix
docker exec mailserver supervisorctl restart dovecot
Check for permission issues
docker exec mailserver ls -la /var/mail/
docker exec mailserver ls -la /tmp/docker-mailserver/
Verify DNS records
dig mx home.conorbriggs.com.au
dig txt _dmarc.home.conorbriggs.com.au
dig txt mail._domainkey.home.conorbriggs.com.au
Test email authentication
docker exec mailserver opendkim-testkey -d home.conorbriggs.com.au -s mail
Reset and Clean Up
Remove all mail data (WARNING: deletes all emails)
docker compose down
rm -rf ./mail-data/*
rm -rf ./mail-state/*
docker compose up -d
Clear logs
docker exec mailserver truncate -s 0 /var/log/mail/mail.log
Rebuild entire container
docker compose down
docker compose pull
docker compose up -d
--force-recreatedocker compose ps
You should see 15+ containers reporting Up.
8. VPS side — wire up Caddy and HAProxy
QuickCaddy Reference(web UI / webmail / autodiscover — HTTPS termination)
Setup
Add Scripta Helpnew
site block to your existing Caddyfile:
Show all setup commands
dockermail.seabee.me exec{
mailserverreverse_proxy setup100.64.0.3:8005
help}
autodiscover.seabee.me, autoconfig.seabee.me {
reverse_proxy 100.64.0.3:8005
}
Caddy
handlesHelpthe ACME cert issuance/renewal automatically here, same as your other sites — no extra config needed for specificthat command
part.
HAProxy (raw mail ports — TCP passthrough)
This is the same pattern from your earlier HAProxy config — just confirm the backend IPs point at your home server's Tailscale IP and the ports match Mailcow's:
dockerfrontend execsmtp_in
mailservermode setuptcp
emailoption helptcplog
dockerbind exec*:25
mailserverdefault_backend setupsmtp_home
aliasbackend helpsmtp_home
dockermode exectcp
mailserverserver setuphome config100.64.0.3:25 helpcheck
frontend smtp_submission_in
mode tcp
option tcplog
bind *:587
default_backend smtp_submission_home
backend smtp_submission_home
mode tcp
server home 100.64.0.3:587 check
frontend smtps_in
mode tcp
option tcplog
bind *:465
default_backend smtps_home
backend smtps_home
mode tcp
server home 100.64.0.3:465 check
frontend imaps_in
mode tcp
option tcplog
bind *:993
default_backend imaps_home
backend imaps_home
mode tcp
server home 100.64.0.3:993 check
frontend pop3s_in
mode tcp
option tcplog
bind *:995
default_backend pop3s_home
backend pop3s_home
mode tcp
server home 100.64.0.3:995 check
frontend sieve_in
mode tcp
option tcplog
bind *:4190
default_backend sieve_home
backend sieve_home
mode tcp
server home 100.64.0.3:4190 check
Reload HAProxy:
File Locations Inside Container
/tmp/docker-mailserver/sudo - Configuration files (mapped to ./config/)
/var/mail/ - Mail data (mapped to ./mail-data/)
/var/mail-state/ - State files (mapped to ./mail-state/)
/var/log/mail/ - Mail logs (mapped to ./mail-logs/)
/etc/letsencrypt/ - SSL certificates (read-only)
/etc/postfix/ - Postfix configuration
/etc/dovecot/ - Dovecot configuration
Important Configuration Files
./config/postfix-accounts.cf - Email accounts
./config/postfix-virtual.cf - Aliases
./config/dovecot-quotas.cf - User quotas
./config/opendkim/ - DKIM keys
Advanced Operations
Database Operations
Export all accounts
docker exec mailserver cat /tmp/docker-mailserver/postfix-accounts.cf > accounts-backup.txt
Import accounts
cat accounts-backup.txt | docker exec -i mailserver tee /tmp/docker-mailserver/postfix-accounts.cf
docker compose restart
Verify account database
docker exec mailserver postmap -q user@home.conorbriggs.com.au /tmp/docker-mailserver/postfix-accounts.cf
Custom Scripts
Run custom maintenance script
docker exec mailserver /bin/bashhaproxy -c "your-script-here"
Execute interactive shell
docker exec -it mailserver /bin/bash
Monitoring & Alerts
Set Up Monitoring
Watch logs in real-time
docker compose logs -f --tail=100
Monitor for failed logins
docker exec mailserver tail -f /var/log/mail/mail.logetc/haproxy/haproxy.cfg
|sudo grepsystemctl "authenticationreload failed"haproxy
On send-proxy: as covered before, only add this once Postfix/Dovecot on the home server are explicitly configured with postscreen_upstream_proxy_protocol = haproxy / haproxy_trusted_networks. Get everything working without it first.
MonitorVPS mail queue size
firewall
watchsudo -nufw 60allow 'docker25,465,587,993,995,4190/tcp
exec mailserver postqueue -p | tail -1'
(80/443
shouldCheckalready be open for errors
Caddy.)
9. First login
dockerhttps://mail.seabee.me/admin
exec mailserver grep -i error /var/log/mail/mail.log | tail -20
Default credentials: admin / moohoo — change this immediately under Configuration → Access → Admin details.
10. Post-install Commonchecklist
- DKIM: generate a
NewkeyUserinthe
admin
→ ARC/DKIM keys), then add the TXT record it gives you to your DNS1.UICreate(Configurationaccount - Change the default admin password (step 9)
- Fail2Ban: built into Mailcow, verify it's enabled in the admin panel
- Watchdog notifications: configure so you get alerted if a component goes down
- Updates: run
./update.shfrom/home/conor/Docker/mailcowmonthly at minimum; more often for security patches
Adding
11. Testing
docker# execFrom an external machine, confirm each port reaches Mailcow
openssl s_client -itstarttls mailserversmtp setup-connect emailmail.seabee.me:587
addopenssl newuser@home.conorbriggs.com.aus_client -connect mail.seabee.me:993
DNS/deliverability checks:
2.
Set- MX
quotaToolbox (optional)
— dockerMX, execSPF, mailserverblacklist setupcheck
quotaMail-tester.com set— newuser@home.conorbriggs.com.ausend 2G a test
email,3.get Verifya accountdeliverability created
score dockercovering execSPF/DKIM/DMARC
mailserver setup email list
4. Test login: Use an email client
check-auth@verifier.port25.com
Migrating Mail
1. Backup old server
tar -czf old-mailserver-backup.tar.gz ./mail-data
2. Copy to new server
scp old-mailserver-backup.tar.gz newserver:/path/to/mailserver/
3. Extract on new server
tar -xzf old-mailserver-backup.tar.gz
4. Fix permissions
chown -R 5000:5000 ./mail-data
5. Restart mailserver
docker compose restart
Security Hardening
1. Enable Fail2Ban (in .env file)
ENABLE_FAIL2BAN=1
2. Check banned IPs regularly
docker exec mailserver fail2ban-client status postfix-sasl
3. Monitorraw authentication attempts
reportdocker exec mailserver grep "authentication failed" /var/log/mail/mail.log
4. Review SSL/TLS settings
docker exec mailserver postconf | grep tls
Quick reference: Tipsport &map
| Service | Port | VPS |
Forwards |
|---|---|---|---|
| Webmail/Admin |
443 | Caddy |
home:8005 |
| SMTP | 25 | HAProxy |
home:25 |
| Submission | 587 | HAProxy |
home:587 |
| SMTPS | 465 | HAProxy |
home:465 |
| IMAPS | 993 | HAProxy |
home:993 |
| POP3S | 995 | HAProxy |
home:995 |
| ManageSieve | 4190 | HAProxy |
home:4190 |
docker compose pull && docker compose up -d