# tailwart > Tailscale × Stalwart. A mailbox with no WAN presence, fronted by a layer-4 > proxy that can live on another machine entirely. A deliberately over-engineered playground: [Stalwart](https://github.com/stalwartlabs/stalwart) mail server wired into **Postgres + Redis + Garage S3** at once, deployed as a Tailscale sidecar, with a separate `caddy-l4` edge that pipes the raw mail ports over the tailnet. For `infinidim.net`. See [CLAUDE.md](./CLAUDE.md) for the architecture and the gotchas, and [LESSONS.md](./LESSONS.md) for the hard-won symptom→cause→fix notes from bringing it up (PG races, ACME DNS-01, PROXY-protocol trust, IPv6 egress, …). ## Layout ``` tailwart/ ├── docker-compose.yml # the mailbox: ts-stalwart sidecar + stalwart (+ IPv6 egress) ├── config/config.json # Stalwart v0.16 bootstrap config — datastore (Postgres) ONLY ├── config/config.toml # dead v0.15-era config, kept as historical reference ├── caddy/ # the edge: custom Caddy (caddy-l4) layer-4 mail proxy │ ├── Dockerfile # pulls prebuilt caddy-l4 binary (caddyserver.com, no local build) │ ├── caddy.json # :25/465/587/143/993 mail + :443 SNI fan-out → stalwart over the tailnet │ ├── docker-compose.yml # deploy on any public-IP, tailnet, tag:reverse-proxy host │ └── README.md ├── acl-snippet.hujson # tag:stalwart owner + grants to merge into your policy ├── .env.example # operator surface — copy to .env └── .gitignore ``` > **v0.16 config model:** `config.json` describes *only* where Postgres lives; > everything else (domains, accounts, listeners, ACME, blob/Redis wiring, proxy > trust, DKIM, spam) lives **in Postgres**, managed via the web UI or JMAP. The > old TOML + `%{env}%` macro model is gone — see CLAUDE.md / LESSONS.md. ## Quickstart ```bash cp .env.example .env && $EDITOR .env # fill secrets (see CLAUDE.md prereqs) # 1. create the stalwart role/db in shared Postgres + the Garage bucket # (one-off; see CLAUDE.md "Prerequisites") # 2. admin console: assign tag:stalwart to the OAuth client + paste acl-snippet # 3. bring up the mailbox (tailnet-only) docker compose up -d # 4. bring up the edge (binds public mail ports; can be a different host) cd caddy && docker compose up -d --build ``` Then point `infinidim.net`'s MX at the edge host, add SPF/DKIM/DMARC, and finish configuration in Stalwart's web admin (`mail.infinidim.net`). ## Status Live. Pinned to `stalwartlabs/stalwart:v0.16.7`, booting cleanly against the shared Postgres/Redis/Garage backends, holding a Let's Encrypt wildcard via ACME DNS-01 (Spaceship), and relaying outbound over the tailnet (the VPS blocks all outbound SMTP ports). The container has its own IPv6 egress. Not yet exposed for inbound IPv6 mail: `mail.infinidim.net` is intentionally A-only until the edge proxy gains v6 `:25` listeners — publish its `AAAA` and the v6 listeners together, or senders will try v6 and some won't fall back. See LESSONS.md.