EmailAugust 4, 20266 min read

How to Set Up DKIM on a Self-Hosted Mail Server

Generate a DKIM key, publish it in DNS and sign outgoing mail on your own Postfix server with OpenDKIM. The full command-by-command walkthrough.

NBy Nxeon

DKIM signs your outgoing mail with a private key and publishes the matching public key in DNS, so receivers can verify the message really came from your server and wasn't altered. On a self-hosted Postfix box you set this up with OpenDKIM. This guide is the full command-by-command walkthrough.

How DKIM works in one paragraph

Your server signs each outgoing message's headers with a private key, adding a DKIM-Signature header. The matching public key lives in a DNS TXT record at a *selector* (e.g. mail._domainkey.example.com). The receiver reads the selector from the signature, fetches the public key from DNS, and verifies the signature. If it checks out, the mail is authenticated. This is one leg of the trio in how to set up SPF, DKIM and DMARC step by step.

Step 1: Install OpenDKIM

On Debian/Ubuntu:

apt update
apt install opendkim opendkim-tools

Step 2: Generate the key pair

Pick a selector (any label — mail is common) and generate a 2048-bit key:

mkdir -p /etc/opendkim/keys/example.com
opendkim-genkey -b 2048 -d example.com -s mail -D /etc/opendkim/keys/example.com
chown -R opendkim:opendkim /etc/opendkim/keys

This creates two files:

  • mail.private — the private signing key (keep it secret, never leaves the server).
  • mail.txt — the public key, formatted as the DNS record you'll publish.

Step 3: Configure OpenDKIM

Edit /etc/opendkim.conf with the essentials:

Domain                  example.com
Selector                mail
KeyFile                 /etc/opendkim/keys/example.com/mail.private
Socket                  inet:8891@localhost
Mode                    sv
Canonicalization        relaxed/simple

(For multiple domains, use a KeyTable/SigningTable instead of the single-domain lines.) Restart:

systemctl restart opendkim

Step 4: Hook OpenDKIM into Postfix

Tell Postfix to pass mail through OpenDKIM as a milter. Add to /etc/postfix/main.cf:

milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Reload Postfix:

systemctl reload postfix

Now every outgoing message is signed. If you're building the whole server, this sits inside how to set up business email on a custom domain.

Step 5: Publish the public key in DNS

Open mail.txt — it contains the record. Publish it as a TXT record:

mail._domainkey.example.com.  TXT  "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...IDAQAB"

Two gotchas: the key is long and DNS TXT strings split at 255 characters — most panels handle this, but if yours doesn't, keep the split-string quoting from mail.txt. And the Host is mail._domainkey (your selector + ._domainkey), not the whole thing doubled. This CNAME-vs-TXT distinction is why hosted senders use CNAMEs — see how to verify your domain for email sending.

Deploying a new server with the Nxeon one-click deploy wizard
Deploying a new server with the Nxeon one-click deploy wizard

Step 6: Verify signing and the record

Check the DNS record resolves:

dig mail._domainkey.example.com TXT +short
opendkim-testkey -d example.com -s mail -vvv

opendkim-testkey should report the key is OK. Then send a message to a Gmail account, open Show original, and confirm DKIM: PASS. If it fails, re-check the selector name and that the published p= matches mail.txt. Don't forget reverse DNS — how to set up reverse DNS (PTR) for a mail server — and general deliverability in how to stop your emails going to spam.

Rotate DKIM keys without downtime

DKIM keys should be rotated periodically (say yearly) so a leaked key has a limited window. The trick is to overlap selectors so no message is ever signed with a key that isn't published:

  1. Generate a new key with a new selector, e.g. 2026b:
opendkim-genkey -b 2048 -d example.com -s 2026b -D /etc/opendkim/keys/example.com
  1. Publish the new 2026b._domainkey TXT record and confirm it resolves.
  2. Switch OpenDKIM to sign with 2026b (update Selector or the SigningTable) and reload.
  3. Wait a few days for in-flight mail signed with the old key to clear, then remove the old selector's DNS record.

Because verification uses whatever selector each message names, both keys validate during the overlap — zero downtime.

Sign multiple domains from one server

The single-domain lines in opendkim.conf don't scale past one domain. For several, switch to tables. In opendkim.conf:

KeyTable      /etc/opendkim/KeyTable
SigningTable  /etc/opendkim/SigningTable

/etc/opendkim/KeyTable:

mail._domainkey.example.com   example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.example.net   example.net:mail:/etc/opendkim/keys/example.net/mail.private

/etc/opendkim/SigningTable:

*@example.com   mail._domainkey.example.com
*@example.net   mail._domainkey.example.net

Generate a key per domain, publish each public key, reload, and every domain signs with its own key. This pairs with the multi-domain mail setup in how to set up business email on a custom domain.

Test tools that save time

Beyond opendkim-testkey and Gmail's Show original, two habits catch problems early: send a message to mail-tester.com for a scored report that flags a broken DKIM alignment, and check the DNS record length, since a 2048-bit key exceeds one 255-character TXT string and some panels mangle the split. Confirm the published value round-trips:

dig mail._domainkey.example.com TXT +short

Compare it against mail.txt. And remember DKIM is only one leg — pair it with SPF, DMARC and clean rDNS from how to set up reverse DNS (PTR) for a mail server for mail that actually lands.

FAQ

What selector should I use for DKIM?

Any label works — mail, default, s1, or a dated one like 2026a for easy rotation. It just has to match between OpenDKIM's config and the DNS record.

Why is my DKIM signature failing at Gmail?

Common causes: the DNS p= value doesn't match mail.txt, the selector name is wrong, the TXT record got truncated, or OpenDKIM isn't actually signing. Run opendkim-testkey and check Show original.

How long should my DKIM key be?

2048-bit RSA is the current standard — strong and widely supported. 1024-bit still works but is weaker; go 2048 unless your DNS provider can't hold the longer record.

Do I still need SPF and DMARC with DKIM?

Yes. DKIM proves integrity and authorship; SPF authorises senders and DMARC ties them together with a policy. All three are needed for the best deliverability.

What key length should I use, and can my DNS host store it?

Use a 2048-bit RSA key — it's the current standard, strong enough for the foreseeable future and accepted everywhere. The one practical snag is that a 2048-bit public key is long enough to exceed a single DNS TXT string's 255-character limit, so the record must be split into multiple quoted strings within one TXT entry. Good DNS panels handle this automatically when you paste the value; weaker ones may truncate it, silently breaking DKIM. If your provider mangles the long record, either use a panel that supports split strings or, as a last resort, a 1024-bit key (weaker, but it fits). Always confirm the published record round-trips by comparing dig mail._domainkey.example.com TXT +short against the generated mail.txt file before relying on it.

Self-hosting mail means owning DKIM, rDNS and reputation — powerful but hands-on. Run it on a fast NVMe VPS with a static IP, or let Nxeon handle signing and deliverability with managed business email.

#dkim#postfix#opendkim#mail-server#deliverability#seobatch

Deploy your first server in under a minute

Creating an account is free and takes no card details. You pay when you deploy — choose a billing term and pay from your wallet or by card at checkout.