Mail Backend Deploy¶
mail_backend_deploy¶
Deploy a full mail backend with Postfix, Dovecot, and PostgreSQL integration.
Overview¶
This role deploys the complete mail backend on macmini:
Postfix for mail transfer and local delivery
Dovecot for IMAP access and authentication
OpenDKIM for DKIM signing
PostgreSQL for virtual users, domains, and aliases
Architecture¶
EDGE MACMINI (this role)
Port 25 Port 25 (edge only)
Port 587/465 (clients)
Port 993 (IMAP)
Port 4190 (Sieve)
[Edge Relay] ────────────────────────► [Postfix] ─────► [Dovecot LMTP]
│ │
│ ▼
[Clients] ◄───────────────────────────────┴──────► [IMAP/Sieve]
submission (587/465) │
▼
[Maildir Storage]
/mnt/cryptdata/vmail/
Requirements¶
Debian/Ubuntu target system
PostgreSQL server running
Redis server running (for rspamd, optional)
Let’s Encrypt certificates via DNS-01 challenge
Network access from edge relay
⚠️ Network Prerequisites (IMPORTANT)¶
If the backend is behind NAT (e.g., home network), you must configure port forwarding on your router before mail will flow:
External Port |
Forward To |
Purpose |
|---|---|---|
25 |
macmini:25 |
Inbound mail from edge relay |
587 |
macmini:587 |
Client submission (STARTTLS) |
465 |
macmini:465 |
Client submission (implicit TLS) |
993 |
macmini:993 |
IMAP access |
4190 |
macmini:4190 |
ManageSieve (optional) |
Without port 25 forwarding, mail from the edge relay cannot reach the backend!
The edge relay connects to the backend via the public hostname (e.g., smtp.home.xn--wersdrfer-47a.de) which resolves to your home IP via DDNS. If port 25 isn’t forwarded, you’ll see errors like:
connect to smtp.home.xn--wersdrfer-47a.de[...]:25: No route to host
status=deferred
Required Variables¶
# Domains to host (use punycode for IDN)
mail_backend_domains:
- "xn--wersdrfer-47a.de"
- "wersdoerfer.de"
# PostgreSQL password for mail user
mail_backend_postgres_password: "CHANGEME" # Set via SOPS
# Edge relay for outbound mail
mail_backend_relay_host: "mail.wersdoerfer.de"
mail_backend_relay_password: "CHANGEME" # Set via SOPS
Optional Variables¶
Server Configuration¶
Variable |
Default |
Description |
|---|---|---|
|
|
Internal hostname |
|
|
Primary mail domain |
|
|
IMAP hostname |
|
|
SMTP hostname |
Storage¶
Variable |
Default |
Description |
|---|---|---|
|
|
Maildir base path |
|
|
vmail user UID |
|
|
vmail group GID |
PostgreSQL¶
Variable |
Default |
Description |
|---|---|---|
|
|
Database host |
|
|
Database name |
|
|
Database user |
|
|
SQL schema mode ( |
|
|
Domain alias mappings ( |
DKIM¶
Variable |
Default |
Description |
|---|---|---|
|
|
Enable DKIM signing |
|
|
DKIM selector |
|
|
Key size |
Sieve¶
Variable |
Default |
Description |
|---|---|---|
|
|
Enable Sieve filtering |
Example Playbook¶
---
- name: Deploy Mail Backend
hosts: macmini
become: true
vars:
mail_secrets: "{{ lookup('community.sops.sops', 'secrets/prod/mail.yml') | from_yaml }}"
roles:
- role: local.ops_library.mail_backend_deploy
vars:
mail_backend_domains:
- "xn--wersdrfer-47a.de"
- "wersdoerfer.de"
mail_backend_postgres_password: "{{ mail_secrets.postgres_password }}"
mail_backend_relay_host: "mail.wersdoerfer.de"
mail_backend_relay_password: "{{ mail_secrets.relay_password }}"
Database Schema¶
The role creates these tables:
mail_domains- Virtual domainsmail_users- Virtual users (mailboxes)mail_aliases- Email aliases (including catch-all)mail_domain_aliases- Domain-to-domain aliases
And these views for Postfix/Dovecot lookups:
mail_users_view- User email and maildir pathmail_aliases_view- Alias mappingsmail_catchall_view- Catch-all aliasesmail_domain_aliases_expanded- Expanded domain aliasesmail_sender_login_view- Sender/login binding
Client Configuration¶
Mail clients must use the full email address (including domain) as the username:
Setting |
Value |
|---|---|
Username |
|
Password |
(from secrets) |
IMAP Server |
|
IMAP Port |
993 (SSL/TLS) |
SMTP Server |
|
SMTP Port |
587 (STARTTLS) or 465 (SSL/TLS) |
Notes:
For IDN domains like
wersdörfer.de, use the punycode form (xn--wersdrfer-47a.de) in usernames. Some mail clients may send Unicode, which won’t match the database.Apple Mail: Adding the account via Mail.app’s account setup does not work reliably. Instead, add the account via System Settings → Internet Accounts → Add Other Account → Mail Account and use the punycode domain in the username.
Managing Users¶
-- Add a new user (use punycode for IDN domains)
INSERT INTO mail_users (domain_id, localpart, password)
SELECT id, 'jochen', '{SHA512-CRYPT}$6$...'
FROM mail_domains WHERE name = 'xn--wersdrfer-47a.de';
-- Generate password hash
doveadm pw -s SHA512-CRYPT
-- Add an alias
INSERT INTO mail_aliases (source_domain_id, source_localpart, destination)
SELECT id, 'postmaster', 'jochen@wersdoerfer.de'
FROM mail_domains WHERE name = 'wersdoerfer.de';
-- Add catch-all
INSERT INTO mail_aliases (source_domain_id, source_localpart, destination)
SELECT id, '*', 'jochen@wersdoerfer.de'
FROM mail_domains WHERE name = 'wersdoerfer.de';
Files Created¶
Path |
Description |
|---|---|
|
Postfix configuration |
|
Postfix services |
|
PostgreSQL lookups |
|
Dovecot main config |
|
Dovecot SQL auth |
|
Dovecot modules |
|
DKIM configuration |
|
DKIM signing tables |
Services Managed¶
postfix- Mail transfer agentdovecot- IMAP serveropendkim- DKIM signing
Ports¶
Port |
Protocol |
Description |
|---|---|---|
25 |
SMTP |
Inbound from edge (firewall restricted) |
587 |
Submission |
Client submission (STARTTLS) |
465 |
SMTPS |
Client submission (implicit TLS) |
993 |
IMAPS |
IMAP access |
4190 |
ManageSieve |
Sieve script management |
DKIM Setup¶
After deployment, add DNS TXT records for each domain:
mail._domainkey.wersdoerfer.de. TXT "v=DKIM1; k=rsa; p=<public-key>"
The public key is displayed during deployment and stored at:
/etc/opendkim/keys/<domain>/mail.txt
Troubleshooting¶
Check service status¶
systemctl status postfix dovecot opendkim
Test SMTP delivery¶
# Send test mail via LMTP
doveadm mailbox list -u user@domain
Test authentication¶
doveadm auth login user@domain
Check mail logs¶
tail -f /var/log/mail.log
journalctl -u postfix -f
journalctl -u dovecot -f
Verify DKIM¶
opendkim-testkey -d wersdoerfer.de -s mail -vvv
License¶
MIT