Files
mail-server/README.md
T
eSlider 5a46ba4b33 feat(mail-admin): read-only account view for mail.produktor.io (#74)
- admin/: Go stdlib-only HTTP viewer, lists accounts from
  config/postfix-accounts.cf with per-mailbox message counts (INBOX and
  total, same numbers doveadm reports) and storage; quota from
  dovecot-quotas.cf; Basic Auth from .env; offline tests vs fixtures
  (go test -race ./...)
- compose: mail-admin service, build admin/Dockerfile, publishes
  127.0.0.1:19945 / 172.17.0.1:19945; config and mail-data mounted :ro,
  no docker socket
- NPM proxy host 66: location /admin/ -> 172.17.0.1:19945
- README: account admin section
2026-09-01 15:07:55 +01:00

3.9 KiB

mail-server

Docker Compose mail stack for mail.produktor.io on arc-01, based on docker-mailserver (DMS).

Service Container Ports
Mail server (DMS) mailserver 25 (SMTP), 465 (SMTPS), 587 (Submission STARTTLS), 143 (IMAP STARTTLS), 993 (IMAPS)
Webmail (Roundcube) webmail 127.0.0.1:19944 / 172.17.0.1:19944 (HTTP, behind NPM)
Account admin (read-only) mail-admin 127.0.0.1:19945 / 172.17.0.1:19945 (HTTP, behind NPM)

Accounts

Source of truth is file-based: config/postfix-accounts.cf (SHA512-CRYPT hashes). Current mailboxes:

  • info@produktor.io
  • andriy.oblivantsev@produktor.io
  • ano@produktor.io
  • postmaster@produktor.io
  • postman@produktor.io

Passwords live in .env (INFO_PASSWORD, ANDRIY_PASSWORD; ano@ uses GATOR_MAIL_PASS in the gator repo .env). Do not commit .env.

Web UI (Roundcube)

Webmail runs as the webmail service (official roundcube/roundcubemail image) and is reachable at https://mail.produktor.io (alias https://webmail.produktor.io) via Nginx Proxy Manager (proxy host 66 → 172.17.0.1:19944, Let's Encrypt).

Login: any mailbox address from the table above + its real password. The UI shows one mailbox per login; to see all accounts, log in with each one. The account list is the postfix-accounts.cf file (see Accounts).

Connection details used by the webmail (IMAP/SMTP):

  • IMAP: mail.produktor.io:143 STARTTLS (or :993 SSL)
  • SMTP submission: mail.produktor.io:587 STARTTLS, AUTH required

Host note: the webmail must connect to the DMS container via the FQDN mail.produktor.io (Docker embedded DNS resolves it to the mailserver container inside the compose network). Connecting to the bare container alias mailserver fails TLS peer-name verification, because the DMS certificate is issued for mail.produktor.io.

Manage

docker compose up -d            # start mailserver + webmail
docker compose logs -f webmail  # webmail logs
docker exec webmail sh          # shell into webmail

The webmail stores its sqlite database (addressbook, settings) in data/roundcube/db/. ROUNDCUBEMAIL_DES_KEY (session encryption) must be set in .env — compose fails without it.

Account admin (read-only view)

The mail-admin service is a small Go (stdlib-only) HTTP viewer for all accounts at once: https://mail.produktor.io/admin/ (HTTP Basic Auth, NPM proxy host 66, location /admin/ → 172.17.0.1:19945).

It shows every account from config/postfix-accounts.cf with:

  • INBOX message count (files in the Maildir cur/ + new/, the same numbers doveadm mailbox status ... messages INBOX reports),
  • total messages across all mailboxes (incl. subfolders),
  • storage used and the quota limit from config/dovecot-quotas.cf,
  • timestamp of the newest message.

Read-only by design: ./config/ and ./data/mail-data/ are mounted :ro, no docker socket, no host access. Management (add/del accounts) stays in docker-mailserver (setup email add, edit postfix-accounts.cf).

Source: admin/ (Go 1.25, go test -race ./... offline vs admin/testdata/).

Manage

docker compose up -d            # builds mail-admin from admin/Dockerfile
docker compose logs -f mail-admin
curl -u "$MAIL_ADMIN_USER:$MAIL_ADMIN_PASSWORD" https://mail.produktor.io/admin/api/accounts

Credentials MAIL_ADMIN_USER / MAIL_ADMIN_PASSWORD are required in .env (compose fails without them). The JSON API is at /admin/api/accounts.

Reverse proxy (NPM)

mail.produktor.io is a proxy host in Nginx Proxy Manager (provider container, see the gitea repo): forward http://172.17.0.1:19944, Let's Encrypt cert (SAN: mail.produktor.io, webmail.produktor.io), SSL forced, HTTP/2.

TLS

DMS uses a Let's Encrypt certificate for mail.produktor.io mounted from tls/letsencrypt/mail.produktor.io/ (SSL_TYPE=letsencrypt).