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.ioandriy.oblivantsev@produktor.ioano@produktor.iopostmaster@produktor.iopostman@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:143STARTTLS (or:993SSL) - SMTP submission:
mail.produktor.io:587STARTTLS, 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 numbersdoveadm mailbox status ... messages INBOXreports), - 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).