
Email deliverability issues rarely look tidy. One message lands, the next bounces, and then you start hearing “your mail isn’t trusted.” On a VPS, a common cause is simple and fixable: your outbound mail isn’t cryptographically signed.
This DKIM setup tutorial shows how to enable DKIM signing for Postfix on Ubuntu with OpenDKIM. You’ll publish the required DNS TXT record, then confirm that outbound mail leaves your server with a valid signature.
The steps below assume you’re running Postfix on Ubuntu 24.04/24.10 (or newer) and you control DNS for your domain.
This works on any VPS. It’s easier with a stable IP and clean reverse DNS.
If you’re building a mail-capable server for apps or WordPress, a HostMyCode VPS gives you the isolation and control you need. You also avoid inheriting someone else’s mail reputation.
What you’ll build (and what DKIM actually changes)
DKIM (DomainKeys Identified Mail) adds a signature header to outbound messages. Receiving servers validate that signature using a public key you publish in DNS.
If the signature matches, the content wasn’t altered in transit. The sending domain is also authenticated at the message level.
- Before DKIM: you mostly depend on IP reputation and SPF; forwarding and mailing lists can muddy authentication signals.
- After DKIM: each message can be validated against your DNS key, and most large providers expect it by default in 2026.
DKIM doesn’t replace SPF and DMARC. It supports them.
If your reverse DNS is wrong, fix that first. DKIM won’t compensate for a bad PTR record.
Pair this guide with rDNS setup tutorial and PTR record troubleshooting.
Prerequisites checklist (don’t skip this)
- Ubuntu server with root or sudo access
- Postfix installed and sending outbound mail
- A hostname (FQDN) that resolves to your server IP (A/AAAA record)
- Working reverse DNS (PTR) that matches your hostname
- Ability to add DNS TXT records for your sending domain
Quick diagnostics:
hostname -f
postconf -n | egrep 'myhostname|mydomain|myorigin|inet_interfaces'
# Confirm outbound connectivity to recipient MX (basic)
ss -lntp | egrep ':25|:587'
Step 1: Install OpenDKIM and tools on Ubuntu
OpenDKIM is the standard choice for DKIM signing with Postfix on Ubuntu. Install the packages, then confirm the service starts cleanly.
sudo apt update
sudo apt install -y opendkim opendkim-tools
systemctl status opendkim --no-pager
If you’re running a firewall, you don’t need to open anything new for DKIM. Signing happens locally, between Postfix and OpenDKIM.
If you administer a server through locked-down panels, keep them private. Use a tunnel instead—see SSH tunnel setup guide.
Step 2: Create DKIM keys (selector-based, per domain)
DKIM keys are scoped by domain and selector. The selector makes rotation painless.
You publish a new selector, switch signing to it, then keep the old selector briefly for delayed mail.
A simple convention is date-based, like s2026 or mta2026.
Create a directory for your domain keys:
sudo mkdir -p /etc/opendkim/keys/example.com
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 0750 /etc/opendkim/keys/example.com
Generate a 2048-bit keypair (typical for DKIM in 2026):
cd /etc/opendkim/keys/example.com
sudo -u opendkim opendkim-genkey -b 2048 -s s2026 -d example.com
sudo -u opendkim chmod 0600 s2026.private
ls -la
You should now have:
s2026.private(private key used by OpenDKIM)s2026.txt(DNS TXT record content you’ll publish)
Step 3: Configure OpenDKIM (SigningTable, KeyTable, TrustedHosts)
Use the standard mapping approach. It stays readable and scales cleanly when you add more domains.
/etc/opendkim/TrustedHosts/etc/opendkim/KeyTable/etc/opendkim/SigningTable
3.1 Trusted hosts (who is allowed to ask OpenDKIM to sign):
sudo tee /etc/opendkim/TrustedHosts >/dev/null <<'EOF'
127.0.0.1
localhost
# Add your server IPs if needed (optional)
# 203.0.113.10
EOF
3.2 KeyTable (selector/domain to private key path):
sudo tee /etc/opendkim/KeyTable >/dev/null <<'EOF'
s2026._domainkey.example.com example.com:s2026:/etc/opendkim/keys/example.com/s2026.private
EOF
3.3 SigningTable (which From: domains get which selector):
sudo tee /etc/opendkim/SigningTable >/dev/null <<'EOF'
*@example.com s2026._domainkey.example.com
EOF
Next, update /etc/opendkim.conf so OpenDKIM uses those mapping files. You also need a socket that Postfix can reach.
On Ubuntu, you can use an inet socket on localhost or a unix socket. For a first setup, inet on localhost is usually the least confusing.
sudo cp -a /etc/opendkim.conf /etc/opendkim.conf.bak
sudo nano /etc/opendkim.conf
Set (or ensure) these key lines exist:
Syslog yes
SyslogSuccess yes
LogWhy yes
UMask 002
Canonicalization relaxed/simple
Mode sv
SubDomains no
AutoRestart yes
AutoRestartRate 10/1h
Background yes
# Where Postfix will connect
Socket inet:8891@localhost
# Mapping files
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
Pitfall: if you previously configured a unix socket, don’t leave both options in place. OpenDKIM may start, but Postfix can point at the wrong socket.
When that happens, mail leaves without a DKIM signature.
Step 4: Connect Postfix to OpenDKIM (milter settings)
Postfix signs mail by handing messages to a milter. Configure Postfix to send outbound mail through OpenDKIM before it leaves the server.
sudo postconf -e 'milter_default_action=accept'
sudo postconf -e 'milter_protocol=6'
sudo postconf -e 'smtpd_milters=inet:localhost:8891'
sudo postconf -e 'non_smtpd_milters=inet:localhost:8891'
Restart services:
sudo systemctl restart opendkim
sudo systemctl restart postfix
Then confirm Postfix can reach the milter:
sudo journalctl -u opendkim -n 50 --no-pager
sudo journalctl -u postfix -n 80 --no-pager
You want clean startup logs and milter connections.
If you get “connection refused,” OpenDKIM isn’t listening where Postfix expects.
Step 5: Publish the DKIM DNS TXT record (the part that usually goes wrong)
Open the generated TXT file and copy the record value:
sudo cat /etc/opendkim/keys/example.com/s2026.txt
DNS panels often show this in a BIND-style format. In most providers, add a TXT record with:
- Name/Host:
s2026._domainkey - Type: TXT
- Value: starts with
v=DKIM1; k=rsa; p=and then a long key
Common pitfalls and fixes:
- Wrong name: some DNS UIs want the full
s2026._domainkey.example.com. Others want onlys2026._domainkey. The difference is whether the UI auto-appends your zone name. - Line breaks/quotes: paste it as one line unless the provider explicitly supports multi-string TXT values.
- Multiple DKIM records: don’t publish two TXT records with the same selector. Rotate by changing the selector, not by stacking TXT values.
If you manage DNS with HostMyCode, keep the zone organized and set TTLs intentionally before mail changes.
The workflow is the same one you’d use for web cutovers—see DNS propagation tutorial.
After publishing the TXT record, verify it from your VPS:
sudo apt install -y dnsutils
dig +short TXT s2026._domainkey.example.com
You should see the DKIM value returned.
If you get nothing back, the record name is wrong or it hasn’t propagated yet.
Step 6: Test signing with a real message (and inspect headers)
Send a message to an external mailbox you control (Gmail, Outlook, Fastmail). You can use mail or sendmail.
If mailx isn’t installed:
sudo apt install -y bsd-mailx
echo "DKIM test $(date -Is)" | mail -s "DKIM test" you@yourmailbox.com
Open the message and view the full headers. You should see something like:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com; s=s2026; ...
If the header is missing, watch the OpenDKIM logs while sending again:
sudo journalctl -u opendkim -f
Useful clue: “no signing table match” means the From: domain didn’t match your SigningTable rule.
That’s usually a typo, or you’re sending from a different domain than you intended.
Step 7: Add SPF and DMARC alignment (minimal, practical)
DKIM gets you most of the way. Receivers still evaluate SPF + DKIM + DMARC together.
For a single VPS sending mail for one domain, this baseline is a good start.
SPF TXT record (at the root of the domain):
v=spf1 a mx ip4:203.0.113.10 -all
DMARC TXT record (start in monitoring mode):
Host/Name: _dmarc
Value: v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; adkim=s; aspf=s
After you confirm legitimate senders are aligned, move from p=none to p=quarantine. Then move to p=reject if it fits your risk tolerance.
If you run WordPress forms or app mail, make sure every sending path uses the same authenticated server.
For a clean pattern, follow Email relay setup tutorial.
Step 8: Key rotation plan (so you’re not stuck with one key forever)
DKIM rotation is normal maintenance. Plan to rotate at least yearly.
Rotate immediately if you suspect key exposure.
- Generate a new selector (example:
s2026b) and publish its TXT record. - Update SigningTable (or KeyTable reference) to use the new selector.
- Restart OpenDKIM and Postfix.
- Leave the old TXT record published for 7–14 days so delayed mail can still validate.
- Remove the old selector from DNS and delete the old private key.
Key hygiene: private keys should be readable only by the OpenDKIM user.
If you back up /etc/opendkim/keys, treat it like credentials.
Troubleshooting: the 6 failures you’ll actually see
- No DKIM-Signature header at all: Postfix isn’t using milters, or the OpenDKIM socket doesn’t match. Re-check
postconf -n | grep milterandSocketin/etc/opendkim.conf. - “no signing table match” in opendkim logs: the From domain doesn’t match your SigningTable pattern. Confirm the visible From header.
- DKIM fails with “bad signature” at receiver: the DNS record is wrong (truncated key, extra quotes, wrong selector). Re-
digthe TXT and compare tos2026.txt. - Mail bounces despite DKIM passing: this is usually PTR mismatch or IP reputation. Start with reverse DNS checks: PTR record troubleshooting tutorial.
- Mail stuck in queue: DKIM won’t clear a backlog. Inspect Postfix queues and logs. This guide helps: Email queue troubleshooting tutorial.
- Multiple domains on one Postfix: you need a SigningTable entry per domain and a key directory per domain.
Hardening checklist for a mail-capable VPS
DKIM is one piece of a working mail server. If you host client mail or send application traffic, these checks prevent the usual headaches:
- Lock down SSH access (keys, allowlists, audit logging).
- Enable automatic security updates and scheduled reboots when required.
- Monitor disk usage—mail queues and logs can fill disks quietly.
- Set up basic monitoring and alerts for Postfix and OpenDKIM processes.
- Keep DNS tidy: A/AAAA, MX, SPF, DKIM, DMARC, and PTR must agree.
If you’d rather not own the daily maintenance (updates, baseline hardening, and ongoing monitoring), managed VPS hosting is often a better fit for production mail and WordPress stacks.
Summary: what “done” looks like
- OpenDKIM installed and running on
localhost:8891 - Postfix configured with
smtpd_miltersandnon_smtpd_milters - DKIM TXT record published and verifiable via
dig - Outbound test emails contain a valid
DKIM-Signatureheader - SPF and DMARC exist and are aligned with your sending domain
After this DKIM setup tutorial is working, your next wins come from consistency. Keep one or two known sending paths, stable DNS, and routine key rotation.
If you’re migrating mail workloads or rebuilding a server, start with infrastructure you can control.
Deploy on a HostMyCode VPS so your MTA settings, IP identity, and DNS changes stay in your hands.
If you’re setting up outbound mail for WordPress or client sites, run it on infrastructure you can actually tune and troubleshoot. A HostMyCode VPS gives you full control over Postfix, OpenDKIM, and DNS alignment, while managed VPS hosting is a good fit if you want day-to-day operations handled for you.
FAQ
Should I use one DKIM key for every subdomain and service?
You can, but separating by purpose is usually cleaner. Use different selectors (or a dedicated sending subdomain like mail.example.com) so you can rotate or revoke without disrupting everything.
What selector name should I choose in 2026?
Choose a name that makes rotation obvious, like s2026 or mta2026. Avoid a generic default if you plan to operate the server long-term.
Can DKIM break anything?
Not typically. Most “breaks” are failed validation from a bad DNS record, or messages modified by a mailing list.
Your DMARC policy and alignment settings determine how strict receivers will be.
How do I confirm DKIM passes without guessing?
Check the recipient’s authentication results header (often Authentication-Results). Confirm it shows dkim=pass for your domain and selector.
Do I still need PTR/rDNS if DKIM is configured?
Yes. Many providers still penalize mail from IPs without correct PTR, even if DKIM passes. If you’re seeing rejections, fix rDNS first.