Self-hosted Authentik via Posthorn
You’re running Authentik as your identity provider. Authentik sends email for account verification, password recovery, and MFA enrollment. On a cloud VPS that blocks outbound SMTP, or when you simply don’t want to hand Authentik your provider credentials, Posthorn’s SMTP listener accepts Authentik’s connection on a private Docker network and forwards over HTTPS to whichever transactional provider you’ve chosen.
At the end: Authentik points its email settings at posthorn:2525 over a private Docker network — no TLS, no AUTH — and Posthorn forwards via the configured HTTP transport to Postmark, Resend, Mailgun, or SES.
The shape
Section titled “The shape”Only the Authentik worker sends mail, but pointing both server and worker at Posthorn is simplest and harmless.
Walkthrough
Section titled “Walkthrough”-
Put Posthorn and Authentik on the same private Docker network (no
ports:exposure for Posthorn):# docker-compose.yml (excerpt)networks:internal:internal: trueservices:posthorn:image: ghcr.io/craigmccaskill/posthorn:latestrestart: unless-stoppedvolumes:- ./posthorn.toml:/etc/posthorn/config.toml:ro # Podman/SELinux: append ,Zenv_file: .envnetworks: [internal]# no ports: — reachable only from inside the network -
Write
posthorn.toml.[smtp_listener]listen = ":2525"auth_required = "none"require_tls = falsetrusted_network = true # asserts the Docker network is the trust boundary (#41)allowed_senders = ["*@yourdomain.com"][smtp_listener.transport]type = "postmark"[smtp_listener.transport.settings]api_key = "${env.POSTMARK_API_KEY}" -
Point Authentik at Posthorn via environment (applies to both
serverandworker):environment:AUTHENTIK_EMAIL__HOST: posthornAUTHENTIK_EMAIL__PORT: "2525"AUTHENTIK_EMAIL__USE_TLS: "false"AUTHENTIK_EMAIL__USE_SSL: "false"AUTHENTIK_EMAIL__FROM: "authentik@yourdomain.com"# AUTHENTIK_EMAIL__USERNAME / __PASSWORD intentionally unset -
Start the stack and test. In the Authentik admin interface, use System → Settings → Test email (or trigger a password recovery). Posthorn logs the session:
{"msg":"smtp_session_open","ingress":"smtp"}{"msg":"smtp_submission_sent","transport_message_id":"..."}
Common gotchas
Section titled “Common gotchas”| Symptom | Cause | Fix |
|---|---|---|
| Authentik logs a TLS handshake error | AUTHENTIK_EMAIL__USE_TLS / USE_SSL still true | Set both to false; the listener runs plaintext on the internal network |
550 5.7.1 Sender not authorized | AUTHENTIK_EMAIL__FROM isn’t on allowed_senders | Add the address (or a *@domain wildcard) |
Posthorn won’t start: auth_required = "none" refused | Missing the trust ack | Add trusted_network = true, and keep the listener off any host port |
See Internal SMTP relay for the full reasoning on the auth-none + private-network model.