Skip to content

DNS (SPF, DKIM, DMARC)

Posthorn relays mail through your configured transport (Postmark, Resend, Mailgun, AWS SES, or an outbound-SMTP relay). The transport sends from your domain (the From: you configure). For that mail to reach the recipient’s inbox — and not their spam folder — three DNS records have to be set up correctly on your sending domain.

The DNS records below use Postmark’s setup as the worked example. Resend, Mailgun, and SES walk you through the same shape via their own dashboards — different host values, same three record types. Outbound-SMTP relays depend on the upstream provider’s setup.

This is not Posthorn-specific. Every transactional email service has the same prerequisites. This page documents them so you don’t get blindsided after going live.

RecordPurposeRequired by
SPF — Sender Policy FrameworkTells receiving servers which IPs are allowed to send mail “from” your domainAll major providers (Gmail, Outlook, Apple)
DKIM — DomainKeys Identified MailCryptographic signature proving the message wasn’t modified in transit and was authorized by your domainAll major providers
DMARC — Domain-based Message Authentication, Reporting & ConformancePolicy combining SPF + DKIM results; tells receivers what to do with messages that fail one or bothGmail required as of Feb 2024 for bulk senders; recommended for all

Without these, your mail will be flagged, junked, or rejected outright. Postmark itself doesn’t deliver mail until SPF and DKIM are verified for your sending domain.

Setup walkthrough (Postmark + DNS provider)

Section titled “Setup walkthrough (Postmark + DNS provider)”

Postmark’s dashboard walks you through this when you add a sender domain. The high-level shape:

Postmark dashboard → Sender Signatures → Add Domain. Enter example.com. Postmark generates the values you need to set in DNS.

Postmark provides:

Type: TXT
Host: @ (or example.com — depends on DNS provider)
Value: v=spf1 a mx include:spf.mtasv.net ~all

If you already have an SPF record (because you also send via Gmail Workspace, Mailgun, etc.), merge the includes rather than creating a second TXT record. Multiple SPF records is an error condition. The merged form looks like:

v=spf1 a mx include:_spf.google.com include:spf.mtasv.net ~all

Postmark provides a public key (long TXT value). Set:

Type: TXT
Host: 20240101pm._domainkey (Postmark provides the selector — varies)
Value: k=rsa; p=MIGfMA0GCSq... (long key, Postmark provides full value)

The selector (20240101pm above) is Postmark’s; just paste what they give you.

Type: TXT
Host: _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; pct=100; aspf=s; adkim=s

Start with p=none if you want to monitor without enforcement:

v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com

Move to p=quarantine after a week of monitoring (mail that fails goes to spam). Move to p=reject later (mail that fails is dropped — strict). Don’t jump straight to p=reject; you’ll lose mail until you’ve verified all your legitimate sources are SPF/DKIM-aligned.

Back in the Postmark dashboard, hit “Verify” on the domain. SPF and DKIM should both flip to verified within minutes of the DNS records propagating. DMARC isn’t directly verified by Postmark (it’s a policy receivers consult, not a record Postmark sends from).

From Posthorn:

Terminal window
curl -X POST http://localhost:8080/api/contact \
-d "name=Test" -d "email=you@gmail.com" -d "message=hello"

Send to a Gmail address you control. Check:

  1. The mail arrives (inbox, not spam, not “Promotions”).
  2. Open the message → “Show original” (Gmail’s term).
  3. The Authentication-Results: header should show spf=pass, dkim=pass, dmarc=pass.
MistakeWhat happens
Two SPF records on the same domainReceiving servers treat this as a permerror → SPF fails → mail goes to spam
SPF too long (>10 DNS lookups)Permerror — split using mx directives or remove unused includes
Missing DKIM selectorDKIM fails — mail signed but receiver can’t verify the signature
Misspelled _domainkey host (e.g., domainkey)DKIM fails — receivers expect the underscore
DMARC p=reject before SPF/DKIM are verifiedLegitimate mail gets dropped silently
Setting up DNS but not waiting for propagationSome receivers cache for hours — wait at least 1 hour before declaring failure

For most operators, the following is a sensible end state:

example.com. TXT "v=spf1 a mx include:spf.mtasv.net ~all"
20240101pm._domainkey.example.com. TXT "k=rsa; p=MIGfMA0..."
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com"

This passes Gmail’s bulk-sender requirements, passes Outlook’s strictness checks, and gives you DMARC reports so you can spot abuse.

If your From: is noreply@example.com, the records go on example.com (apex). If your From: is noreply@mail.example.com, the records go on mail.example.com (subdomain).

Subdomain sending is often a good idea — keeps your transactional mail isolated from your main domain’s reputation. A spam blip on your contact form won’t affect deliverability for you@example.com.

ConcernPosthorn?
Set up DNS for youNo. Pre-deployment manual step.
Verify DNS before sendingNo. A posthorn dns check subcommand is a planned future addition; not yet on a specific milestone.
Sign DKIM itselfNo. Postmark does this. Posthorn is just the HTTP client.
Re-send mail when deliverability fails post-hocNo. Deliverability is recipient-side — once Postmark says “accepted,” it’s out of Posthorn’s hands.