Back to tutorials
Tutorial

DKIM Setup Guide Tutorial (2026): Sign Outbound Email on a VPS with OpenDKIM + Postfix (DNS, Testing, and Fixes)

DKIM setup guide tutorial for 2026: install OpenDKIM on a VPS, publish DNS, test signatures, and fix common failures.

By Anurag Singh
Updated on Sep 17, 2026
Category: Tutorial
Share article
DKIM Setup Guide Tutorial (2026): Sign Outbound Email on a VPS with OpenDKIM + Postfix (DNS, Testing, and Fixes)

Most “my emails land in spam” tickets trace back to missing authentication, not message wording. A proper DKIM signature gives receiving servers a cryptographic way to confirm the email left your domain. It also confirms the message wasn’t changed in transit.

This DKIM setup guide tutorial walks through a repeatable DKIM deployment on a Linux VPS using Postfix + OpenDKIM. It includes real file paths, DNS records, and tests you can run right away.

The steps below match a typical VPS that sends mail for WordPress, contact forms, and small apps. The same approach works for higher-volume or multi-domain setups. You just need stricter discipline around key storage, permissions, and DNS records.

If you don’t want to maintain mail daemons, keep the web stack on a managed VPS hosting plan. That way, someone else handles the operational burden.

What you’ll build (and what DKIM actually signs)

DKIM signs selected message headers (for example From:, Subject:, Date:) and optionally the body. It uses a private key stored on your server.

Your DNS publishes the matching public key as a TXT record under a selector such as mail2026._domainkey.example.com. Receivers verify the signature against DNS. If everything matches, DKIM passes.

  • Goal: Every outbound message from your domain leaves with a valid DKIM signature.
  • Tools: Postfix + OpenDKIM on Ubuntu/Debian (commands included). Notes for RHEL-family included.
  • Outcome: Better inbox placement and fewer “spoofing” rejections (especially once you enforce DMARC later).

Prerequisites checklist (do this before touching DKIM)

Don’t skip this. You can configure DKIM “correctly” and still fail if your hostname, DNS, or outbound SMTP basics are messy.

  • A VPS with root access (Ubuntu 24.04 LTS / Debian 12 are common in 2026).
  • A stable hostname that matches DNS (mail.example.com is typical).
  • Working outbound SMTP (port 25 or submission ports as configured).
  • Domain DNS access (so you can add TXT records).

If your mail hostname or SMTP banner looks questionable, fix that first: email server hostname setup tutorial.

If you also need reverse DNS for deliverability, follow: PTR record setup tutorial.

Step 1 — Install OpenDKIM packages

On Ubuntu/Debian:

sudo apt update
sudo apt install -y opendkim opendkim-tools

On AlmaLinux/Rocky:

sudo dnf install -y opendkim opendkim-tools

Confirm the service exists:

systemctl status opendkim --no-pager

Step 2 — Create a DKIM key pair (selector-based)

Pick a selector you can keep long-term. Date-based selectors make rotation easier later (example: mail2026).

Create a directory for your domain keys:

sudo install -d -m 0750 -o opendkim -g opendkim /etc/opendkim/keys/example.com

Generate the key, then lock down permissions:

cd /etc/opendkim/keys/example.com
sudo opendkim-genkey -s mail2026 -d example.com
sudo chown opendkim:opendkim mail2026.private mail2026.txt
sudo chmod 0600 mail2026.private

You should now have:

  • /etc/opendkim/keys/example.com/mail2026.private (private key)
  • /etc/opendkim/keys/example.com/mail2026.txt (DNS TXT record template)

Step 3 — Configure OpenDKIM (KeyTable, SigningTable, TrustedHosts)

This layout is standard and easy to maintain. It works well for one domain or many.

3.1 Create KeyTable

Create /etc/opendkim/KeyTable:

sudo tee /etc/opendkim/KeyTable >/dev/null <<'EOF'
mail2026._domainkey.example.com example.com:mail2026:/etc/opendkim/keys/example.com/mail2026.private
EOF

3.2 Create SigningTable

Create /etc/opendkim/SigningTable:

sudo tee /etc/opendkim/SigningTable >/dev/null <<'EOF'
*@example.com mail2026._domainkey.example.com
EOF

This signs any message with a From: in @example.com.

If you host multiple domains, add one line per domain and selector.

3.3 Create TrustedHosts

Create /etc/opendkim/TrustedHosts:

sudo tee /etc/opendkim/TrustedHosts >/dev/null <<'EOF'
127.0.0.1
localhost
::1
# Your server hostname
mail.example.com
EOF

Common pitfall: OpenDKIM only signs for hosts listed here.

On split-tier setups, your web apps may send from a different internal IP. If that’s your environment, add the app server’s IP to TrustedHosts. Otherwise OpenDKIM may refuse to sign.

3.4 Point OpenDKIM to these tables

Edit /etc/opendkim.conf (same path on most distros):

sudo nano /etc/opendkim.conf

Make sure these lines exist (add them if they’re missing):

Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes

UMask                   002

Canonicalization        relaxed/simple
Mode                    sv
SubDomains              no

KeyTable                /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
ExternalIgnoreList      /etc/opendkim/TrustedHosts
InternalHosts           /etc/opendkim/TrustedHosts

Socket                  local:/run/opendkim/opendkim.sock

Why the socket matters: Postfix talks to OpenDKIM through this socket.

If Postfix points to the wrong path, mail will still send. It just won’t be DKIM-signed.

Step 4 — Wire Postfix to OpenDKIM via milter

Now tell Postfix to hand outbound messages to OpenDKIM for signing.

Edit /etc/postfix/main.cf:

sudo postconf -e "milter_default_action=accept"
sudo postconf -e "milter_protocol=6"
sudo postconf -e "smtpd_milters=unix:/run/opendkim/opendkim.sock"
sudo postconf -e "non_smtpd_milters=unix:/run/opendkim/opendkim.sock"

Reload Postfix after OpenDKIM restarts. The next step covers both.

Step 5 — Fix permissions and start services cleanly

On systemd, the most common failure is the runtime directory or socket permissions.

Create a systemd override so the runtime directory exists. Ubuntu/Debian often do this already, but confirm it:

sudo systemctl edit opendkim

Add:

[Service]
RuntimeDirectory=opendkim
RuntimeDirectoryMode=0755

Then restart OpenDKIM and reload Postfix:

sudo systemctl daemon-reload
sudo systemctl restart opendkim
sudo systemctl reload postfix

Quick socket check:

sudo ls -l /run/opendkim/opendkim.sock

You’re looking for an existing socket. If it’s missing, go straight to the logs:

sudo journalctl -u opendkim -n 80 --no-pager

Step 6 — Publish the DKIM DNS TXT record

Open the generated DNS template:

sudo cat /etc/opendkim/keys/example.com/mail2026.txt

You’ll see something like:

mail2026._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." )

Create a DNS TXT record in your DNS zone:

  • Name/Host: mail2026._domainkey
  • Type: TXT
  • Value: the full v=DKIM1; ...; p=... string (no extra quotes unless your DNS UI requires them)

Practical DNS warning: some DNS panels wrap long TXT records safely. Others mangle spacing or quotes.

If DKIM fails later, verify the published TXT value before you change anything else.

If you’re moving DNS during a hosting migration, lower TTL ahead of time and plan the cutover. This companion guide helps: DNS cutover checklist tutorial.

Step 7 — Verify DNS and confirm your server is signing

7.1 Confirm the DKIM record resolves

dig +short TXT mail2026._domainkey.example.com

You should see the public key returned. If you get nothing, DNS hasn’t propagated yet or the record is wrong.

7.2 Send a test email and inspect headers

Send a message from your domain to a mailbox you control (Gmail, Outlook, Fastmail, etc.). Open the full headers and check for:

  • DKIM-Signature: header present
  • Authentication-Results: shows dkim=pass

From the command line, send a quick test:

printf "Subject: DKIM test\n\nHello from DKIM\n" | sendmail -v you@yourmailbox.tld

7.3 Validate with opendkim-testkey

This confirms the DNS record matches the private key you generated:

sudo opendkim-testkey -d example.com -s mail2026 -vvv

If it’s correct, you’ll see “key OK”. If it fails, the output usually points to DNS lookup problems or a selector/key mismatch.

Step 8 — Troubleshoot common DKIM failures (fast diagnostics)

Most DKIM problems are straightforward once you check the right places. Treat this as a small runbook.

No DKIM-Signature header at all

  • Check the milter socket path matches in both /etc/opendkim.conf and Postfix main.cf.
  • Confirm Postfix loaded the config:
postconf | egrep 'milter|smtpd_milters|non_smtpd_milters'
  • Check OpenDKIM logs:
sudo journalctl -u opendkim -n 120 --no-pager

DKIM fails (dkim=fail) even though the header exists

  • DNS record pasted incorrectly (extra spaces, broken quoting, missing chunks of the key).
  • Selector mismatch (you signed with mail2026 but published default or vice versa).
  • Wrong domain in SigningTable (signing @example.com but your From is @www.example.com or a different domain).

Re-run:

sudo opendkim-testkey -d example.com -s mail2026 -vvv

Some mail signs, some doesn’t (WordPress vs SMTP users)

This almost always means you have multiple outbound paths.

A common example is WordPress using local sendmail while users submit via authenticated SMTP.

To cover both paths, you need:

  • smtpd_milters (SMTP sessions into Postfix)
  • non_smtpd_milters (local submissions like sendmail)

If you’re tracing delivery end-to-end, these two HostMyCode guides pair well with DKIM:

OpenDKIM says “permission denied” or can’t read the private key

Verify ownership and mode:

sudo ls -l /etc/opendkim/keys/example.com/mail2026.private

Fix if needed:

sudo chown opendkim:opendkim /etc/opendkim/keys/example.com/mail2026.private
sudo chmod 0600 /etc/opendkim/keys/example.com/mail2026.private

Step 9 — Key rotation plan (simple, safe, and realistic)

Don’t treat DKIM keys as “set and forget.” Rotation limits exposure if a key leaks. It also keeps your mail authentication posture current.

  1. Generate a new selector (example: mail2027) alongside the existing one.
  2. Publish the new selector TXT record in DNS.
  3. Switch SigningTable to the new selector.
  4. Keep the old selector in DNS for at least 7–14 days (some receivers retry or store signatures).
  5. Remove the old private key and then delete the old DNS record.

For multi-tenant hosting (resellers), rotation gets easier if you standardize selectors per domain.

Keep keys in per-domain directories, exactly as shown above.

Step 10 — Hardening notes for hosting VPS environments

DKIM is only one piece of email authentication. You’ll get the best results when the rest of the server is clean and predictable.

Summary: your DKIM deployment checklist

  • Installed opendkim and opendkim-tools
  • Generated selector-based keys under /etc/opendkim/keys/<domain>/
  • Configured KeyTable, SigningTable, and TrustedHosts
  • Connected Postfix to the OpenDKIM milter socket
  • Published DNS TXT record and verified with dig + opendkim-testkey
  • Confirmed headers show dkim=pass

If this is going on a production mail VPS, plan for safe edits and quick rollbacks. A HostMyCode VPS is a clean fit for running Postfix/OpenDKIM with full root control.

If you want lower maintenance, managed VPS hosting is the better option. It helps keep the stack stable and updated.

If you want DKIM (and the rest of your mail posture) to stay predictable in 2026, start with a VPS that offers stable networking, clean rDNS support, and enough headroom for spikes. HostMyCode offers both a self-managed HostMyCode VPS for hands-on admins and managed VPS hosting if you’d rather focus on the site than babysit the mail queue.

FAQ

Do I need DKIM if I already have SPF?

Yes. SPF validates the sending server IP for a given envelope sender. DKIM validates the message itself and is required for reliable DMARC enforcement.

What DKIM key size should I use in 2026?

Use 2048-bit RSA unless you have a specific constraint. It’s widely supported by receivers and DNS providers in 2026.

Can I use one DKIM key for multiple domains?

Technically you can, but don’t. Use separate keys per domain so rotation and incident response stay contained.

Why does DKIM pass sometimes and fail sometimes?

It’s usually multiple mail paths (local sendmail vs authenticated SMTP) or a DNS record that’s intermittently misread due to formatting. Check both milter settings and re-verify the TXT record with dig.

After DKIM, what should I do next for deliverability?

Add DMARC with reporting so you can see real-world failures before enforcing a strict policy. Also confirm your PTR/rDNS and HELO are correct and stable.