Skip to content

Self-Hosted Mail Server (2026)

Status of the irregulars.io community mail stack: production. Stalwart 0.15.5 on 46.110.152.206 (proxmox), with SOGo webmail at mail.irregulars.io, transactional app mail via Cloudflare Workers send_email binding from mail.irregulars.io subdomain, and inbound MX direct to Stalwart (Cloudflare Email Routing decommissioned 2026-05-16).

This page documents what’s deployed, what the 2026 landscape looks like, and the SSO-integration considerations that drove the architecture decisions.

LayerServiceURL / IP
Mailbox server (SMTP/IMAP/JMAP/Sieve)Stalwart 0.15.546.110.152.206 (smtp/imap.irregulars.io)
WebmailSOGohttps://mail.irregulars.io
Mail adminStalwart admin UIhttps://mail-admin.irregulars.io
Inbound MXStalwart directirregulars.io MX 10 smtp.irregulars.io
Outbound (user clients)Brevo SMTP relay(transitional — direct from .206 after PTR is set)
Outbound (app transactional)Cloudflare Workers send_emailbinding in wrangler.toml
SSO / IdPAuthentikhttps://sso.irregularchat.com

Two important new realities in 2026:

  1. Cloudflare now does BOTH inbound routing AND outbound sending. Email Routing has been around since 2022; Email Service went public beta in 2026 and lets Cloudflare Workers send transactional email via a send_email binding. We use it for app-level transactional mail (meeting reminders, invites) but not for user mailboxes.

  2. App Passwords are now the standard pattern for OIDC + legacy clients. Stalwart 0.16.5, Mailcow, Mailu, and every modern self-hosted mail server either ships or supports app-password flows because mainstream clients (Apple Mail, Outlook, Thunderbird) don’t speak OAUTHBEARER/XOAUTH2 with third-party OAuth providers. SSO at the webmail/admin layer, App Passwords at the mail-client layer, is the canonical 2026 architecture.

  • Inbound mail to *@irregulars.io is delivered to Stalwart mailboxes on .206. MX records point at smtp.irregulars.io (resolves to .206).
  • Outbound from user clients routes through Brevo SMTP (clean reputation while PTR is pending for direct delivery).
  • Outbound from apps (TeamCo meeting reminders, event-service guest invites) goes via Cloudflare Workers send_email binding from <service>@mail.irregulars.io. See apps/teamco/docs/EMAIL_SETUP.md for the per-worker setup.
  • SOGo webmail at https://mail.irregulars.io — TLS-protected via Cloudflare Tunnel.
  • Stalwart admin at https://mail-admin.irregulars.io.
  • TLS via Stalwart’s built-in ACME (Let’s Encrypt) for smtp/imap.irregulars.io.
  • DKIM with selector mail (mail._domainkey.irregulars.io) + inherited Brevo selectors during the transitional period.
  • SPF v=spf1 ip4:46.110.152.206 include:_spf.brevo.com include:_spf.mx.cloudflare.net ~all — covers direct outbound, Brevo relay, and CF Email Service paths simultaneously.
  • DMARC v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@irregulars.io
  1. PTR (reverse DNS) for 46.110.152.206 — must be mail.irregulars.io. Open a ticket with Metronet business support. Without this, direct outbound from .206 will be spam-foldered by Gmail/Microsoft. Keep Brevo as the outbound relay until PTR is confirmed.
  2. Cut over from Brevo to direct outbound — remove [queue.route.relay] and [queue.route.mx] config from Stalwart once PTR is in place.
  3. Sieve forwarding rule for sac@irregulars.ioissac@alfaren.xyz to replace the now-removed Cloudflare Email Routing rule. Set via SOGo’s Filters UI or the Stalwart admin sieve API.
  4. SSO integration for mail clients — currently runs on Stalwart’s internal directory with separate passwords from Authentik. See “SSO integration patterns” below for the upgrade path to 0.16 App Passwords (recommended) or migration to Mailcow.

The community’s hard requirement is that mail accounts integrate with Authentik SSO. There are three patterns, with different trade-offs:

Section titled “Pattern A — App Passwords (Stalwart 0.16.5+, recommended)”

Architecture:

  • Authentik is the source of truth for user identity.
  • Stalwart’s primary directory is oidc (Authentik). Users are auto- provisioned on first login.
  • Webmail (SOGo, Stalwart’s built-in WebUI) authenticates via OIDC → OAUTHBEARER tokens → Stalwart’s userinfo endpoint validates against Authentik. SSO experience preserved.
  • For mail clients that don’t speak OAuth (Apple Mail, Outlook, Thunderbird, mobile): each user generates an App Password in Stalwart’s WebUI. The app password is unique to that client and can be scoped/revoked independently.

Why this is the modern best practice:

  • It’s what Google, Microsoft, and Apple all do for legacy mail clients.
  • Compromised app password ≠ compromised SSO identity (only that one client breaks).
  • App passwords can be revoked individually, with expiration dates and IP restrictions (Stalwart 0.16.5+).
  • Webmail still gets MFA + SSO benefits via the OIDC path.

Requirements:

  • Upgrade Stalwart 0.15.5 → 0.16 (we’re currently on 0.15.5).
  • Set storage.directory = "authentik" (already configured).
  • In Authentik, configure the OAuth2 Provider’s access_token_validity to ≥ 1h (currently set to hours=1 per stalwart-mail provider, pk=21).
  • Users log into the Stalwart WebUI once via Authentik OIDC, then generate app passwords for each mail client.

Trade-off: Users have to set up app passwords. One-time effort per client per user.

Pattern B — Mailcow with native Authentik IdP (2025-03+)

Section titled “Pattern B — Mailcow with native Authentik IdP (2025-03+)”

Architecture:

  • Mailcow with IdP feature enabled (System → Configuration → Access → Identity Provider).
  • Configure Authentik as the IdP. Mailcow auto-creates mailboxes on first OIDC login.
  • Users authenticate to Mailcow’s SOGo/Roundcube via Authentik.
  • For mail clients, users must create App Passwords (Mailcow’s built-in feature) — Mailcow forces this because Dovecot doesn’t natively speak OIDC for PLAIN auth.

Why this is interesting:

  • Mailcow has had native Authentik integration shipped since 2025-03. No custom config needed — just point it at Authentik’s well-known endpoint.
  • Bundled Rspamd (best-in-class spam filtering), ClamAV, SOGo, Roundcube, Dovecot, Postfix in one Docker Compose — opinionated and fully integrated.
  • Web UI for everything (no CLI/config-file editing).

Trade-off:

  • Migration cost from Stalwart: 1-2 days of careful work moving mailboxes, Sieve scripts, DKIM keys, ACME, and clients.
  • More resource-heavy (~2 GB RAM vs Stalwart’s ~150 MB).
  • Mailcow’s native IdP is functional but less polished than Mailcow’s own auth UI (Authentik branding shows up).

Pattern C — LDAP with Authentik-as-LDAP-outpost

Section titled “Pattern C — LDAP with Authentik-as-LDAP-outpost”

Architecture:

  • Authentik runs a built-in LDAP outpost (already in place at ldap.irregularchat.com).
  • Mailcow / Mailu / Stalwart authenticates against LDAP.
  • Users authenticate with their Authentik password directly (no OAuth flow needed for SMTP/IMAP).

Why this is interesting:

  • Apple Mail, Outlook, Thunderbird all “just work” with LDAP-backed mail servers using PLAIN/LOGIN.
  • Single password (Authentik) across SSO and mail.
  • No app-password management UX needed.

Trade-off:

  • Mail server has direct access to user passwords during auth (vs OAuth where it only sees tokens). Mitigated by TLS and short-lived password cache.
  • LDAP doesn’t enforce MFA. Combined with SSO MFA at Authentik, this is a security gap: mail clients with valid Authentik password can bypass the MFA prompt that webmail requires.
  • App passwords (Pattern A) solve this MFA bypass problem cleanly.

Recommendation: Pattern A (App Passwords on Stalwart 0.16.5) for our specific environment. It preserves SSO for webmail, fixes the legacy- client compatibility problem, and integrates MFA correctly. Pattern B (Mailcow) is the right call if we ever outgrow Stalwart’s web UI limitations or want bundled antivirus.

Reality check (updated 2026-05-17): Two upgrade attempts to Stalwart 0.16 in 24 hours, both blocked:

  1. In-place TOML→JSON migration (2026-05-16): Stalwart 0.16 requires fresh-install + STALWART_RECOVERY_ADMIN env-var bootstrap; old TOML config is ignored, stalwart-cli binary is gone, [authentication.fallback-admin] no longer honored. Reverted.

  2. Parallel deploy on .205 with proper bootstrap (2026-05-17): Got the entire stack standing — listeners, ACME-issued LE cert, OIDC directory pointing at Authentik, DKIM keys auto-generated, domain configured. Wizard x:Bootstrap/set reports “Setup complete” with admin credentials but never persists the admin to the RocksDB. x:Account/query returns empty after wizard. Matches upstream issue #3025. Recovery admin (env var) works but isn’t a real principal — can’t create users with credentials. Hard block.

What we shipped instead (2026-05-17, working): Pattern A’ — a custom SASL auth proxy in front of Stalwart 0.15.5. Mail clients connect to mail.irregulars.io:1993 (IMAPS) or :1587 (SMTP STARTTLS) with PLAIN auth + a per-device app password. The proxy bcrypts the password locally, then connects upstream to Stalwart on :993 / :587 using OAUTHBEARER with a per-user Authentik OIDC access token (refreshed on each connection from a stored refresh token). SOGo’s existing OIDC integration is untouched.

Per-device enrollment is a one-time browser OAuth flow: python3 add-mail-creds.py <user> --label <device> → user signs in to Authentik once → app password printed, ready to paste into Apple Mail. Revoke via python3 revoke-mail-creds.py <user> --label <device>.

Full operator guide: selfhost/stalwart/sasl-proxy/README.md. Migration writeup: selfhost/stalwart/MIGRATION-TO-PUBLIC-IP-2026-05-16.md. v0.16 retry plan (for when upstream ships a fix): selfhost/stalwart/PLAN-v016-fresh-install.md.

Modern self-hosted mail server comparison (2026)

Section titled “Modern self-hosted mail server comparison (2026)”
ProjectLanguageStackResourceOIDC/SSOSpamNotes
StalwartRustAll-in-one (SMTP, IMAP, JMAP, Sieve, ManageSieve, CalDAV, CardDAV, WebDAV)~150 MB RAM✅ Native OIDC backend, App Passwords in 0.16+Built-in Bayesian + heuristicsWhat we run today. Best for resource-constrained or modern-protocol-focused deploys.
MailcowPHP + many containersPostfix + Dovecot + Rspamd + ClamAV + SOGo + Roundcube + Solr~2 GB RAM✅ Native Authentik/OIDC since 2025-03Rspamd (best available)Most feature-rich, most production-tested. Best for full-featured deployments.
MailuPython + many containersPostfix + Dovecot + Rspamd + Roundcube~1 GB RAMOIDC via reverse proxy or LDAPRspamdMature, similar to Mailcow but more modular.
MoxGoAll-in-one (SMTP, IMAP, JMAP)~80 MB RAMOAUTH2 in roadmap (not yet stable)Built-in junk filterNewest entrant, modern protocol stack (JMAP, MTA-STS, DANE), low-maintenance design philosophy. Not yet recommended for production — webmail is “early stages”.
docker-mailserverBash + Postfix/DovecotPostfix + Dovecot + Rspamd + Postscreen~500 MB RAMLDAP onlyRspamdLower-level, ops-team-friendly. No web UI bundled.
iRedMailMixedPostfix + Dovecot + Rspamd + Roundcube~1.5 GB RAMLDAP, OIDC via proxyRspamdOlder but well-maintained. Free tier vs paid edition.
Poste.ioMixedHaraka + Dovecot + Rspamd~500 MB RAMLDAP onlyRspamdWas previously recommended in this wiki. Less active development than Mailcow.
MaddyGoSMTP + IMAP (LMTP)~50 MB RAMLDAPCustomLightweight, “composable” design. Smaller userbase.
  • Pick Stalwart if: you want modern protocols (JMAP, CalDAV), low resource use, a single binary that handles everything including ACME, and you value a Rust-native security posture. App Passwords in 0.16 make it fully production-viable for mixed SSO/legacy-client environments.
  • Pick Mailcow if: you want maximum compatibility with the larger ecosystem (Rspamd integrations, ClamAV updates, SOGo’s mature ActiveSync/CalDAV), and you have RAM to spare. The native Authentik IdP integration in 2025-03+ is genuinely well-done.
  • Pick Mailu if: you want Mailcow-like features but more modular Docker Compose layout, or if you specifically need their granular RBAC.
  • Pick Mox if: you’re greenfield and want the most modern stack — but expect rough edges on webmail and OIDC.
  • Pick docker-mailserver / Poste.io / iRedMail if: you’re already comfortable with that platform. None of these have a strong case over the top three in 2026.

Cloudflare has TWO email-related products now (post-2026 changes):

Inbound MX service. You point your domain’s MX records at route1/2/3.mx.cloudflare.net, configure rules in the dashboard, and CF forwards mail to other addresses. Use cases:

  • Forward info@yourdomain.com to a personal Gmail account.
  • Catch-all to a single inbox.
  • Drop spam at the edge before it hits your server.

What it can’t do: Deliver to your own SMTP server. There is no “deliver to SMTP” rule type. If you want self-hosted mailboxes, you have to bypass CF Email Routing on the MX layer.

We disabled it for irregulars.io on 2026-05-16 because we needed test@irregulars.io and other addresses to land in Stalwart mailboxes.

Email Service / Email Sending (outbound, public beta in 2026)

Section titled “Email Service / Email Sending (outbound, public beta in 2026)”

Cloudflare Workers can now send outbound mail via the send_email binding. You add [[send_email]] name = "EMAIL" to wrangler.toml, onboard mail.<your-domain> in the dashboard, and env.EMAIL.send(message) delivers from CF’s infrastructure.

Use cases:

  • Transactional mail from serverless apps (password resets, invites, notifications) without standing up SMTP infrastructure.
  • Multi-app shared sender domain (we use mail.irregulars.io for TeamCo, future signal-bot, etc. — see apps/teamco/docs/EMAIL_SETUP.md for the runbook).

Pricing: Workers Paid plan ($5/month) required. CF charges per message but is finalizing the model as of mid-2026.

What it can’t do: Host user mailboxes. There’s no IMAP for received mail. CF Email Service is a one-way SEND-only service.

Decision matrix — which Cloudflare option for which use case

Section titled “Decision matrix — which Cloudflare option for which use case”
Use caseCloudflare serviceOr self-host with
Forward info@yourdomain.com → GmailEmail RoutingSelf-host with a forwarding rule
Send password-reset emails from WorkersEmail Service send_email bindingNone — this is CF’s killer use case
User has a you@yourdomain.com mailbox they log intoNot supported by CFSelf-host (Stalwart, Mailcow, Mailu…)
Catch-all spam drop at the edgeEmail Routingrspamd at the mail server (more flexible)
Marketing newsletter sendsNot great fit — use Resend/Postmark/Brevo
Cron-driven transactional sendsEmail ServiceStalwart’s queue or self-managed Postfix relay

Architecture diagram (current irregulars.io, 2026-05-16)

Section titled “Architecture diagram (current irregulars.io, 2026-05-16)”
Internet
┌──────────────────────────────┼──────────────────────────────┐
│ │ │
Inbound MX User clients (Apple Cloudflare
│ Mail, mobile, etc.) Workers
│ │ │
│ smtp.irregulars.io │ smtp/imap.irregulars.io │ send_email
│ (MX 10) │ │ binding
▼ ▼ ▼
46.110.152.206:25 46.110.152.206:465/587/993 CF Email Service
(Stalwart MX listener) (Stalwart SMTP/IMAP) (delivers from CF
│ │ edge servers)
│ │ │
▼ ▼ ▼
Mailbox routing Auth chain External recipients
│ (currently: (Gmail, etc.)
│ internal directory,
│ PLAIN/LOGIN)
▼ │
Stalwart mailbox │
(sac@, test@, etc.) │
│ │
▼ ▼
SOGo webmail Outbound queue
(mail.irregulars.io) │
(PTR pending)
Brevo SMTP
(smtp-relay.brevo.com:587)
Internet (recipient MX)

The path (revised 2026-05-17 after the SASL proxy ship):

  1. Get PTR set for 46.110.152.206mail.irregulars.io. Metronet business support ticket. Until done, keep Brevo.

  2. Switch outbound to direct — remove [queue.route.relay] and [queue.route.mx] from Stalwart config. Stalwart does direct MX delivery from .206.

  3. Watch upstream Stalwart for a 0.16.6+ release that fixes x:Bootstrap/set (#3025). The infrastructure to retry is already provisioned: IP .205, DNS reusable, ACME cert valid, DOCKER-USER firewall rules in place, Authentik provider has the signing key + offline_access scope. A 0.16.6 retry should be a 30-minute project.

  4. When 0.16 works: migrate users from saslproxy → native Stalwart App Passwords. Each user rotates their app password in Stalwart’s WebUI once, then revokes the saslproxy entry. Tear down /datadrive/home/saslproxy/ and delete selfhost/stalwart/sasl-proxy/.

  5. Optionally: replace Brevo entirely for app sends by migrating TeamCo / event-service to direct SMTP at mail.irregulars.io:587 (using app passwords for the service accounts). CF Email Service remains an option but having a single mail server reduces moving parts.

Current working stack (2026-05-17, no migration needed):

Mail clientPortAuth
SOGo webmailn/a (browser)Authentik OIDC SSO
Apple Mail / iOS / Outlook1993 (IMAPS) + 1587 (STARTTLS)App password via add-mail-creds.py
App transactional (TeamCo etc)Cloudflare Workers send_email bindingn/a

From Stalwart → Mailcow (if needed later)

Section titled “From Stalwart → Mailcow (if needed later)”

If we decide Stalwart isn’t the right fit in 12 months:

  1. Stand up Mailcow on a separate host or proxmox VM.
  2. Configure Mailcow’s native Authentik IdP (System → Config → Access).
  3. Migrate mailboxes via imapsync per user.
  4. Replicate DKIM keys (or generate new ones).
  5. Update DNS MX to point at Mailcow’s hostname.
  6. Decommission Stalwart.

Don’t do this preemptively — Stalwart 0.16.5 + App Passwords solves the core SSO + legacy-client problem. Mailcow’s advantage is Rspamd + the extended feature set (ActiveSync polish, ClamAV updates), not SSO.

Server-level auto-provisioning from Authentik (optional pattern)

Section titled “Server-level auto-provisioning from Authentik (optional pattern)”

Independent of which mail server you pick, you can build out “create user in Authentik → mailbox appears on the mail server” auto-provisioning via:

Option 1 — Authentik SCIM provider → mail server SCIM endpoint

Section titled “Option 1 — Authentik SCIM provider → mail server SCIM endpoint”

Authentik 2024+ supports SCIM 2.0 as an outbound provider. If the mail server has an SCIM endpoint:

  • Stalwart: not yet (request open at github.com/stalwartlabs/mail-server/discussions). Workaround: use Authentik’s OIDC backend and let first-login auto-provision the mailbox.
  • Mailcow: not native, third-party scripts exist.
  • Authentik → custom Webhook → mail server’s admin API: this is the current best-effort pattern.

Option 2 — Authentik LDAP outpost → mail server’s LDAP integration

Section titled “Option 2 — Authentik LDAP outpost → mail server’s LDAP integration”

Already running an LDAP outpost? Most mail servers (Mailcow, Mailu, Postfix-direct) support LDAP user backends. Users in Authentik appear in the mail server. No webhook needed.

Trade-off discussed in “Pattern C” above: LDAP doesn’t enforce MFA, so mail-client auth bypasses the MFA prompt webmail users get.

Option 3 — Auth-time auto-provision (what Stalwart and Mailcow do today)

Section titled “Option 3 — Auth-time auto-provision (what Stalwart and Mailcow do today)”

User logs in via OIDC for the first time. The mail server’s OIDC directory creates a new principal on the fly using the email/username from the IdP. No SCIM, no LDAP, no webhook. Simplest and what we already use.

Recommended: Option 3 (auto-provision on first OIDC login). Reserve SCIM/LDAP for environments with hundreds of users where pre-provisioning matters.

  • Email Hardening Guide — SPF / DKIM / DMARC tuning, phishing defense, end-user practices.
  • Cloudflare — DNS + Tunnel + Workers basics.
  • Authentik Installation — the SSO/IdP this all integrates with.
  • Self-Host Cloudflare Tunnels — used to publish mail.irregulars.io webmail without exposing the origin IP.
  • apps/teamco/docs/EMAIL_SETUP.md (monorepo) — runbook for app-transactional email via Cloudflare Workers send_email.