DKIM key rotation playbook — rolling keys without breaking inbound mail
DKIM keys should be rotated periodically — annually at least, quarterly if you're being thorough. The trick is doing it without breaking in-flight mail that's already been signed with the old key. This article walks the safe sequence.
Why rotate
- Reduces the impact window if a key is ever compromised.
- Forces you to verify the rotation process works — better to find the broken step on a calm rotation than during an incident.
- Some compliance frameworks (PCI, HIPAA) specifically require periodic key rotation.
The challenge: in-flight mail
Mail you sent yesterday might still be in some receiver's queue today. If you delete the old DKIM key the moment you publish a new one, that yesterday-mail fails DKIM check today and either gets rejected or spam-folder.
The fix is to overlap: keep both keys valid in DNS for a window long enough to drain in-flight mail.
The sequence
Step 1 — Pick a new selector
DKIM keys are identified by a selector, the prefix on the DNS record name. If your old key is at 2025._domainkey.example.com, your new key goes at 2026._domainkey.example.com.
Common naming conventions:
- Year-based:
2025,2026. - Year-quarter:
2026q2. - Date-based:
20260618.
Whatever you pick, don't reuse a selector — once retired, retire it permanently. Reuse causes confusion in DKIM-history caches.
Step 2 — Generate the new key pair
On the mail server:
cd /etc/opendkim/keys/example.com
opendkim-genkey -b 2048 -s 2026 -d example.com
chown opendkim:opendkim 2026.private
chmod 600 2026.private
2048-bit RSA is the standard. 4096-bit is fine but the resulting DNS record is large and some DNS hosts won't accept it without splitting.
Step 3 — Publish the new public key in DNS
The generated 2026.txt file contains the DNS record to publish. Add it as a TXT record at 2026._domainkey.example.com.
Do not yet delete the old key's DNS record. Both should be in DNS at this point.
Wait for propagation — minutes to an hour depending on TTL. Verify:
dig +short TXT 2026._domainkey.example.com
dig +short TXT 2025._domainkey.example.com
Both should resolve.
Step 4 — Switch the signing config
In OpenDKIM's config, change the selector and key file references:
# /etc/opendkim.conf
Selector 2026
KeyFile /etc/opendkim/keys/example.com/2026.private
Reload OpenDKIM:
systemctl reload opendkim
From this moment, outbound mail is signed with the new key. The DNS record for the new key validates the signature.
Step 5 — Verify
Send a test email to a Gmail account, view the message source, look for the DKIM signature header:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=example.com; s=2026; ...
The selector should be the new one. The dkim=pass line in the Authentication-Results header confirms verification succeeded.
Step 6 — Wait the overlap window
Leave the old key's DNS record published for at least 7-14 days. This covers:
- Mail you sent before the rotation that's still in delivery queues.
- Mail that's being delivered to receivers with delayed SMTP retry behaviour.
- Spam filters that re-evaluate signatures over a few-day window.
14 days is conservative; 7 is the minimum safe.
Step 7 — Retire the old key
After the overlap window:
- Delete the old TXT record from DNS.
- Move the old private key file out of the working dir (don't delete; archive it for forensic purposes for at least a year).
Mail from before rotation that's still floating around now fails DKIM — but by this point, nothing legitimate should still be in flight.
Multiple domains
If OpenDKIM signs for multiple domains, you rotate each independently. The KeyTable / SigningTable mechanism in OpenDKIM lets you have different selectors per domain at the same time during a phased rotation.
Automating the rotation
For ongoing rotation, automate the steps:
- Cron or systemd timer triggers the rotation script every quarter.
- Script generates new key, publishes via DNS API (most DNS hosts have one), updates OpenDKIM config, reloads daemon.
- Two scheduled cycles later, an "old key retire" task removes the previous-previous key.
Sample structure:
# Every quarter
generate-new-key-and-publish.sh
sleep 24h
switch-signing-to-new-key.sh
# Every quarter, lagging by one cycle
retire-previous-quarter-key.sh
Test the automation in non-production first. A bug in rotation can silently break all outbound mail.
If something goes wrong
- Mail starts failing DKIM after rotation. The new public key in DNS doesn't match the private key being used to sign. Verify the DNS record was published correctly — typos in the long base64 string are easy.
- "No DKIM signature" on received messages. OpenDKIM isn't signing — check
journalctl -u opendkim. - "DKIM signature did not verify." Likely a key/selector mismatch or DNS propagation delay.
The safest revert: change OpenDKIM back to the previous selector (the old key's DNS record is still there) and reload. Outbound mail is now signed with the old key again, validation works against the old DNS record, and you can investigate the new key issue calmly.
Also Read
Powered by WHMCompleteSolution