On a VPS running your own Postfix, the entire responsibility for outbound mail reputation falls on you: there's no pre-warmed, pre-configured provider infrastructure behind it. DKIM, SPF, and DMARC on a self-managed mail server aren't optional hardening — they're the minimum without which mail starts landing in spam or getting rejected from day one.
Installing OpenDKIM
apt install opendkim opendkim-tools -y
# Generate keys
mkdir -p /etc/opendkim/keys/yourdomain.com
cd /etc/opendkim/keys/yourdomain.com
opendkim-genkey -t -s mail -d yourdomain.com
chown opendkim:opendkim mail.private
Getting the public key for DNS
cat /etc/opendkim/keys/yourdomain.com/mail.txt
# Add this record to DNS as TXT for:
# mail._domainkey.yourdomain.com
Full set of DNS records
| Record | Type | Name | Value |
|---|---|---|---|
| MX | MX | @ | mail.yourdomain.com (priority 10) |
| SPF | TXT | @ | v=spf1 mx ~all |
| DKIM | TXT | mail._domainkey | v=DKIM1; k=rsa; p=PUBLICKEY |
| DMARC | TXT | _dmarc | v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com |
Unlike managed mail services, where the provider hands you MX, SPF, and DKIM as an already-consistent set, on a VPS all four records are assembled by hand and must reference each other without contradiction: the domain in MX, the domain in SPF, and the selector in the DKIM record's name all have to match what's actually configured in Postfix and OpenDKIM.
Verifying the DKIM signature
# Test the signature
opendkim-testkey -d yourdomain.com -s mail -vvv
# Check after sending
# The email header should contain:
# DKIM-Signature: v=1; a=rsa-sha256; d=yourdomain.com; s=mail...
Common OpenDKIM Errors
| Message | Cause | Fix |
|---|---|---|
| opendkim: mail.private: No such file or directory | The KeyFile path in /etc/opendkim.conf doesn't match where the key actually lives | Compare the path in the config against the output of ls /etc/opendkim/keys/yourdomain.com/ |
| body hash did not verify (DKIM-Signature) | The message changed after signing — for example, an intermediate server added a mailing-list footer | Exclude such routes from the relay, or sign the message after all body modifications |
| key not found in DNS (opendkim-testkey) | The TXT record hasn't propagated yet, or the selector in DNS doesn't match the name in the config | Check dig TXT mail._domainkey.yourdomain.com and wait out the TTL |
| Postfix: reject: 550 5.7.1 SPF check failed | The outbound server's IP isn't included in the sending domain's SPF record (a third-party relay) | Ask the sending domain's owner to add the server's IP to their SPF via an ip4: mechanism |
For any of these errors, the first step is checking /var/log/mail.log (Debian/Ubuntu) or /var/log/maillog (RHEL family): it shows exactly at what stage Postfix and the OpenDKIM milter processed the message and with what rejection code, rather than guessing based on symptoms seen at the recipient's end.
From p=none to p=reject: a DMARC Rollout Plan
| Stage | p value | Duration | What happens |
|---|---|---|---|
| Monitoring | p=none | 1–2 weeks | rua reports are collected, no message is blocked |
| Partial quarantine | p=quarantine; pct=25 | 1 week | A quarter of failing messages get sent to recipients' spam folders |
| Full quarantine | p=quarantine; pct=100 | 1–2 weeks | All failing messages go to spam |
| Rejection | p=reject | ongoing | Messages without valid DKIM/SPF are rejected on receipt |
On a self-hosted VPS, rua reports arrive as an ordinary email to the address you specified, containing XML inside a gzip archive — there's no ready-made dashboard for reading them the way large mail providers offer. In practice they're either parsed with a homegrown script, or forwarded to a free third-party service like dmarcian, which turns them into a readable table of sending sources for you.
Verifying SPF and DMARC Records
# Check the published SPF record
dig TXT yourdomain.com +short
# Check the DMARC record and its current policy
dig TXT _dmarc.yourdomain.com +short
# Quick online check by sending a message
# mail-tester.com — returns an SPF/DKIM/DMARC breakdown within seconds
When DKIM/SPF/DMARC on a VPS Doesn't Fit
- Mail is sent through several external mailing services at once — you need a single SPF record with an include for each service, not separate TXT records: a zone only allows one SPF-type record
- The server's IP is in a PBL or a dynamic provider range — DKIM won't save you from a block based on the IP's own reputation; a dedicated static address is needed
- You need zero-downtime key rotation — you'll need to run two selectors in parallel, for example mail and mail2, and switch between them
- Mail is already hosted on Google Workspace or another SaaS — DKIM is configured on the provider's side, see Google Workspace: MX and DNS, not through OpenDKIM on a VPS
Frequently asked questions
Is it safe to jump straight to p=reject in DMARC without a monitoring period?
Jumping straight to p=reject is risky: if DKIM or SPF is misconfigured, legitimate mail starts getting rejected by recipients before you even notice. The monitoring phase with p=none exists precisely so you can see the real sending sources through rua reports and confirm your own mail passes authentication before tightening the policy.
What happens if mail goes out through several external sending services at once?
A DNS zone only allows one record of the SPF type, so you can't add a separate SPF record per sending service — every sending source has to be listed through an include mechanism inside the domain's single SPF record. Add multiple separate SPF records instead, and verification becomes unpredictable, with some mail getting rejected regardless of DKIM.
How do I read DMARC (rua) reports without a ready-made dashboard like the big mail providers offer?
On a self-hosted VPS these reports arrive as an ordinary email containing a compressed archive with markup inside — reading them by eye is impractical. In practice they're either parsed with a small homegrown script or forwarded to a free third-party service such as dmarcian, which builds a table of sending sources and highlights what passes authentication and what doesn't.
Do I need to change DKIM settings when moving mail to Google Workspace or another SaaS?
Yes, the OpenDKIM configuration on the VPS becomes unnecessary in that case: the provider generates the key and signature on its own side, and all you need to do is add its records to the domain's DNS. The old OpenDKIM setup and the SaaS provider shouldn't both sign the same mail at once — that creates confusing, conflicting authentication results.