Dovecot only — no Postfix. No external mail routing from this server.• Inbound from internet: still via popit3 fetching from Live.com POP3 (unchanged)• Outbound to internet: still via Brevo SMTP relay (envoy replies) or Swisscom (John's own mail)• This server is a private mailbox store for IMAP clients (phone, laptop) and envoy• No PTR record needed; no port 25 involved
Three virtual users (passwd-file auth, SHA512-CRYPT):• john — John's personal mailbox• envoy — Envoy agent mailbox• hermes — Claude Code assistant mailbox
Permanent name: mail.critchley.biz (CNAME → gravlax.critchley.biz).Use this name everywhere (certs, client config, netrc) so the server can move to a different host without reconfiguring clients.
ZoneEdit CNAME to add (no API — must be done manually in ZoneEdit control panel):
mail.critchley.biz. CNAME gravlax.critchley.biz.
File: ~/ansible/setup_mail.yml
ansible-playbook setup_mail.yml -e "target=gravlax" -e @secrets.yml
Prerequisites before running:• CNAME mail.critchley.biz added in ZoneEdit and propagated• secrets.yml contains three pre-hashed passwords (generate with doveadm pw -s SHA512-CRYPT):
mail_pw_john: "{SHA512-CRYPT}$6$..."
mail_pw_envoy: "{SHA512-CRYPT}$6$..."
mail_pw_hermes: "{SHA512-CRYPT}$6$..."
Playbook tags: install, vmail, config, users, certs, ports
• IMAPS on port 993 (TLS required)• LMTP unix socket: /var/run/dovecot/lmtp (mode 0660, owner vmail:vmail)• Maildir storage: /var/mail/vhosts/{user}/• TLS: Let's Encrypt for mail.critchley.biz via Apache webroot on gravlax• Cert renewal hook auto-reloads Dovecot
stunnel on pomelo currently tunnels port 143 → cv.critchley.biz:993. Update to mail.critchley.biz:993 (or leave as-is since cv.critchley.biz is a DNS alias for gravlax anyway).netrc entry machine imap already has login envoy — password will need updating to the new dovecot password.
popit3 currently writes directly to local Maildir on pomelo. Once the mail server is on gravlax, popit3 needs to deliver remotely. Options:• Submit via LMTP over TCP to gravlax (requires small change to process_emails.py)• Move popit3 to run on gravlaxThis is not yet done — popit3 delivery path is the main remaining work item.
first_valid_uid must be 33 — virtual users are mapped to uid=www-data (UID 33) so Dovecot and Apache share ownership of the Maildir (enabling WebDAV delivery). Dovecot's default first_valid_uid is 500, which blocks login with "Mail access for users with UID 33 not permitted". Fix: add first_valid_uid = 33 to /etc/dovecot/conf.d/10-mail.conf and reload Dovecot. This is already in setup_mail.yml but easy to lose on a rebuild.
namespace inbox must be declared — 15-mailboxes.conf defines sub-mailboxes inside a namespace inbox { } block but assumes the parent definition (with inbox = yes) is in 10-mail.conf. Without it Dovecot logs "namespace configuration error: inbox=yes namespace missing" and disconnects immediately after login. Fix: add namespace inbox { inbox = yes } to 10-mail.conf and reload. Already in setup_mail.yml.
Thunderbird must use mail.critchley.biz — not cv.critchley.biz or gravlax.critchley.biz. The Let's Encrypt cert covers only mail.critchley.biz. Connecting with any other hostname gives a "Wrong Site" / impersonation error. IMAP port 993, SSL/TLS.
Playbook written, not yet run. CNAME not yet added in ZoneEdit. gravlax is clean (Debian 12, no Dovecot installed).