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.

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.

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:
- 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
- Publish the new
2026b._domainkeyTXT record and confirm it resolves. - Switch OpenDKIM to sign with
2026b(updateSelectoror the SigningTable) and reload. - 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.