To set up SPF, DKIM, and DMARC for cold email, publish three DNS TXT records on each sending domain: one SPF record listing every server that sends mail, one DKIM record holding a 2048-bit public key generated by your mailbox provider, and one DMARC record at _dmarc.yourdomain.com starting at p=none. This guide gives you the exact TXT values for Google Workspace, Microsoft 365, and custom SMTP, plus the alignment trap that silently breaks DMARC when you send through Instantly or Smartlead.
What are SPF, DKIM, and DMARC, and why do they matter for cold email?
SPF, DKIM, and DMARC are three DNS records that prove your cold email is not spoofed. SPF lists which servers can send from your domain. DKIM signs each message with a private key so receivers can verify nothing was tampered with. DMARC tells inbox providers what to do when one or both checks fail, and where to send reports.
Since February 2024, Gmail and Yahoo require all three records for any sender hitting 5,000 messages per day to their users. Microsoft followed in May 2025 for Outlook.com. Miss any of the three and your messages get a 550 permanent rejection, not a deferral.
Adoption is still patchy. According to the EasyDMARC 2026 Adoption Report, only 47.7% of top domains have a valid DMARC record, and just 7.7% reach the gold standard of p=reject with reporting enabled. For cold email, that gap is your edge: properly authenticated domains land in the inbox while unauthenticated competitors get deferred.
What SPF record do I need for cold email?
You need exactly one SPF TXT record on the root of your sending domain, listing every service that sends mail as your domain. Publishing two SPF records voids both, per RFC 7208. Use ~all (softfail) during warmup, then move to -all (hardfail) once DMARC is at p=quarantine or higher.
Here are the exact values for the three most common cold email stacks:
Google Workspace only:
Host: @
Type: TXT
Value: v=spf1 include:_spf.google.com ~all
TTL: 3600
Microsoft 365 only:
Host: @
Type: TXT
Value: v=spf1 include:spf.protection.outlook.com -all
TTL: 3600
Google Workspace + a custom SMTP (e.g. Mailgun):
Host: @
Type: TXT
Value: v=spf1 include:_spf.google.com include:mailgun.org ~all
TTL: 3600
Keep your record under 10 DNS lookups. Each include: counts as one lookup, and chained includes count too. Per DMARCLY's PermError guide, exceeding 10 lookups returns SPF PermError, which DMARC treats as a hard fail and reduces inbox placement by up to 30%.
How do I set up DKIM for Google Workspace?
To set up DKIM for Google Workspace, generate a 2048-bit key in the Admin Console, publish it as a TXT record at google._domainkey.yourdomain.com, wait for propagation, then click Start Authentication. Full steps from Google's official DKIM setup docs:
- Open the Admin Console at admin.google.com.
- Navigate to Apps → Google Workspace → Gmail → Authenticate email.
- Select your domain from the dropdown.
- Click Generate new record. Choose 2048-bit key length (do not use 1024). Use selector
google. - Copy the TXT record value Google shows you.
- In your DNS panel, create a new TXT record:
Host: google._domainkey
Type: TXT
Value: v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... (the rest of the key Google gave you)
TTL: 3600
- Wait 48 hours for DNS to propagate. Most DNS providers update within an hour, but Google's checker is conservative.
- Return to the Admin Console and click Start Authentication.
The public key string is roughly 400 characters because DNS TXT records cap each string at 255 chars. Cloudflare, Namecheap, and GoDaddy split the value automatically. If your DNS panel does not, wrap the key in quoted segments: "v=DKIM1; k=rsa; p=MIIB..." "...rest".
How do I set up SPF, DKIM, and DMARC for Microsoft 365?
Microsoft 365 requires one SPF TXT record, two DKIM CNAME records (not TXT), and one DMARC TXT record. DKIM is disabled by default and must be enabled in both the Defender portal and DNS. Follow the Microsoft Learn DKIM configuration doc:
Step 1 — SPF:
Host: @
Type: TXT
Value: v=spf1 include:spf.protection.outlook.com -all
Step 2 — DKIM CNAMEs. In security.microsoft.com, go to Email & collaboration → Policies & rules → Threat policies → Email authentication settings → DKIM, select your domain, and copy the two CNAME hostnames Microsoft shows you. Then publish:
Host: selector1._domainkey
Type: CNAME
Value: selector1-yourdomain-com._domainkey.tenantname.onmicrosoft.com
Host: selector2._domainkey
Type: CNAME
Value: selector2-yourdomain-com._domainkey.tenantname.onmicrosoft.com
Replace yourdomain-com with your domain (hyphens, not dots) and tenantname with your M365 tenant. Return to the DKIM screen and toggle Sign messages for this domain to On.
Step 3 — DMARC:
Host: _dmarc
Type: TXT
Value: v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; ruf=mailto:dmarc@yourdomain.com; fo=1; adkim=r; aspf=r
Set TTL to 3600 (1 hour). M365 propagates faster than Google -- you can usually verify in 15 to 30 minutes.
How do I set up SPF, DKIM, and DMARC for a custom SMTP?
For custom SMTP (Mailgun, Postmark, Amazon SES, self-hosted Postfix), you generate your own DKIM keypair, publish the public half in DNS, and configure your MTA to sign with the private half. The DNS records look like this:
SPF:
Host: @
Type: TXT
Value: v=spf1 ip4:203.0.113.42 include:mailgun.org -all
Replace 203.0.113.42 with your SMTP server's static IP. If you have several, list each ip4: separately.
DKIM (self-generated 2048-bit): Generate the keypair on your server:
opendkim-genkey -b 2048 -d yourdomain.com -s mail
This produces mail.private (keep secret) and mail.txt (publish to DNS):
Host: mail._domainkey
Type: TXT
Value: v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQE...
DMARC: same as Google/Microsoft (see next section).
For managed SMTPs like Mailgun or SES, the provider gives you a CNAME instead of TXT. Use a unique selector per provider -- never reuse default or mail across two services or you will overwrite a working key.
What DMARC policy should I start with?
Start with p=none and reporting enabled. This puts DMARC in monitoring mode -- failures are still delivered, but you get daily aggregate reports showing which servers are sending as your domain. The exact starter record:
Host: _dmarc
Type: TXT
Value: v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; ruf=mailto:dmarc-reports@yourdomain.com; fo=1; adkim=r; aspf=r; pct=100
Tag reference:
p=none-- monitor only, no enforcementrua=-- aggregate XML reports (daily, machine-readable)ruf=-- forensic reports per failure (most providers ignore this now for privacy)fo=1-- send forensic report if SPF or DKIM fails alignmentadkim=r/aspf=r-- relaxed alignment (the default, matches organizational domain)pct=100-- apply policy to 100% of mail (usepct=25when ramping)
Do not skip p=none. Per the DMARC Report enforcement timeline guide, organizations using percentage-based ramp-ups experience 60% fewer delivery disruptions than those who jump straight to quarantine. Sign up for a free DMARC report parser like Postmark's free service or dmarcian to make the XML readable.
What is the DMARC alignment trap when sending through Instantly or Smartlead?
The alignment trap: Smartlead, Instantly, and Lemlist sign your messages with their own DKIM and use their own MAIL FROM domain, so SPF authenticates against *.smartlead.ai instead of your domain. DMARC then fails alignment even though SPF and DKIM both pass individually.
Here is what actually happens. You connect a Google Workspace mailbox to Smartlead. Smartlead sends through Google's SMTP relay. The From: header reads you@yourdomain.com (organizational identifier), but:
- SPF authenticates the return path (envelope sender), which Smartlead rewrites to
bounce-xyz@em.smartlead.ai - DKIM is signed by Google for
yourdomain.com-- this passes alignment - DMARC requires that either SPF or DKIM align with the From header
So DMARC passes via DKIM, but only because you are sending through your own mailbox. If you ever switch Smartlead to its own SMTP, SPF fails alignment and you must rely entirely on DKIM. Per the Valimail third-party sender guide, the fix is:
- Keep
adkim=randaspf=r(relaxed). Never use strict for cold email -- it breaks third-party senders. - Use a unique DKIM selector per tool. Smartlead:
smartlead._domainkey. Instantly:instantly._domainkey. Do not reusedefault. - Configure a custom tracking domain (e.g.
track.yourdomain.com) so link rewrites don't trigger SpamAssassin'sURI_NOVOWELrule. - Verify alignment with MXToolbox's DMARC Inspector before scaling volume -- it shows which side of alignment is passing.
How do I move from DMARC p=none to p=reject in 6 weeks without breaking deliverability?
Move through five staged policies, each held for one to two weeks, watching DMARC aggregate reports between each step. Never flip directly from p=none to p=reject. Six-week schedule for a simple cold email setup (one or two sending services):
| Week | Policy | What to watch in reports |
|---|---|---|
| Week 1-2 | p=none; pct=100 |
All sources sending as you. Whitelist any legitimate ones missing DKIM/SPF |
| Week 3 | p=quarantine; pct=25 |
25% of failing mail goes to spam. Watch for legitimate sources still failing |
| Week 4 | p=quarantine; pct=100 |
All failing mail goes to spam. Bounce rate should hold steady |
| Week 5 | p=reject; pct=25 |
25% of failing mail bounces. Confirm no legitimate sources affected |
| Week 6 | p=reject; pct=100 |
Full enforcement |
Exact week 5 record:
v=DMARC1; p=reject; pct=25; sp=quarantine; rua=mailto:dmarc@yourdomain.com; fo=1; adkim=r; aspf=r
Note sp=quarantine -- this sets the subdomain policy. Most cold emailers send from the root domain but use subdomains for transactional mail. Setting sp=reject too early kills password resets and order confirmations.
Per the Global Cyber Alliance's enforcement data, the most damaging mistake is rushing. If you see any legitimate source failing in reports during week 2, pause the ramp and fix that source before moving on. Better to spend 10 weeks at p=reject than 4 weeks bouncing your own marketing emails.
How do I verify SPF, DKIM, and DMARC are working?
Use dig from the command line for raw DNS truth, then send a test message to a Gmail account and check the original headers for dmarc=pass. Verification checklist:
1. Check SPF:
dig TXT yourdomain.com +short
Look for exactly one line starting v=spf1. If you see two, delete one or you will fail authentication.
2. Check DKIM (Google Workspace):
dig TXT google._domainkey.yourdomain.com +short
Look for v=DKIM1; k=rsa; p=MIIB.... Empty result means DNS has not propagated.
3. Check DKIM (Microsoft 365):
dig CNAME selector1._domainkey.yourdomain.com +short
dig CNAME selector2._domainkey.yourdomain.com +short
Both should resolve to the *.onmicrosoft.com target.
4. Check DMARC:
dig TXT _dmarc.yourdomain.com +short
Look for v=DMARC1; p=....
5. Send a live test. Email a Gmail address. Open the message, click the three-dot menu, click Show original. You should see:
SPF: PASS with IP 209.85.220.41
DKIM: 'PASS' with domain yourdomain.com
DMARC: 'PASS'
6. Cross-check with MXToolbox SuperTool for an external view that catches caching issues. Run spf:yourdomain.com, dkim:yourdomain.com:google, and dmarc:yourdomain.com.
What are the most common SPF, DKIM, and DMARC errors and how do I fix them?
Eight errors account for nearly all cold email authentication failures. Use this lookup table when you see a specific message in DMARC reports or bounce logs:
| Error message | What it means | Fix |
|---|---|---|
SPF PermError: too many DNS lookups |
More than 10 include: chains |
Flatten with SPF macros or drop unused senders |
SPF temperror |
DNS timeout, often Cloudflare | Retry. If persistent, lower TTL to 300 |
SPF none |
No SPF record found | Publish one. Most common on freshly-purchased domains |
DKIM neutral: bad RSA signature |
Private key on server doesn't match published public key | Regenerate keypair, republish DNS, restart MTA |
DKIM permerror: key not found |
Selector mismatch or DNS not propagated | Verify selector with dig. Wait 24h |
DMARC fail: dkim=pass, spf=pass |
Both passed but neither aligned with From | Switch to relaxed (adkim=r aspf=r) or fix MAIL FROM |
DMARC fail: no aligned passing identifier |
Sending tool rewrites both Return-Path and DKIM | Add the tool to SPF and publish their DKIM selector |
550 5.7.26 unauthenticated email is not accepted |
Gmail/Yahoo bulk sender enforcement | Verify all three records resolve. Check sender reputation in Google Postmaster Tools |
For the last two errors (alignment failures and Gmail 550 bounces), the root cause is almost always DMARC alignment, not missing records. Use the dig commands above plus Postmaster Tools' Authentication tab to pinpoint which side fails.
| Provider | SPF Value | DKIM Host | DKIM Type | DMARC Host |
|---|---|---|---|---|
| Google Workspace | v=spf1 include:_spf.google.com ~all | google._domainkey | TXT (2048-bit) | _dmarc |
| Microsoft 365 | v=spf1 include:spf.protection.outlook.com -all | selector1._domainkey, selector2._domainkey | CNAME | _dmarc |
| Amazon SES | v=spf1 include:amazonses.com ~all | {token}._domainkey | CNAME (provided) | _dmarc |
| Mailgun | v=spf1 include:mailgun.org ~all | k1._domainkey | TXT | _dmarc |
| Custom Postfix (OpenDKIM) | v=spf1 ip4:YOUR.IP ~all | mail._domainkey | TXT (self-generated) | _dmarc |