Self-hosted Vaultwarden via Posthorn
You’re running Vaultwarden, the lightweight Bitwarden-compatible server. It sends email for new-device login notifications, email verification, organization invitations, emergency access, and email 2FA. On a cloud host that blocks outbound SMTP — or when you’d rather not hand your password manager the provider credentials — Posthorn’s SMTP listener accepts Vaultwarden’s connection on a private Docker network and forwards over HTTPS to whichever transactional provider you’ve chosen.
At the end: Vaultwarden talks to posthorn:2525 over a private Docker network with no TLS and no AUTH, and Posthorn forwards via the configured HTTP transport to Postmark, Resend, Mailgun, or SES.
The shape
Section titled “The shape”Walkthrough
Section titled “Walkthrough”-
Put Posthorn and Vaultwarden on the same private Docker network (no
ports:exposure for Posthorn — see the Authentik recipe for the identical Posthorn service block):# docker-compose.yml (excerpt)networks:internal:internal: trueservices:vaultwarden:image: vaultwarden/server:latestrestart: unless-stoppednetworks: [internal]# ... your volumes, DOMAIN, ADMIN_TOKEN, etc. ... -
Write
posthorn.toml.[smtp_listener]listen = ":2525"auth_required = "none"require_tls = falsetrusted_network = true # required for auth "none" on a container bind (#41)allowed_senders = ["*@yourdomain.com"][smtp_listener.transport]type = "postmark"[smtp_listener.transport.settings]api_key = "${env.POSTMARK_API_KEY}" -
Point Vaultwarden at Posthorn via environment. Vaultwarden enables SMTP as soon as
SMTP_HOSTandSMTP_FROMare set:environment:SMTP_HOST: posthornSMTP_PORT: "2525"SMTP_SECURITY: "off" # plaintext on the internal network — no STARTTLSSMTP_FROM: "vaultwarden@yourdomain.com"SMTP_FROM_NAME: "Vaultwarden"# SMTP_USERNAME / SMTP_PASSWORD intentionally unset — the listener performs no AUTH -
Test from the admin panel. With
ADMIN_TOKENset, open/admin→ SMTP Email Settings → Send test email. Posthorn logs the session:{"msg":"smtp_session_open","ingress":"smtp","tls":"no"}{"msg":"smtp_submission_sent","transport_message_id":"..."}
Common gotchas
Section titled “Common gotchas”| Symptom | Cause | Fix |
|---|---|---|
Vaultwarden log: Connection refused connecting to SMTP | Mail dialed port 25, not 2525 | Set SMTP_PORT="2525" explicitly — with SMTP_SECURITY=off the default is 25 |
| TLS / STARTTLS handshake error in the Vaultwarden log | SMTP_SECURITY still starttls or force_tls | Set SMTP_SECURITY="off"; the listener runs plaintext on the internal network |
550 5.7.1 Sender not authorized from Posthorn | SMTP_FROM domain isn’t in allowed_senders | Add *@yourdomain.com (or the exact address) |
| No Send test email button | Admin panel disabled | Set ADMIN_TOKEN (argon2 hash recommended) and reload /admin |
| Test email works but verification links are wrong | DOMAIN unset or wrong | Set DOMAIN=https://vault.yourdomain.com — it drives the links inside the mail, not the SMTP path |
Only the mail path runs through Posthorn; DOMAIN, ADMIN_TOKEN, and the rest of Vaultwarden’s config are unchanged. See Internal SMTP relay for the trust model behind auth_required = "none".