Back to tutorials
Tutorial

Set Up DKIM, SPF, And DMARC For A Domain

Set up DKIM SPF DMARC to fix spam-folder delivery with correct DNS records, alignment checks, and verification steps.

By Anurag Singh
Updated on Oct 17, 2026
Category: Tutorial
Share article
Set Up DKIM, SPF, And DMARC For A Domain

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.

  1. Send a test email from your application (password reset, test notification, etc.).
  2. In the receiving mailbox, view “Original” / “Message source”.
  3. 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 (and ip6 if 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.com instead of yourdomain.com.
  • Using ptr mechanism (discouraged, inconsistent).
  • Exceeding the SPF DNS lookup limit (10). Too many include chains 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:

  • tx2026 for transactional mail
  • default if 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=quarantine with a percentage (start at 10–25%)
  • Week 3+: p=reject once 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, omit ruf.
  • adkim=s and aspf=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: PASS and shows the correct sending IP or include domain
  • DKIM: PASS and d=yourdomain.com
  • DMARC: PASS and 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 (tx2026 vs txn2026), 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.1 and dig @8.8.8.8 to 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:

  1. Keep p=none for 7–10 days and read aggregate reports.
  2. Switch to p=quarantine; pct=25 for another week.
  3. Move to p=reject for 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 ~all only during migrations)
  • DKIM TXT record exists at selector._domainkey.yourdomain.com
  • Mail headers show dkim=pass with d=yourdomain.com
  • DMARC TXT exists at _dmarc.yourdomain.com
  • Mail headers show dmarc=pass and 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.

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.