Files
mailgoserver/docker-deploy/README.md
T

5.7 KiB

Docker deployment

Two independent images, both built from the same source tree:

  • Dockerfile — mailgoserver only.
  • Dockerfile.rspamd — mailgoserver + the latest rspamd in the same container, already wired together (see below). Use this one if you want stronger spam filtering than the built-in heuristic score alone.

docker-compose.yml defines both as Compose profiles so a plain docker compose up can't accidentally start both at once:

cd docker-deploy

# mailserver only
docker compose --profile standalone up -d --build

# mailserver + rspamd, bundled
docker compose --profile with-rspamd up -d --build

Either way, the app itself now binds the real standard mail ports by default — 25 (SMTP), 465 (direct-TLS SMTP), 143 (IMAP), 993 (direct-TLS IMAP) — and the admin/webmail UI on its usual non-privileged 5000/5001 (HTTP/HTTPS); put your own reverse proxy or a 80:5000/443:5001 port mapping in front if you want those on 80/443 too (note port 80 is already published here for Let's Encrypt HTTP-01, see below — pick a different host port for the web UI's 80 mapping if you use both). Binding the low mail ports needs no extra capability here since the container runs as root. Copy .env.example to .env in this folder to change any host-side port — useful if something else on the host already owns 25/143/etc., or if you want to run both profiles side by side.

What happens on first boot

There's no baked-in config. On first start, the binary generates a fresh settings.ini with defaults (mirroring internal/config/config.go), a self-signed TLS certificate, DKIM/mailstore master keys, and an empty SQLite database — all inside the /app/data volume, so it survives container restarts/rebuilds. The web UI seeds one admin account: username admin, password Password123!, and forces an immediate username + password change on first login — see the main README for the full first-login walkthrough.

Before using this for real mail, exec into the container (or edit the volume from the host) and update settings.ini:

docker exec -it mailgoserver sh -c 'vi /app/data/settings.ini'
docker restart mailgoserver

At minimum, set [Server] HOSTNAME / helo_hostname to your real mail domain, and if you'll use passkeys, [Auth] rp_id / rp_origin to match the exact domain the admin dashboard is reached at (rp_id can't be localhost once you're on a real domain — see the main README's WebAuthn note). There's no environment-variable override mechanism — settings.ini in the volume is the one source of config truth.

Enabling rspamd (the with-rspamd profile)

The bundled rspamd's default config already listens on 127.0.0.1:11333 for scanning requests — exactly what [Rspamd] url defaults to in settings.ini, and since both processes share the container's network namespace, no networking setup is needed. All that's left is turning it on:

[Rspamd]
enabled      = true
url          = http://127.0.0.1:11333
reject_score = 15

...then restart the container. The built-in heuristic spam score (internal/mailstore/spam.go) always runs regardless of this setting — rspamd is additive, not a replacement, and if it's ever unreachable, mail still flows on the heuristic score alone (rspamd errors are logged, never fatal to delivery).

This bundle intentionally skips Redis — rspamd runs fine without it for SPF/DKIM/RBL/ regexp-based scoring, but Bayes learning and greylisting need it. Add a redis service to docker-compose.yml and point rspamd's redis.conf at it if you need those.

Let's Encrypt HTTP-01 (no DNS provider needed)

If this domain's DNS isn't hosted anywhere the app can automate, enable HTTP-01 on the admin dashboard's Let's Encrypt page and restart the container — it runs independently alongside (or instead of) the DNS-01 flow above, obtaining its own separate certificate. Port 80 (already published by docker-compose.yml) stays bound for the container's whole lifetime once enabled, not just during an obtain — curl it and you should get a plain 200 ok, the quickest way to confirm your port-forwarding/reverse-proxy setup actually reaches this container. Optionally also request the certificate for this container's public IP address (autodetected, or a manual override) so clients connecting by bare IP get a trusted cert too — note this uses Let's Encrypt's shortlived profile, so those certificates renew roughly every few days instead of every couple months (handled automatically).

Which listener actually uses which certificate — the DNS-01 cert, the HTTP-01 cert, or the custom/self-signed one — is chosen independently per listener (SMTP-TLS, IMAP-TLS, web UI) on the admin dashboard's Settings page. A common setup: HTTP-01 for SMTP/IMAP, DNS-01 (or a real custom cert) for the web UI.

Persistence

Volume What's in it
mailserver-data / mailserver-rspamd-data settings.ini, the SQLite DB, encrypted mailbox storage, DKIM/mailstore master keys, TLS certs, the CSRF app secret — everything mailgoserver itself owns.
rspamd-data (rspamd profile only) rspamd's own Bayes/fuzzy-hash storage, so spam-learning state survives image rebuilds.

Back up the *-data volume like you would the equivalent bare-metal server_data/ directory — losing the mailstore master key makes all stored mail unrecoverable, same as a non-Docker install.

Logs / health

docker compose --profile standalone logs -f      # or --profile with-rspamd
docker inspect --format '{{.State.Health.Status}}' mailgoserver

Both images expose GET /health (used by the container HEALTHCHECK), matching the JSON the admin dashboard's own health check reads.