Skip to main content
Deliverability is decided in DNS before a single byte of mail moves. A receiving server looks up your domain, asks who is allowed to send for it, is this message intact and authentic, and can I reach you over enforced TLS — and answers those questions entirely from records you publish. This page is the operator’s DNS contract: what to publish for a mail domain, why each record exists, how OxiMail generates and serves it, and how to verify the result. Two commands frame the whole workflow:
  • oximail setup provisions the full record set. With --cloudflare-token it writes the records straight into the zone through the Cloudflare API (idempotent — an existing record is left in place). Without a token it tells you to create the records by hand.
  • oximail check-dns performs live lookups and scores the domain. It queries through Cloudflare (1.1.1.1 / 1.0.0.1) with DNSSEC validation on, deliberately bypassing stale local caches and split-horizon resolvers that mislead during setup. The report ends with n/n -- Ready for production or n/n -- Fix the items above.
Run oximail check-dns from the server after every DNS change. DNSSEC is enforced on the TXT lookups, so a zone with broken DNSSEC surfaces as a hard error rather than silently accepting forged records.

The record set

For a mail domain example.com served by the host mail.example.com at IP 203.0.113.10, the full set OxiMail provisions is: The sections below explain the records that carry real policy. The MX, A, and SRV records are mechanical — publish them and verify with check-dns.

SPF

OxiMail publishes a strict, single-source SPF record:
ip4:203.0.113.10 authorizes exactly your mail host; -all is a hard fail — any other IP claiming to send as example.com is rejected outright. This is the right default for a single self-hosted server. Adjust it by hand if your topology is wider:
  • Send from more than one IP (a backup MX host, a separate relay): add each ip4: / ip6: mechanism, e.g. v=spf1 ip4:203.0.113.10 ip4:203.0.113.20 -all.
  • Relay through a third party (a transactional provider): add their published include:, e.g. include:_spf.example-relay.com.
  • IPv6 senders: SPF only authorizes what it lists. If your MX also sends over IPv6, add the matching ip6: mechanism, otherwise IPv6 mail hard-fails SPF at the receiver.
check-dns evaluates SPF by direct IP presence and +all only; it does not recurse into include: or redirect=. If you rely on an include:, verify alignment with an external SPF validator as well.

DKIM

DKIM signs each outgoing message with a private key; receivers fetch the public key from DNS and verify the signature. OxiMail signs at delivery time in the outbound worker (see the SMTP layer). Generate the key and read back the record to publish:
This writes the private key to /etc/oximail/dkim/example.com.default.key and prints the matching DNS record:
  • Selector is default. The DNS name is always <selector>._domainkey.<domain>, so a rotation publishes a new selector alongside the old one, lets both verify during the cutover, then retires the old record.
  • Algorithm defaults to RSA (k=rsa). The full setup wizard publishes the RSA record automatically when run with a Cloudflare token.
  • Oversigning. The signature’s h= list names From, To, and Subject one extra time, so a downstream actor cannot inject a second copy of a display-critical header without invalidating the signature (RFC 6376 §8.15). No operator action needed; it is how OxiMail signs.
On the inbound side, dkim=fail says why. The verification reason now travels alongside the verdict, into the Authentication-Results header and into the result stored per message — so a post-mortem can separate cases a bare verdict conflated, and which call for opposite actions: The reason recorded is that of the first non-passing signature. A pass carries none, deliberately: a bad signature sitting next to a good one is normal — mailing lists re-sign — and dkim=pass reason=… would read as a defect where there is none.
When you add a mail domain after the initial setup, generate and publish its DKIM record explicitly. A domain whose key exists but whose default._domainkey TXT record is missing will sign mail that receivers then reject as dkim=fail (no key found). Run oximail check-dns for each domain to confirm the DKIM check passes.

DMARC

DMARC tells receivers what to do when a message fails both SPF and DKIM alignment, and where to send aggregate reports. OxiMail’s default is:
p=quarantine routes failing mail to the recipient’s Junk folder. Tune the policy to your rollout stage:
  • p=none — monitor only. Publish this first if you are unsure your SPF and DKIM are correct; read the rua aggregate reports, then tighten.
  • p=quarantine — the OxiMail default. Failing mail is junked, not refused.
  • p=reject — failing mail is refused at SMTP time. On the receiving side, OxiMail honours a sender’s p=reject with a 550 5.7.26 rejection (see the SMTP layer).
Make sure the rua mailbox exists and is monitored — it is where you find out a legitimate source is failing before recipients stop seeing your mail.

MTA-STS and TLS-RPT

MTA-STS (RFC 8461) lets you require that other servers use validated TLS when they deliver to you, closing the downgrade-to-cleartext window that plain opportunistic STARTTLS leaves open. It has three moving parts, and OxiMail serves the policy itself:
  1. The TXT record _mta-sts.example.com = v=STSv1; id=<YYYYMMDD> announces that a policy exists. The id changes whenever the policy changes.
  2. The policy host mta-sts.example.com (a CNAME to your mail host) is where senders fetch the policy over HTTPS.
  3. The server serves /.well-known/mta-sts.txt from the [mta_sts] configuration block:
  • enforce — senders that honour MTA-STS refuse to deliver to you over unvalidated TLS.
  • testing — policy is published but failures are reported, not enforced. Use this while validating, then switch to enforce.
  • none — withdraw the policy.
TLS-RPT (RFC 8460) is the companion that gives you visibility:
Receivers send daily reports of TLS connection failures to the rua address, which is how you detect a misconfigured policy or an active downgrade attempt. As with DMARC, monitor that mailbox.

DANE (TLSA)

DANE (RFC 7672) is the DNSSEC-anchored alternative to MTA-STS: a TLSA record under _25._tcp.mail.example.com pins your certificate so a sender validates TLS against DNS rather than a public CA list. It requires a DNSSEC-signed zone. OxiMail enforces DANE on the outbound path by default and keeps your inbound TLSA records fresh automatically after each certificate renewal. Because a renewed certificate changes the pinned value, this requires a configured DNS provider:
When dane_enabled = true (the default), [dns] provider must be set or the server refuses to start (fail-loud, ADR-027). Without a provider, an ACME renewal would silently leave your TLSA records stale and break inbound mail for every DANE-validating sender.
The provider options:
  • cloudflare — auto-publishes the new TLSA records via the Cloudflare API after each ACME renewal. Recommended.
  • manual — you update TLSA records out of band. The renewal logs a warning reminding you to do it.
  • rfc2136 — a v1 stub that currently behaves like manual (logs the same warning); the dynamic-update implementation is deferred.
See TLS & ACME for certificate provisioning and renewal.

Reverse DNS (PTR) and deliverability

Reverse DNS is published by whoever controls the IP block (your hosting provider — Hetzner, OVH, AWS, …), not in your domain’s zone. It is one of the strongest reputation signals a receiver has.
  • Set the PTR for 203.0.113.10 to mail.example.com — the same name your server uses in its SMTP HELO/EHLO. Many large receivers reject or heavily penalize a mismatch (no FCrDNS).
  • check-dns passes the PTR check when the record resolves to the mail domain or a subdomain of it; for best results make it resolve exactly to your mail host and confirm the forward A record points back to the same IP.
  • New IPs have no sending reputation. Warm up gradually and keep volume steady; a sudden spike from a cold IP looks like a compromised host.

Autodiscovery

Mail clients find your endpoints from the SRV records and the autoconfig / autodiscover CNAMEs in the table above. The full oximail setup run also publishes a CAA record restricting certificate issuance to Let’s Encrypt, which both hardens the domain and matches OxiMail’s ACME setup. None of these carry tunable policy — publish them and let check-dns confirm.

Verifying with oximail check-dns

The report runs ten live checks — MX, rDNS (PTR), SPF, DKIM, DMARC, TLS-RPT, and the four SRV records — and prints a pass/fail line for each:
The MTA-STS policy and DANE TLSA records are enforced by the server at send time and served from the policy host rather than re-checked here; validate those with an external MTA-STS / DANE checker after publishing. Common failures:
  • PTR mismatch — fix the reverse record at your hosting provider, not in your zone.
  • DKIM “no record” — you generated the key but did not publish the TXT record. See the DKIM section above.
  • SPF “does not include IP” — your sending IP is not listed; add the ip4: / ip6: mechanism.
  • Lookup failed — a propagation delay (wait and re-run) or a broken DNSSEC zone (the lookup fails hard rather than trusting an unvalidated answer).

Standards

  • RFC 7208 — Sender Policy Framework (SPF).
  • RFC 6376 — DomainKeys Identified Mail (DKIM).
  • RFC 7489 — Domain-based Message Authentication, Reporting, and Conformance (DMARC).
  • RFC 8461 — SMTP MTA Strict Transport Security (MTA-STS).
  • RFC 8460 — SMTP TLS Reporting (TLS-RPT).
  • RFC 7672 — SMTP Security via Opportunistic DANE TLS.
For how these records are consumed and enforced on the wire, see the SMTP layer; for certificate provisioning, see TLS & ACME; for the spam pipeline that authentication feeds, see anti-spam.