Architecture: Stalwart Mail Server

arch-stalwart Architecture

architecture infrastructure email stalwart
## Deployment: Stalwart Mail Server Stalwart Mail Server — Rust single-binary mail server handling SMTP relay for transactional email (password resets, notifications). ### Diagram ```mermaid graph TB subgraph Internet Recipient[Recipient Mail Servers] Bounce[Bounce Messages] end subgraph Hetzner["Hetzner Edge VPS"] Caddy[Caddy :443] subgraph Stalwart["Stalwart Mail Server"] SMTP_In[SMTP :25] Submission[Submission :587] IMAP[IMAPS :993] Admin[Admin UI :8080] DKIM[DKIM Signing] end end subgraph k3s["Home k3s Cluster"] Keycloak[Keycloak Realms] end Keycloak -->|sends via port 587| Submission Submission --> DKIM DKIM -->|signed email| Recipient Bounce -->|port 25| SMTP_In Caddy -->|reverse proxy| Admin ``` ### Components | Component | Purpose | Notes | |-----------|---------|-------| | Stalwart SMTP (port 25) | Inbound relay for bounces | Receives NDRs and feedback loops | | Stalwart Submission (port 587) | Authenticated outbound sending | Keycloak connects here per-realm | | Stalwart IMAPS (port 993) | Optional mailbox access | For checking bounce mailboxes | | Stalwart Admin UI (port 8080) | Web management interface | Behind Caddy reverse proxy | | DKIM Signing | Cryptographic email authentication | Per-domain key pairs | | Keycloak | Identity provider sending transactional email | Each realm has own from-address | ### Key Decisions - **Stalwart over alternatives**: Single binary, ~50MB RAM; no Java/Elasticsearch dependencies - **NOT on k3s**: Requires port 25 + PTR/rDNS on the sending IP — must run on Hetzner VPS - **SaltStack managed**: Declarative state in `salt/states/stalwart/`; pillar-driven config - **Per-realm SMTP config**: Each Keycloak realm sends from its own domain (e.g., noreply@landscaping-assistant.app) - **DKIM per domain**: Keys generated by Stalwart, public key extracted for DNS records ### Related - `arch-edge-vps` — host VPS where Stalwart runs - `arch-dns` — DNS records (SPF/DKIM/DMARC) for mail deliverability - Project: `stalwart-email` ## Integration with Keycloak Keycloak realms connect via SMTP submission (port 587): - Config lives in `~/secrets/pal-e-services/k3s.tfvars` under `keycloak_realms.{realm}.smtp` - Terraform `dynamic "smtp_server"` block in `terraform/keycloak.tf` (lines 47-63) - Each realm gets its own from-address (e.g., noreply@landscaping-assistant.app) ## Per-Domain Setup Pattern For each new sending domain: 1. Create domain + account in Stalwart admin UI 2. Extract DKIM public key 3. Add SPF/DKIM/DMARC DNS records 4. Add `smtp = {...}` block to realm in k3s.tfvars 5. Apply terraform to configure Keycloak ## Key Files - `salt/states/stalwart/init.sls` — install + configure state - `salt/pillar/stalwart.sls` — domains, accounts, DKIM keys - `salt/states/top.sls` — assigns stalwart state to edge-proxy - `terraform/keycloak.tf` — dynamic smtp_server block