Skip to main content
One certificate serves every TLS surface of the instance: HTTPS/JMAP (443), submission STARTTLS (587), SMTPS (465), and IMAPS (993). Who obtains it depends on the deployment topology chosen at first boot.

Direct topology: built-in ACME

When OxiMail owns ports 80/443, it provisions and renews its own certificate from Let’s Encrypt:
The first certificate is obtained at first start (the wizard polls up to 90 seconds for it); renewals happen automatically before expiry. Port 80 must stay reachable from the internet for the HTTP-01 challenge. The wizard also publishes a CAA record restricting issuance to Let’s Encrypt — a hardening default that matches this setup. Renewal never restarts anything. The new certificate is swapped in atomically (ArcSwap, ADR-016): in-flight connections finish on the old one, new handshakes get the new one, on every listener at once — including SMTP and IMAP.

Reverse-proxy topology

Behind Caddy or Nginx, the proxy owns 80/443 and its own certificates, and acme_enabled = false (the wizard writes this for you). Two things still matter:
  • SMTP (25/587/465) and IMAPS (993) are not proxied — OxiMail terminates TLS on those itself. It needs a certificate for them: point [tls] at the proxy’s certificate files or run a deploy hook that copies them, and remember OxiMail hot-reloads the files when they change.
  • The proxy must forward everything except the ACME challenge path, with X-Forwarded-For from a [server] trusted_proxies address — see first boot for the generated snippet and the CORS rule.

The DANE coupling

If you publish DANE TLSA records (email authentication), a certificate renewal changes the pinned value — stale TLSA records break inbound mail from every DANE-validating sender. This is why dane_enabled = true (the default) requires a configured [dns] provider: after each renewal, OxiMail republishes the TLSA records itself (Cloudflare API), or logs a loud reminder in manual mode. A server with DANE on and no DNS provider refuses to start rather than setting you up for that silent breakage.

Post-quantum key exchange

Every TLS surface OxiMail terminates itself — 25, 465, 587, 993, and 443 when no frontend sits in front of it — offers the hybrid group X25519MLKEM768 first, with classic X25519 as the fallback. Nothing is broken by this: a peer that does not know the hybrid group negotiates X25519 exactly as it did before. There is no setting to turn it on; it is the shipped posture. The threat it addresses is “harvest now, decrypt later” — an adversary recording today’s traffic to decrypt it when the machine to do so exists. Being late to this is not recoverable after the fact, which is why it is a default rather than an option. It is not theoretical for mail either: Gmail already offers the hybrid group on 25, 465, 587 and 993, so outbound mail to it negotiates post-quantum today. Verify rather than assume. This exact property was silently false here for months: the feature was compiled in while the installed crypto provider had no implementation for it, so every session fell back to classic X25519 while the configuration read as if it did not. A fallback of this kind is invisible — no error, no log line, nothing failing. With OpenSSL 3.5 or later:
Run it against each port you serve. A frontend in front of 443 answers for itself, not for OxiMail — check the mail ports separately. Outbound connections use the same stack. The HTTP client behind webhooks, push and link unfurling runs on the same TLS implementation as the server, so those fetches gained the hybrid group too. It trusts the operating system’s certificate store rather than a bundled root list, deliberately: an endpoint whose certificate chains to a private CA installed on the box keeps working.

Inspecting and troubleshooting

Common failures:
  • Port 80 unreachable — the HTTP-01 challenge cannot complete. Open it in the firewall/security group; it only ever serves challenges and redirects.
  • Rate-limited by Let’s Encrypt — repeated failed issuance attempts (usually a DNS mistake) can hit LE’s per-domain limits; fix the cause before retrying in a loop.
  • DANE senders bouncing after a renewal — the TLSA records were not updated. Check the [dns] provider configuration and the renewal logs.
Certificates and ACME account material live under /etc/oximail/tls/, which is part of the standard backup surface.