SPF, DKIM and DMARC on a self-hosted SMTP server

The first time an email you sent from your own SMTP server landed in the recipient's spam folder, or worse, never arrived at all, you likely blamed the IP reputation or the content. But often the real culprit is missing or misconfigured email authentication. SPF, DKIM, and DMARC are the three DNS records that tell receiving mail servers: "this email really came from my server, and it was not tampered with." Without them, your carefully crafted messages look like forgeries to Gmail, Outlook, and every other major provider.
This post walks through exactly what each record does, how to generate the values, and how to publish them in your DNS zone. You will leave with copy-pasteable DNS templates and the commands to generate the cryptographic keys yourself. The environment assumed: a self-hosted SMTP server running Postfix on Ubuntu 24.04 LTS or Debian 12, with full root access and a dedicated IPv4 address in Vietnam from a provider like thueVPS.
自我托管 SMTP 服务器的 SPF、DKIM 和 DMARC 配置是确保邮件送达率的关键步骤。
Configuring SPF, DKIM, and DMARC on a self-hosted SMTP server is the critical step to ensuring email deliverability.
Why email authentication exists, and what happens without it
Email spoofing is trivial to pull off. Any server can craft a message with your domain in the From header. Before SPF, DKIM, and DMARC, there was no standard way for the receiving end to verify legitimacy. SMTP itself has no built-in authentication for the envelope or header.
SPF (Sender Policy Framework) lets you publish which IP addresses are authorised to send mail for your domain. The receiving server checks the envelope sender domain against the SPF record. If the sending IP is not listed, the message can be rejected or flagged.
DKIM (DomainKeys Identified Mail) attaches a digital signature to every outgoing message. The signature is generated with a private key stored on your SMTP server. The public key is published as a DNS TXT record. Receiving servers fetch the public key, verify the signature, and know the message was not altered in transit and genuinely came from your server.
DMARC (Domain-based Message Authentication, Reporting, and Conformance) tells the receiver what to do when SPF and DKIM checks fail. It also provides reporting so you can see who is sending mail claiming to be from your domain, legitimate or fraudulent.
A self-hosted SMTP server without these three records delivers poorly. Major inbox providers run strict authentication checks. A message that fails SPF and has no DKIM signature goes straight to spam, even if the IP reputation is clean. Setting these up costs you nothing but a few minutes in your DNS control panel and on the server. Skipping them costs you deliverability.
Step 1, Identify the domains and IPs you need to authorise
Before touching the DNS panel, list every domain you send mail from and every server IP that sends mail. This includes:
- The domain in your From address (example.com).
- Any subdomain your SMTP server uses in the HELO/EHLO greeting (often mail.example.com).
- Every IPv4 address that your server sends outbound mail from. If you run multiple SMTP servers, each IP must be in the SPF record.
If your SMTP server operates behind a NAT or uses a dedicated IPv4 provided by your host, such as the dedicated Vietnamese IPv4 that comes with every plan at thueVPS, use that public IP. Do not use the RFC 1918 private range.
Step 2, Publish the SPF record
The SPF record is a single TXT entry at the root of your domain. The syntax is straightforward. A minimal record for a server sending from IPv4 203.0.113.10 for example.com looks like this:
example.com. TXT "v=spf1 ip4:203.0.113.10 ~all"
Breakdown of the value:
v=spf1, declares this is SPF version 1.ip4:203.0.113.10, authorises this single IPv4 to send mail for the domain. You can list multiple IPs separated by spaces:ip4:203.0.113.10 ip4:198.51.100.20.~all, soft-fail any IP not listed. The message is still accepted but tagged as suspicious. Use-allfor hard fail (reject) once you are sure no legitimate sender is left out. Do not start with-allif you are still identifying all sending IPs.
If your domain also sends mail through a third-party service like Google Workspace or a newsletter provider, add their SPF include mechanism:
example.com. TXT "v=spf1 include:_spf.google.com ip4:203.0.113.10 ~all"
After adding the record, propagate it (usually within minutes if the TTL is low), then verify:
dig TXT example.com +short
Expected output includes the SPF line you just published.
Step 3, Generate DKIM keys and configure the signer
DKIM is the most technical part, but the process is well standardised. You generate a 2048-bit RSA key pair, install the private key on your Postfix server, and publish the public key in DNS.
3.1, Install OpenDKIM
sudo apt update
sudo apt install opendkim opendkim-tools
3.2, Generate the key pair
Pick a selector, a label that identifies this key. default is common, but you can use 2026 or any alphanumeric string. Create a directory for the domain and generate the keys:
sudo mkdir -p /etc/opendkim/keys/example.com
cd /etc/opendkim/keys/example.com
sudo opendkim-genkey -D /etc/opendkim/keys/example.com -d example.com -s default -b 2048
sudo chown -R opendkim:opendkim /etc/opendkim/keys/example.com
This creates two files:
default.private, the private key. Keep this on the server, never expose it.default.txt, the DNS record to publish. It contains the public key.
Look at default.txt:
cat /etc/opendkim/keys/example.com/default.txt
You will see a line like:
default._domainkey IN TXT "v=DKIM1; h=sha256; k=rsa; p=MIGfMA0GCSqGSIb4DQEBAQUAA4GNADCBiQKBgQ..."
Remove the quotes and line breaks. The DNS record value is the quoted string, put together as a single string. The hostname (left side of the record) is default._domainkey.example.com.
3.3, Publish the DKIM TXT record
In your DNS zone, add:
default._domainkey.example.com. TXT "v=DKIM1; h=sha256; k=rsa; p=MIGfMA0GCSqGSIb4DQEBAQUAA4GNADCBiQKBgQ..."
Verify with:
dig TXT default._domainkey.example.com +short
3.4, Configure OpenDKIM and Postfix
Edit /etc/opendkim.conf (check the full path; on Debian/Ubuntu it is here). Key lines to ensure are present or add:
Domain example.com
KeyFile /etc/opendkim/keys/example.com/default.private
Selector default
Socket inet:8891@localhost
Syslog Yes
OversignHeaders From
TrustAnchorFile /usr/share/dns/root.key
UserID opendkim
Restart OpenDKIM:
sudo systemctl restart opendkim
sudo systemctl enable opendkim
Now configure Postfix to use OpenDKIM as a milter. Add these lines to /etc/postfix/main.cf:
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
milter_default_action = accept
milter_protocol = 2
Restart Postfix:
sudo systemctl restart postfix
Send a test email to a DKIM validator like [email protected]. It will reply with a report that confirms whether the signature passes.
Step 4, Publish the DMARC policy
DMARC lives at _dmarc.example.com as a TXT record. A safe starting policy is:
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]; ruf=mailto:[email protected]; fo=1"
What each tag does:
v=DMARC1, version, always this.p=quarantine, send failing messages to spam (p=nonefor monitoring only,p=rejectto block). Start withp=none, review reports for a week, then move top=quarantine.pct=100, apply the policy to 100% of messages.rua, aggregate report destination (a mailbox where you receive daily XML reports).ruf, forensic report destination (individual message details). Not all receivers send these.fo=1, generate forensic reports when SPF or DKIM alignment fails.
Verify:
dig TXT _dmarc.example.com +short
If you do not have a report-parsing tool yet, services like dmarcian or URIports accept free tier uploads. Review the XML reports to spot any IPs or services sending mail for your domain that you forgot to authorise.
Step 5, Verify all three with a real test
Do not trust your own DNS lookups alone. Send a test email to [email protected] and also to [email protected] (ourmail.app provides a DMARC checker). The automated response from Port25 includes a detailed section for each mechanism:
==========================================================
Summary of Results
==========================================================
SPF check: pass
DKIM check: pass
DMARC check: pass
==========================================================
If any show fail, the response tells you why, missing record, wrong IP, or signature mismatch.
Common pitfalls and how to avoid them
Publishing the SPF record with too many DNS lookups. SPF has a hard limit of ten DNS lookups per evaluation chain (each include counts as one, plus the initial check). If you include more than a few third-party services, the record becomes invalid. Consolidate or outsource to a service that collapses includes.
Using a DKIM key shorter than 1024 bits. Many providers now require 2048-bit keys. If you already published a 1024-bit key, rotate it: generate a new key with -b 2048, update the DNS record, and replace the private key on the server.
Setting DMARC to p=reject too early. Until you have reviewed aggregate reports for at least a week, you risk blocking legitimate mail from services you forgot to authorise. Start with p=none, read the reports, adjust SPF and DKIM coverage, then move to p=quarantine.
Forgetting subdomains. DMARC alignment checks the domain in the From header. If your SMTP server sends with a subdomain (e.g. mail.example.com), the DKIM signature domain must match or be a parent domain. Set the DKIM domain to the same domain in the From address.
What about rDNS (PTR records)?
SPF, DKIM, and DMARC do not replace reverse DNS. Receiving servers also check that the sending IP has a PTR record that matches the server hostname in the HELO. Set the PTR record on your dedicated IPv4 to mail.example.com via your hosting provider's control panel. At thueVPS, free rDNS configuration is included with every dedicated IP.
FAQ
Do I need all three records?
Yes, for reliable delivery to major inboxes. SPF alone is easily spoofed. DKIM alone does not tell the receiver what to do on failure. DMARC ties them together and provides reporting. Skipping one weakens the chain.
Can I use the same DKIM selector for multiple domains?
No. Each domain needs its own key pair and selector. You can however reuse the same private key across domains if the selector names differ, but it is not recommended for security separation.
How long does DNS propagation take?
DNS changes propagate within minutes to hours depending on the TTL set on your records. Start with TTL 300 seconds during setup, then increase to 3600 or higher once everything works.
What do DMARC reports look like?
Aggregate reports are XML files sent daily. They list each IP that sent mail for your domain, the number of messages, and whether SPF and DKIM passed or failed. Forensic reports are individual message samples showing the failing headers.
I get an SPF perm_error when testing. What causes it?
The most common cause is exceeding the ten-DNS-lookup limit in the SPF evaluation. Count every include and a/mx mechanism that requires a lookup. Reduce the count or use a subdomain as a dedicated sending domain with a shorter SPF record.
Does a Vietnam-based SMTP server change anything for SPF/DKIM/DMARC?
No. The protocols are DNS-based and location-agnostic. What matters is that the IP you publish in SPF matches the actual outbound IP of your server. A Vietnamese dedicated IPv4 is treated the same as any other IP by Google or Microsoft, clean reputation and proper authentication are what count.
Related articles
- Warming up a new sending IP: a realistic 30-day schedule
- Why shared SMTP relays hurt deliverability once you scale
- Configure DNS records for your domain: A, CNAME, MX, TXT
- Configure swap and optimize memory on a low-ram VPS
自建SMTP的SPF、DKIM与DMARC
三条DNS记录决定邮件的身份验证结果。文中给出可直接复制的记录示例和校验方法,并说明常见的配置错误。配置完成后务必做一次实际投递测试。


