Netgrimoire/Keystone-Grimoire/Mail/Install.md
2026-04-12 09:53:51 -05:00

15 KiB

title description published date tags editor dateCreated
Mailcow Dockerized Install and Config true 2026-02-25T21:05:48.256Z markdown 2026-02-25T21:05:38.864Z

MailCow — Installation & Configuration

Host: docker4 (192.168.5.16) Hostname: hermes.netgrimoire.com Admin URL: https://mail.netgrimoire.com Version: 2025-10a (update 2026-01 available as of documentation date) Installed: /opt/mailcow-dockerized Timezone: America/Chicago Architecture: x86_64 CPU: 16 cores RAM: 30.63 GB Disk: /dev/nvme0n1p2 — 442G / 502G used (93% — monitor this)


Overview

Mailcow runs as a Docker stack on docker4, attached to the netgrimoire overlay network. All containers use restart: unless-stopped via a compose override. Outbound mail routes through MXRoute via sender-dependent transports. Inbound mail arrives from MXRoute which acts as the public-facing inbound gateway (solving residential AT&T IP filtering issues with banks).

See MXRoute Master Configuration for full inbound/outbound/DNS detail per domain.


Installation Paths

Path Purpose
/opt/mailcow-dockerized/ Mailcow root
/opt/mailcow-dockerized/mailcow.conf Primary configuration file
/opt/mailcow-dockerized/docker-compose.yml Base compose (do not edit)
/opt/mailcow-dockerized/docker-compose.override.yml Local overrides — network and restart policy
/opt/mailcow-dockerized/data/conf/postfix/extra.cf Persistent Postfix overrides
/opt/mailcow-dockerized/data/conf/postfix/main.cf Postfix base config (managed by Mailcow)
/opt/mailcow-dockerized/data/conf/rspamd/ Rspamd configuration
/opt/mailcow-dockerized/data/assets/ssl/ TLS certificates

mailcow.conf — Key Settings

MAILCOW_HOSTNAME=hermes.netgrimoire.com
MAILCOW_PASS_SCHEME=BLF-CRYPT

# Database
DBNAME=mailcow
DBUSER=mailcow
DBPASS=mg7Z8W9UsPlOh0S6vF7TmmPb6n1s
DBROOT=JdymsZFFACHkDcOdziQ53QruCTG2

# Redis
REDISPASS=6AduWQsmBYGMKfOi1CNEGQfTE3RH

# Ports — HTTPS runs on 3443, proxied through Caddy
HTTP_PORT=80
HTTP_BIND=
HTTPS_PORT=3443
HTTPS_BIND=
HTTP_REDIRECT=n

# Mail ports (standard)
SMTP_PORT=25
SMTPS_PORT=465
SUBMISSION_PORT=587
IMAP_PORT=143
IMAPS_PORT=993
POP_PORT=110
POPS_PORT=995
SIEVE_PORT=4190

# Internal ports (localhost only)
DOVEADM_PORT=127.0.0.1:19991
SQL_PORT=127.0.0.1:13306
REDIS_PORT=127.0.0.1:7654

# TLS cert coverage
ADDITIONAL_SAN=smtp.*,imap.*
AUTODISCOVER_SAN=y

# ACME / Let's Encrypt
SKIP_LETS_ENCRYPT=n
SKIP_IP_CHECK=y
SKIP_HTTP_VERIFICATION=y

# Services — all enabled
SKIP_CLAMD=n
SKIP_OLEFY=n
SKIP_SOGO=n
SKIP_FTS=n

# FTS (Flatcurve/Xapian)
FTS_HEAP=128
FTS_PROCS=1

# Watchdog
USE_WATCHDOG=y
WATCHDOG_NOTIFY_START=y
WATCHDOG_NOTIFY_BAN=n
WATCHDOG_EXTERNAL_CHECKS=n

# Networking
IPV4_NETWORK=172.22.1
IPV6_NETWORK=fd4d:6169:6c63:6f77::/64
ENABLE_IPV6=false

# Misc
MAILDIR_GC_TIME=7200
MAILDIR_SUB=Maildir
SOGO_EXPIRE_SESSION=480
SOGO_URL_ENCRYPTION_KEY=ojmPfhnM4MYMsA2f
ACL_ANYONE=disallow
ALLOW_ADMIN_EMAIL_LOGIN=n
DOCKER_COMPOSE_VERSION=native
COMPOSE_PROJECT_NAME=mailcow
LOG_LINES=9999

docker-compose.override.yml

All services are attached to the external netgrimoire overlay network and set to restart: unless-stopped. The override does not change any image versions or environment variables — it only adds network membership and restart policy.

services:
  unbound-mailcow:
    networks:
      netgrimoire:
    restart: unless-stopped

  mysql-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  redis-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  clamd-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  rspamd-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  php-fpm-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  sogo-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  dovecot-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  postfix-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  postfix-tlspol-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  memcached-mailcow:
    restart: unless-stopped

  nginx-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  acme-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  watchdog-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  dockerapi-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  olefy-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

  ofelia-mailcow:
    networks:
      - netgrimoire
    restart: unless-stopped

networks:
  netgrimoire:
    external: true
    driver: overlay

Container Image Versions

From docker-compose.yml (base file — version 2025-10a):

Service Image
unbound-mailcow ghcr.io/mailcow/unbound:1.24
mysql-mailcow mariadb:10.11
redis-mailcow redis:7.4.6-alpine
clamd-mailcow ghcr.io/mailcow/clamd:1.71
rspamd-mailcow ghcr.io/mailcow/rspamd:2.4
php-fpm-mailcow ghcr.io/mailcow/phpfpm:1.94
sogo-mailcow ghcr.io/mailcow/sogo:1.136
dovecot-mailcow ghcr.io/mailcow/dovecot:2.35
postfix-mailcow ghcr.io/mailcow/postfix:1.81
postfix-tlspol-mailcow ghcr.io/mailcow/postfix-tlspol:1.0
memcached-mailcow memcached:alpine
nginx-mailcow ghcr.io/mailcow/nginx:1.05
acme-mailcow ghcr.io/mailcow/acme:1.94
netfilter-mailcow ghcr.io/mailcow/netfilter:1.63
watchdog-mailcow ghcr.io/mailcow/watchdog:2.09
dockerapi-mailcow ghcr.io/mailcow/dockerapi:2.11
olefy-mailcow ghcr.io/mailcow/olefy:1.15
ofelia-mailcow mcuadros/ofelia:latest

Postfix Configuration

extra.cf

myhostname = hermes.netgrimoire.com

The MXRoute trusted network entries should also be here. Current extra.cf only contains myhostname — confirm mynetworks is set correctly or add the MXRoute IP ranges if not already present via the UI.

Key Postfix Settings (from running config)

mynetworks = 127.0.0.0/8 172.22.1.0/24 10.0.1.0/24 [::1]/128 [fd4d:6169:6c63:6f77::]/64 [fe80::]/64
message_size_limit = 104857600    # 100MB
mailbox_size_limit = 0            # unlimited
bounce_queue_lifetime = 1d
maximal_queue_lifetime = 5d
delay_warning_time = 4h
postscreen_dnsbl_threshold = 6
postscreen_dnsbl_action = enforce
postscreen_greet_action = enforce
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
disable_vrfy_command = yes
broken_sasl_auth_clients = yes

Domains

10 domains configured. All active.

Domain Mailboxes Sender-Dependent Transport Created
bamalady.com 0 / 10 (not confirmed)
bill740.com 1 / 10 (not confirmed)
florosafd.org 4 / 10 ID 4: heracles.mxrouting.net:587 (relay@florosafd.org) 2025-11-21
gnarlypandaproductions.com 2 / 10 ID 5: heracles.mxrouting.net:587 (relay@gnarlypandaproductions.com) 2025-11-21
netgrimoire.com 2 / 10 ID 2: heracles.mxrouting.net:587 (relay@netgrimoire.com) 2025-11-21
nucking-futz.net 0 / 10 (not confirmed)
pncfishandmore.com 4 / 10 ID 6: heracles.mxrouting.net:587 (relay@pncfishandmore.com)
pncharris.com 4 / 10 ID 3: heracles.mxrouting.net:587 (passer@pncharris.com) 2025-11-21
pncharrisenterprises.com 2 / 10 (not confirmed from screenshots)
wasted-bandwidth.net 1 / 10 ID 1: heracles.mxrouting.net:587 (relay@wasted-bandwidth.net)

MXRoute relay hostname is heracles.mxrouting.net:587 — note this differs from the generic smtp.mxroute.com placeholder used in setup docs. Always use heracles.mxrouting.net:587 for this account.


Mailboxes

19 active mailboxes across all domains:

Mailbox Messages Domain
bill@bill740.com 1 bill740.com
chieflee@florosafd.org 2124 florosafd.org
cindy@pncfishandmore.com 1109 pncfishandmore.com
cindy@pncharris.com 33797 pncharris.com
cindy@pncharrisenterprises.com 819 pncharrisenterprises.com
dads_attic@pncharris.com 0 pncharris.com
jim.harris@florosafd.org 8 florosafd.org
kyle@gnarlypandaproductions.com 486 gnarlypandaproductions.com
kyle@pncfishandmore.com 110 pncfishandmore.com
kyle@pncharris.com 31182 pncharris.com
phil@florosafd.org 5 florosafd.org
phil@gnarlypandaproductions.com 5 gnarlypandaproductions.com
phil@netgrimoire.com 1 netgrimoire.com
phil@pncfishandmore.com 10 pncfishandmore.com
phil@pncharris.com 3210 pncharris.com
phil@pncharrisenterprises.com 1 pncharrisenterprises.com
times@florosafd.org 191 florosafd.org
traveler@netgrimoire.com 3 netgrimoire.com
traveler@wasted-bandwidth.net 138 wasted-bandwidth.net

Aliases

ID Alias Target Domain Internal
7 cindy@bamalady.com bamalady.com No

Sender-Dependent Transports

All outbound relay routes through heracles.mxrouting.net:587. This is your MXRoute server hostname — use this exact value when adding new transports.

ID Host Username Password
1 heracles.mxrouting.net:587 relay@wasted-bandwidth.net dZ4yLYznVvgSJtqWZJFA
2 heracles.mxrouting.net:587 relay@netgrimoire.com TVGCnJp9SxRbWU8EhkMw
3 heracles.mxrouting.net:587 passer@pncharris.com bBJtPhrGkHvvhxhukkae
4 heracles.mxrouting.net:587 relay@florosafd.org 2Fe8XMyaeh6Z5dvdHYdq
5 heracles.mxrouting.net:587 relay@gnarlypandaproductions.com vG5ZsUQhRWD2UyzLPsqA
6 heracles.mxrouting.net:587 relay@pncfishandmore.com (confirm from MXRoute panel)

DKIM Keys

Two DKIM selectors are configured per domain — one for Mailcow (selector: dkim) and one added separately for MXRoute outbound signing. The Mailcow-managed keys use selector dkim._domainkey.

pncharris.com

v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqhgQV7r+KKQwJceWenZ3FNq8AsllgW6cIm/0jpsLT62vF1yy0nh2MdhjYgQAX2MK9HHYzNZcCB3+OPpqBbXeNbSDckxB/dC+z/vboMHrJmYonfaSYshZjSR80V/a2Yoq+hiXQ9eBcuOggENtMm4XvEsl/vOWLBMfasqe+X11gzQBeRv1tTaXJB0C4i7tAcfi0O/AxH8QFTr2099+k2iepn8J15ukk1zu4zemBJj4Z3uFTNnBP8YpgKbYoUDyMVIKIxGjANVBBypcrMKavpQ4F1JLhgGFhWAsAuFRwZsnOaftZyMuzAZxM37DTd/bF2WanmK3Xe75SN5uOnEXjuzW/wIDAQAB

netgrimoire.com

v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoJ9YKqV9+6gOcVKI+UJ0TRcMmergxU8HLO+mwTMfqOhblsEcDPO60c8ya24iIXg51AA2k5Xcbb0bLScaaIi0P/TRzP/bonAZkPS1Y8Fx1se9dikTsA9Lazho u6DvoFkkV/IPH1ZNg68Cd9teAD5tvoY18OSneJJsocXwFo57c+XccUaTxjpV7eReuT4da7iNHMmUmZNfKenxVMKD740zrDJAeAsXtEb/71CochHYSm+qAvuG9/WPixJbMsJLF/iVhV3Byp0LCrB+CwGTwnsiUcd7QpuD6rRs/7zzdGBtoN22m/j390GimFstYvB61I20h8sHWGAG66dLko6Sgvs47wIDAQAB

gnarlypandaproductions.com

v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...

(scroll cut off in screenshot — retrieve full key from Mailcow UI → Edit domain → bottom of page)

All other domain DKIM keys should be retrieved from the Mailcow domain edit page and recorded here for disaster recovery completeness.


Network Configuration

Mailcow containers join the netgrimoire external overlay network, allowing communication with other Docker Swarm services (Caddy reverse proxy, etc.) without exposing ports directly to the host network.

Internal Docker network: 172.22.1.0/24

Key container IPs within the mailcow-network:

  • unbound: 172.22.1.254
  • redis: 172.22.1.249
  • sogo: 172.22.1.248
  • dovecot: 172.22.1.250
  • postfix: 172.22.1.253

IPv6: disabled (ENABLE_IPV6=false)


Caddy Reverse Proxy

Mailcow's nginx listens on HTTPS port 3443 internally. Caddy proxies external requests to it. Mailcow handles its own TLS for direct mail client connections (IMAP 993, SMTP 465/587).

The admin UI at mail.netgrimoire.com is proxied through Caddy on the netgrimoire overlay network.


Updating Mailcow

cd /opt/mailcow-dockerized

# Pull latest
git fetch origin
git checkout origin/master

# Update containers
docker compose pull
./update.sh

As of documentation date, version 2026-01 is available. Current running version is 2025-10a. Update when convenient — check the MailCow changelog for breaking changes first.

Monthly update check is recommended. MailCow had multiple security vulnerabilities in 2025 — staying current is important.


Common Operations

Restart all containers

cd /opt/mailcow-dockerized
docker compose restart

Restart single container (e.g. after extra.cf change)

docker compose restart postfix-mailcow

View logs

# Postfix
docker compose logs postfix-mailcow -f

# Dovecot
docker compose logs dovecot-mailcow -f

# All containers
docker compose logs -f

Check queue

docker exec mailcow-postfix-mailcow-1 postqueue -p

Flush queue

docker exec mailcow-postfix-mailcow-1 postqueue -f

Check container health

docker compose ps

Known Gotchas

Disk usage is at 93%. The nvme0n1p2 volume has 442G used of 502G. This needs attention — vmail storage grows over time and garbage collection runs hourly but only removes items older than 7200 minutes (5 days). Monitor this and consider quota enforcement per mailbox if growth continues.

extra.cf is minimal. The MXRoute trusted network IPs should be confirmed in the running Postfix config. The mynetworks value from postconf shows 10.0.1.0/24 is already trusted — confirm whether MXRoute IP ranges 69.167.160.0/19 and 198.54.120.0/22 are included. If not, add them to extra.cf and restart postfix.

MXRoute relay hostname. The actual relay hostname for this account is heracles.mxrouting.net:587 — not the generic smtp.mxroute.com placeholder. All 6 transports use heracles.mxrouting.net:587. Use this exact hostname for any new transport entries.

pncharris.com uses passer@ not relay@. Transport ID 3 for pncharris.com authenticates as passer@pncharris.com, not relay@pncharris.com. This is intentional — the relay@ account exists but passer@ is the current active relay credential.

HTTPS on port 3443. Mailcow's web UI is not on the standard 443 — it binds to 3443 and Caddy handles the public-facing 443 proxy. Direct access to the UI requires going through Caddy or using the internal port.

nucking-futz.net vs nucking-futz.com. The domains list shows nucking-futz.net but the intended new domain is nucking-futz.com. Verify which is actually configured and correct if needed.

bamalady.com and bill740.com have no transport assigned in the screenshots. Confirm whether these domains need MXRoute relay configured.