
Email deliverability often fails without drama. Your app still “sends,” your logs look normal, and customers never receive password resets or receipts.
This guide shows you how to set up DKIM SPF DMARC for a domain used for transactional email. You’ll publish the right DNS records and confirm alignment. That way, mailbox providers stop treating your messages as suspicious.
The workflow is the same whether you send from a VPS running Postfix/Exim, a WordPress site using SMTP, or an app using a relay.
First, identify the real sender. Then publish DNS that matches it. Finally, validate using headers and DMARC reports.
What you’ll build (and what you need ready)
- SPF record that authorizes your sending sources
- DKIM signing key (public key in DNS, private key on your mail system/provider)
- DMARC policy that enforces alignment and gives you reporting
Prerequisites:
- Access to your domain’s DNS zone (at your registrar/DNS host). If you need the DNS side first, see DNS cutover tutorial for practical record editing and TTL strategy.
- You know where your transactional email is sent from (your VPS MTA, cPanel/WHM server, or a third-party SMTP relay).
- A mailbox you can receive test emails at (Gmail, Outlook, Fastmail, or your own). You’ll also need one “reporting” address like
dmarc@yourdomain.com.
If you run your own mail server (Postfix or Exim) on a VPS or dedicated server, you also need rDNS and a correct HELO hostname.
These are separate deliverability basics. They still matter.
Two good companions:
Step 1: Identify your sending sources (don’t guess)
SPF and DMARC only help when you authorize the systems that actually send mail.
Start by listing every service that can send transactional email for this domain. Include “temporary” tools you may have forgotten.
Common patterns:
- App server sends directly from your VPS/dedicated server IP (Postfix/Exim).
- App server relays through an SMTP provider (recommended for many teams).
- WordPress sends via SMTP plugin to a relay or to your server MTA.
- cPanel/WHM server sends from Exim on the hosting server.
Quick way to confirm: send yourself a message and inspect headers.
- Send a test email from your application (password reset, test notification, etc.).
- In the receiving mailbox, view “Original” / “Message source”.
- Find the earliest
Received:line and note the public IP and hostname.
If your app runs on a VPS and you’re still choosing infrastructure, you’ll get the cleanest control with a HostMyCode VPS.
Use a dedicated server when you need stable outbound reputation and higher throughput.
Step 2: Publish an SPF record that matches reality
SPF is a DNS TXT record at your root (apex) domain. It tells receivers which systems can send mail for your domain in the envelope sender (Return-Path).
DMARC later checks SPF alignment. “Close enough” often becomes “fails in production.” Keep identities consistent.
2.1 Decide which SPF “mechanisms” you need
- Single sending IP (your VPS): use
ip4(andip6if applicable). - Third-party relay: they usually provide an
include:domain. - Google Workspace / Microsoft 365: use their published SPF include (from their docs).
Hard rule: You should have one SPF record per domain.
Multiple SPF TXT records often trigger “permerror” and break authentication.
2.2 Create the SPF TXT record
In your DNS manager, add:
- Type: TXT
- Name/Host:
@(or leave blank, depending on your DNS UI) - Value: one of the examples below
- TTL: 300 seconds while you test, then 3600+
Example A: only your VPS IP sends mail
v=spf1 ip4:203.0.113.10 -all
Example B: your VPS + an SMTP relay include
v=spf1 ip4:203.0.113.10 include:spf.your-smtp-provider.example -all
Example C: “soft fail” while migrating (use briefly; it’s weaker than -all)
v=spf1 ip4:203.0.113.10 include:spf.your-smtp-provider.example ~all
2.3 Verify SPF from a terminal
From any Linux machine (your laptop or server):
dig +short TXT yourdomain.com
You should see your SPF string returned.
If you see two SPF-like strings, fix that before you touch DKIM or DMARC.
Common SPF mistakes:
- Publishing SPF on
mail.yourdomain.cominstead ofyourdomain.com. - Using
ptrmechanism (discouraged, inconsistent). - Exceeding the SPF DNS lookup limit (10). Too many
includechains can break evaluation.
Step 3: Generate and publish DKIM keys (the part spam filters actually trust)
DKIM adds a cryptographic signature to each outgoing message. Receivers fetch the public key from DNS. They then verify the signature against what your server signed.
For transactional mail, DKIM is often the difference between “accepted” and “treated like a spoof.”
You have two routes:
- You send mail from your own server (Postfix/OpenDKIM or Exim DKIM): generate your own DKIM key.
- You send via a provider: they generate DKIM for you; you only publish the DNS record they provide.
3.1 Choose a DKIM selector name
A selector is the label in DNS that lets you rotate keys without breaking mail.
Pick something you’ll recognize later, such as:
tx2026for transactional maildefaultif your provider requires it
Your DKIM DNS name becomes:
<selector>._domainkey.yourdomain.com
3.2 If you run your own Postfix: generate a 2048-bit DKIM key
On Ubuntu/Debian:
sudo apt update
sudo apt install -y opendkim opendkim-tools
Create a key directory and generate the keypair. In 2026, 2048-bit is the expected baseline:
sudo mkdir -p /etc/opendkim/keys/yourdomain.com
cd /etc/opendkim/keys/yourdomain.com
sudo opendkim-genkey -b 2048 -s tx2026 -d yourdomain.com
sudo chown -R opendkim:opendkim /etc/opendkim/keys/yourdomain.com
sudo chmod 0700 /etc/opendkim/keys/yourdomain.com
sudo chmod 0600 /etc/opendkim/keys/yourdomain.com/tx2026.private
This creates:
tx2026.private(keep secret)tx2026.txt(contains the DNS TXT record value)
Print the DNS record content:
sudo cat /etc/opendkim/keys/yourdomain.com/tx2026.txt
You’ll see something like:
tx2026._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..." )
In your DNS UI, add a TXT record with:
- Type: TXT
- Name/Host:
tx2026._domainkey - Value: the
v=DKIM1; k=rsa; p=...part (no parentheses; most DNS panels don’t want quotes)
3.3 Configure OpenDKIM for Postfix (minimal working config)
Edit /etc/opendkim.conf.
Confirm these lines exist, and adjust paths/domain as needed:
Syslog yes
UMask 002
Canonicalization relaxed/simple
Mode sv
SubDomains no
AutoRestart yes
AutoRestartRate 10/1h
Background yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
KeyTable /etc/opendkim/key.table
SigningTable /etc/opendkim/signing.table
ExternalIgnoreList /etc/opendkim/trusted.hosts
InternalHosts /etc/opendkim/trusted.hosts
Socket local:/run/opendkim/opendkim.sock
Create the tables:
sudo tee /etc/opendkim/key.table >/dev/null <<'EOF'
tx2026._domainkey.yourdomain.com yourdomain.com:tx2026:/etc/opendkim/keys/yourdomain.com/tx2026.private
EOF
sudo tee /etc/opendkim/signing.table >/dev/null <<'EOF'
*@yourdomain.com tx2026._domainkey.yourdomain.com
EOF
sudo tee /etc/opendkim/trusted.hosts >/dev/null <<'EOF'
127.0.0.1
localhost
::1
# Your server public IP (optional)
# 203.0.113.10
EOF
Hook OpenDKIM into Postfix. Edit /etc/postfix/main.cf and add:
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/run/opendkim/opendkim.sock
Restart services:
sudo systemctl enable --now opendkim
sudo systemctl restart opendkim
sudo systemctl restart postfix
Verify the socket exists:
sudo ls -l /run/opendkim/opendkim.sock
Verify DKIM DNS lookup from your server:
dig +short TXT tx2026._domainkey.yourdomain.com
If you send through cPanel/WHM instead of raw Postfix, don’t build OpenDKIM manually.
Use WHM’s DKIM management, then validate.
HostMyCode also has a focused guide for WHM domains here: WHM email deliverability tutorial.
Step 4: Create a DMARC record that enforces alignment (safely)
DMARC tells receivers what to do when SPF and/or DKIM fail. It also gives you visibility through reports.
For transactional mail, the goal is simple: SPF or DKIM must pass and align with the visible From: domain.
4.1 Start with monitoring (p=none), then move to enforcement
Roll this out in stages. That reduces the chance you block a legitimate sender you forgot about:
- Week 1:
p=none(monitor) - Week 2:
p=quarantinewith a percentage (start at 10–25%) - Week 3+:
p=rejectonce reports look clean
4.2 Publish the DMARC TXT record
DMARC always lives at _dmarc.yourdomain.com.
Start (monitoring):
Host/Name: _dmarc
Type: TXT
Value: v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; ruf=mailto:dmarc@yourdomain.com; fo=1; adkim=s; aspf=s; pct=100
Notes on these tags:
rua: aggregate reports (XML summaries). Use a mailbox you actually read or pipe to a reporting tool.ruf: forensic reports (not always sent; can include message snippets). If privacy policy is a concern, omitruf.adkim=sandaspf=s: strict alignment. This is ideal for transactional mail if you control the From domain and want consistency.fo=1: request reports on any auth failure.
Move to quarantine (partial) once DKIM/SPF pass consistently:
v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s
Then reject (recommended for a stable transactional domain):
v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s
4.3 Verify DMARC is visible
dig +short TXT _dmarc.yourdomain.com
If your DNS host splits long TXT strings, that’s normal.
DMARC parsers reassemble them as long as the content is correct.
Step 5: Send a test email and verify authentication results
Now confirm the records aren’t just published. They must also be used and aligned by your sending path.
5.1 Send a message from the exact path your app uses
Don’t test from a different system “just to check.”
Trigger a real transactional template (receipt, reset, login code). That’s the mail stream providers judge.
5.2 Check headers for SPF/DKIM/DMARC
In Gmail, open the message → “Show original”. Look for:
- SPF:
PASSand shows the correct sending IP or include domain - DKIM:
PASSandd=yourdomain.com - DMARC:
PASSand alignment details
Typical “good” authentication-results header includes lines like:
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of bounce@yourdomain.com designates 203.0.113.10 as permitted sender)
dkim=pass header.i=@yourdomain.com header.s=tx2026 header.b=...
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=yourdomain.com
5.3 If DKIM fails: quick diagnostics
- DNS typo: wrong selector name (
tx2026vstxn2026), or publishing under the wrong domain. - Key mismatch: you rotated the private key but didn’t update DNS.
- DNS propagation: your resolver still has an old record. Try
dig @1.1.1.1anddig @8.8.8.8to compare. - Milter not active (self-hosted): check logs.
On a Postfix/OpenDKIM server, check service status and recent logs:
sudo systemctl status opendkim --no-pager
sudo journalctl -u opendkim -n 100 --no-pager
5.4 If SPF passes but DMARC fails: it’s usually alignment
DMARC looks at the domain in the visible From: header.
It compares that domain to:
- SPF-authenticated envelope sender domain (Return-Path)
- DKIM
d=domain
If your provider sets Return-Path to provider-bounces.example and you don’t have DKIM aligned to yourdomain.com, DMARC fails even though SPF “passes.”
Fix this by enabling custom DKIM signing for your domain at the provider. Or change the sending identity so SPF aligns (less common).
Step 6: Tighten policy without breaking legitimate mail
Once your test mail shows SPF/DKIM/DMARC pass consistently, increase DMARC enforcement.
A practical progression for most site owners sending transactional mail:
- Keep
p=nonefor 7–10 days and read aggregate reports. - Switch to
p=quarantine; pct=25for another week. - Move to
p=rejectfor full protection against spoofing.
One warning: DMARC enforcement will flush out other senders fast.
If other systems still send mail as @yourdomain.com (old plugins, forgotten CRMs, a second server), expect failures. Schedule time to fix or retire those senders.
Step 7: Verification checklist you can keep for change windows
- Only one SPF TXT record at
yourdomain.com - SPF ends with
-all(or~allonly during migrations) - DKIM TXT record exists at
selector._domainkey.yourdomain.com - Mail headers show
dkim=passwithd=yourdomain.com - DMARC TXT exists at
_dmarc.yourdomain.com - Mail headers show
dmarc=passand alignment - rDNS and HELO hostname are correct if you send directly from a VPS
Common mistakes that put transactional mail in spam
- Using a different From domain than your DKIM/SPF identity (alignment failure).
- Publishing “wildcard” SPF like
+all(this effectively disables SPF and looks suspicious). - Multiple SPF records after copying values from two providers.
- DMARC set to reject too early while a legacy sender still exists.
- Forgetting rDNS when sending directly from a VPS IP.
Practical next steps for a production mail setup
If you run email on your own server, don’t stop after DNS.
Add guardrails so a bug or backlog doesn’t burn your reputation.
Rate limits, queue monitoring, and a basic recovery plan prevent most real-world incidents.
- For building a safer relay flow: Mail Transfer Agent setup guide tutorial
- For fast incident triage: VPS email queue troubleshooting
If you’re sending transactional email from your own infrastructure, prioritize stable IPs, clean DNS control, and enough headroom to avoid mail queue pileups. A HostMyCode VPS is a solid baseline for Postfix/Exim plus DKIM signing, and managed VPS hosting is a good fit if you don’t want to maintain mail services and DNS changes yourself.
FAQ: set up DKIM SPF DMARC for transactional email
Do I need all three: SPF, DKIM, and DMARC?
Yes. SPF and DKIM prove authorization and message integrity. DMARC ties them to the visible From domain and tells providers what to do when checks fail.
For transactional email in 2026, “SPF only” rarely holds up.
Should I use DMARC strict alignment (adkim=s; aspf=s)?
If you control your transactional sender and From domain, strict alignment is the cleanest setup.
If you have multiple subdomains or mixed senders, start relaxed (omit adkim/aspf) and tighten later.
Why does SPF pass but DMARC fail?
Because DMARC checks alignment. SPF might pass for a provider’s bounce domain, while your visible From domain is yourdomain.com.
Fix it by enabling DKIM signing as d=yourdomain.com (best), or by changing the envelope sender domain to align.
How long does it take for DNS changes to affect deliverability?
Often minutes, sometimes hours. TTL, resolver caching, and DNS provider propagation all matter.
During setup, use a low TTL (300) and verify with dig against public resolvers.
Will DKIM/SPF/DMARC guarantee inbox placement?
No, but they remove several common reasons providers distrust your mail.
Content, complaint rates, list hygiene, and sending patterns still matter. For transactional email, authentication plus stable infrastructure is usually the fastest win.
Summary
To keep transactional emails out of spam, your authentication has to match your real sending path.
Publish one SPF record for your domain, add a 2048-bit DKIM key (or enable provider DKIM), and roll out DMARC from monitoring to enforcement once alignment stays clean.
If you want a predictable environment for sending mail—where DNS, rDNS, and signing keys are under your control—start with a HostMyCode VPS, and consider HostMyCode migrations if you’re moving from shared hosting or consolidating multiple senders.