Skip to main content

HTTP monitors, white-label, custom domain. Live in 3 minutes.

Monitors & groups

  • Unlimited HTTP monitors
  • Group by service or region

100% customizable

  • Logo & color palette
  • Title & SEO meta
  • Free-form content
New New feature

White-label

  • No CaptainDNS mention
  • Your domain via CNAME
  • Automatic TLS

Real-time & history

  • In sync with your monitors
  • 30-day history
  • Incidents & maintenance

DKIM key rotation: operational runbook (D-7 to D+30)

By CaptainDNS
Published on May 19, 2026

Operational timeline for rotating a DKIM key in production over 37 days (D-7 to D+30)
TL;DR
  • Recommended frequency: 6 to 12 months in standard production, 3 months in critical environments (banking, public sector)
  • Zero-downtime strategy: two selectors in parallel, 7 days before cutover, 30 days after retirement
  • Algorithms: RSA 2048 remains the compatible default, Ed25519 in double-publish for modern MTAs
  • Storage: private key in KMS (AWS, GCP, Vault), never in cleartext in a Git repo
  • Automation: cron every 3 to 6 months, post-rotation validation via DMARC aggregate reports

DKIM key rotation is a recurring operation that every mail-sending organisation has to plan. Vendor guides (Microsoft, AWS, SendGrid) describe the "rotate" button without explaining the overlap window, DNS propagation delay or rollback procedure. This runbook fills that gap with a calendar timeline from D-7 to D+30, tested openssl scripts and DKIM key rotation best practices reported from the field.

For the general reference, see the complete DKIM guide. To locate your active selectors before you start, the how to find a DKIM selector walk-through helps. Here, the goal is operational: generate the key pair, publish the selector, switch signing, monitor DMARC, retire the old selector. Target audience: sysadmins, DevOps, SRE and infrastructure teams running production mail in any environment.

Audit your DKIM selectors before rotation

Why DKIM key rotation matters

Three operational reasons justify periodic DKIM key rotation.

Key compromise. A DKIM private key always ends up leaking: unencrypted backup, legacy KMS access from a former employee, dump from a SaaS provider. Rotation limits the exploitation window of a leak.

Cryptographic limit. RFC 8301 deprecated RSA keys below 1024 bits back in 2018 and recommends RSA 2048 as a minimum. An RSA 1024 key published in 2018 is now rejected by Google, Yahoo and Microsoft. Regular rotation forces replacement of legacy algorithms.

Cryptographic hygiene. NIST SP 800-57 Part 1 Rev. 5 recommends short cryptoperiods for signing keys: 1 to 2 years maximum, shorter for sensitive environments. Without regular rotation, the first forced rotation turns into a disaster.

DKIM2, currently being standardised at the IETF, adds replay protection but does not remove the need for DKIM key rotation. Therefore, plan rotations now and adjust the cadence once DKIM2 ships. See DKIM2 and replay protection. If your monitoring already shows recurring failures, the DKIM fail causes and fixes playbook complements this runbook.

DKIM key rotation strategy: parallel selectors

Fundamental principle of any safe DKIM key rotation: never replace a DKIM key by overwrite. Always publish a new selector in parallel, switch signing, then retire the old selector after a grace period. In practice, this is how teams rotate DKIM keys without downtime.

Three factors require temporary coexistence:

  • DNS resolver cache. The TXT value of a selector is cached for the TTL (1 hour to 24 hours). Some resolvers still serve the old value.
  • SMTP retry. A receiving MTA keeps a message in queue for up to 5 days. If the selector is retired too early, re-verification fails.
  • Forensic audit. Security teams must verify the signature of a message received 2 weeks earlier. A retired selector makes such verification impossible.

Naming convention: quarterly dated selectors. Example: s2026-q1 for January to March 2026, s2026-q2 for the following one. This convention avoids collisions with vendor selectors (selector1 Microsoft 365, google Google Workspace, mte1 SendGrid). The two selectors coexist during the overlap window:

s2025-q4._domainkey.captaindns.com.  3600  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOC..."
s2026-q1._domainkey.captaindns.com.  3600  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOC..."

Minimum overlap duration: 7 days on the propagation side, 30 days on the retirement side to cover the longest SMTP retries.

DKIM key rotation timeline in production

6-step calendar procedure to rotate a DKIM key in production without downtime
  1. Generate the new pair (see next section), store the private key in KMS, publish the public key under the new selector in DNS without enabling signing on the MTA side. The 7-day window covers DNS propagation and allows an integrity check before cutover.

    dig +short TXT s2026-q1._domainkey.captaindns.com
    

    The old selector still signs all outbound messages. Verify the published content with the DKIM Record Check.

  2. Reconfigure the MTA to sign with the new selector. The switch must be atomic: avoid signing alternately with both keys, which complicates DMARC auditing.

    On Postfix combined with OpenDKIM, modify KeyTable and SigningTable, then reload. On SES BYODKIM, call aws sesv2 put-email-identity-dkim-signing-attributes. On Microsoft 365, the Rotate-DkimSigningConfig cmdlet handles the switch automatically.

  3. For 24 hours, parse the DMARC reports. The auth_results/dkim/selector field must reflect the new selector for 100% of legitimate traffic. Any trace of the old selector indicates a misconfigured flow. Alert threshold: 1% of residual traffic on the old selector after 48 hours.

  4. Aggregate DMARC reports from the last 7 days. If the new selector covers 100% of traffic, proceed to D+8. Otherwise, extend the window and investigate the residual flows (internal cron jobs, legacy applications, third-party provider).

  5. Disable any technical ability to sign with the old selector. The public TXT remains published to allow verification of messages still in retry queue.

    On OpenDKIM, remove the corresponding line. On Vault, revoke the read lease on the old key to prevent any accidental rollback.

  6. Final deletion of the TXT record. The 30-day delay covers SMTP retries (up to 5 days) and exotic DNS caches. After deletion, verify that dig +short TXT s2025-q4._domainkey.captaindns.com returns an empty response. Destroy the corresponding private key in KMS.

DayActionOld selectorNew selector
D-7Publish the TXT of the new selectorSigns and publishedPublished (inactive)
D+0Switch MTA signingPublishedSigns and published
D+1Monitor DMARC reportsPublishedSigns and published
D+7Validate 100% traffic on the newPublishedSigns and published
D+8Remove MTA signing for the oldPublished (frozen)Signs and published
D+30Delete the old TXTDeletedSigns and published

Operational DKIM rotation timeline from D-7 to D+30 with two parallel selectors

Generate the DKIM key pair with openssl

Four openssl commands cover every DKIM key rotation need. All were tested with OpenSSL 3.x. Use them in any rotation script, regardless of the receiving provider.

RSA 2048: private key generation.

openssl genrsa -out dkim_private.pem 2048

RSA 2048: extraction of the base64 public key for DNS.

openssl rsa -in dkim_private.pem -pubout -outform der | openssl base64 -A

The result is the value to paste into the p= tag of the TXT record.

Ed25519: private key generation.

openssl genpkey -algorithm ed25519 -out dkim_ed25519_private.pem

Ed25519: extraction of the base64 public key (32 bytes).

openssl pkey -in dkim_ed25519_private.pem -pubout -outform der | tail -c 32 | openssl base64

The tail -c 32 is necessary: openssl emits a 12-byte ASN.1 header in front of the actual 32 bytes of the Ed25519 key, a header that RFC 8463 requires to strip out for p=.

Comparison table of the three realistic options in 2026:

AlgorithmPublic key sizeSignature size2026 supportTXT split risk
RSA 2048~392 characters256 bytesUniversalLow (1 string)
RSA 4096~736 characters512 bytesUniversalHigh (3 TXT strings)
Ed25519~44 characters64 bytesPartial (Google, Fastmail, open source MTAs)None

2026 recommendation: RSA 2048 alone for maximum compatibility, or double-publish RSA 2048 plus Ed25519 to benefit from Ed25519 signing at compatible receivers. Avoid RSA 4096: the public key exceeds 255 characters and forces a TXT split into multiple strings, fragile at every DNS edit. For the comparative detail, see RSA vs Ed25519 for DKIM.

DKIM private key management

The DKIM private key is a cryptographic secret on par with a TLS key. Therefore, treat it as a Tier-0 asset across the DKIM key rotation lifecycle. Three acceptable storage models exist in production.

Managed KMS (AWS KMS asymmetric keys, GCP Cloud KMS, Azure Key Vault). The key is generated and remains in the HSM. The MTA calls the KMS API to sign each message. The private key is never exposed in application memory, rotation is tracked through IAM. Drawback: 5 to 20 ms of latency per call.

HashiCorp Vault Transit. Self-hosted equivalent of the managed KMS. The key stays in the Transit backend, the MTA signs through the API. Conventional path: secret/dkim/captaindns/s2026-q1.

Encrypted PEM file at rest. Minimum acceptable for small infrastructures. The key is stored on the sending server in an encrypted volume (LUKS, encrypted EBS), permissions 0600, ownership opendkim:opendkim. Deployment via Ansible Vault, Sealed Secrets for Kubernetes or SOPS.

Anti-patterns to ban:

  • Private key committed to the Git repo, even on a private branch. Git keeps history forever.
  • Private key passed as an unencrypted environment variable (DKIM_PRIVATE_KEY=...) in a docker-compose or .env file.
  • Private key stored in a secret manager shared with unnecessary service accounts (least privilege principle violated).

At rotation time, do not forget to rotate KMS access too. A former employee who had read access to secret/dkim/captaindns/s2025-q4 must not retain that access even if the key is no longer active: it remains valid for signing backdated messages.

DKIM private key storage architecture with managed KMS, Vault Transit and encrypted PEM file

Automate DKIM key rotation via cron

A manual rotation is forgotten at the first team vacancy. Therefore, automation is essential beyond 3 to 4 active domains. Split the DKIM key rotation script into 3 idempotent steps:

  1. Generate: produce the pair and store the private key in KMS.
  2. Publish-DNS: call the DNS provider's API to add the TXT.
  3. Switch-signing: reconfigure the MTA after the D-7 window.

Bash skeleton Generate + Publish-DNS on Cloudflare:

#!/bin/bash
set -euo pipefail

SELECTOR="s$(date +%Y-q%q)"
DOMAIN="captaindns.com"
ZONE_ID="${CLOUDFLARE_ZONE_ID}"

# Generate RSA 2048
openssl genrsa -out "dkim_${SELECTOR}.pem" 2048
PUB=$(openssl rsa -in "dkim_${SELECTOR}.pem" -pubout -outform der | openssl base64 -A)

# Store private key in Vault
vault kv put "secret/dkim/${DOMAIN}/${SELECTOR}" private_key=@"dkim_${SELECTOR}.pem"

# Publish DNS via Cloudflare API
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
  -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"TXT\",\"name\":\"${SELECTOR}._domainkey\",\"content\":\"v=DKIM1; k=rsa; p=${PUB}\",\"ttl\":3600}"

# Verify publication
sleep 30
dig +short TXT "${SELECTOR}._domainkey.${DOMAIN}"

Cron: 0 3 1 */6 * (every 6 months, on the 1st of the month at 3am UTC) triggers the Generate plus Publish-DNS phase, followed 7 days later by a second cron that runs Switch-signing.

Vendor integrations:

  • AWS SES BYODKIM: command aws sesv2 put-email-identity-dkim-signing-attributes with the DomainSigningSelector and DomainSigningPrivateKey pair encoded in base64.
  • Microsoft 365: Rotate-DkimSigningConfig -Identity captaindns.com -KeySize 2048 (Exchange Online PowerShell). The rotation generates a new selector on the Microsoft side but requires publishing the new CNAME in DNS to activate the new key.
  • SendGrid: rotation is managed via the POST /whitelabel/domains/{id}/validate API after adding the new CNAME at the DNS provider.

Recommended alerting: trigger an alert if the dkim=fail rate in DMARC aggregate reports exceeds 1% during the D+0 to D+7 window. In practice, post-rotation validation runs programmatically via the DKIM Record Check API. Pair the cron with a CI gate that fails the build if a domain ships unsigned. See DKIM in CI for the full pipeline.

DKIM rotation automation flowchart with cron, KMS and DNS API

Common DKIM key rotation pitfalls to avoid

Five recurring DKIM key rotation pitfalls show up in support tickets and reddit r/sysadmin threads. In practice, they account for the majority of post-rotation incidents.

TTL too long on the old selector. A 24-hour TTL prevents rapid propagation of a fix. Lower the TTL to 300 seconds 48 hours before D+0, then raise it back to 3600 seconds after D+7.

Selector with misplaced hyphens. Some strict DKIM parsers reject a selector with a hyphen in first or last position. Prefer s2026q1 to -s2026-q1-.

DMARC in strict alignment (adkim=s). A d=mail.captaindns.com signature is rejected for a From: contact@captaindns.com. Verify that the d= domain of the new key remains identical to that of the old one.

Public key hidden on the SaaS side. Mailchimp, ActiveCampaign and others publish a CNAME rather than a direct TXT. Rotation is handled by the provider: validate that the CNAME points to the new target after the declared rotation. For the provider-specific procedure, see DKIM on Microsoft 365 and Google Workspace.

s= tag misconfigured. The optional s=email tag restricts the use of the key to signing email messages. A key published with s=* or without the tag accepts all services. For the detail, see the s= tag in the DKIM record.

Example of a poorly encoded TXT (base64 wrap multi-line with raw newline, rejected by some resolvers):

s2026-q1._domainkey.captaindns.com.  IN  TXT  ( "v=DKIM1; k=rsa; "
                                                "p=MIIBIjANBgkqhkiG\n"
                                                "9w0BAQEFAAOC..." )

Correct example (single string or clean split into chunks of 255 characters):

s2026-q1._domainkey.captaindns.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."

FAQ

FAQ

What is the recommended DKIM key rotation frequency?

The recommended DKIM key rotation frequency is 6 to 12 months in production for a standard organisation, and 3 months in critical environments (banking, public sector). RFC 6376 sets no duration but NIST SP 800-57 recommends short cryptoperiods for signing keys. Beyond 18 months, the risk of leakage (backups, former employees, legacy KMS access) outweighs the operational cost of a clean rotation.

What to do if DMARC reports show dkim=fail during rotation?

First check that the new selector is published with dig +short TXT s2026-q1._domainkey.captaindns.com, then validate the signature with a DKIM record checker. If the old selector was retired too early, re-publish it with the same p= and wait 7 days. Resolver caches (up to 24h) and SMTP retries (up to 5 days) explain the majority of failures.

How do you rotate a DKIM key without downtime?

Yes, you can rotate DKIM keys without downtime and it is the only acceptable mode in production. The strategy relies on two parallel selectors: the new one published 7 days before cutover, the old one kept for 30 days after. During the overlap, both signatures are valid. Thus, no outage occurs and no message is rejected for dkim=none.

How to rotate in a multi-provider environment (M365, AWS SES, SendGrid)?

Each provider manages its own DKIM selector, so the rotations are independent but must be coordinated. Procedure: 1) list all active selectors via a DNS audit, 2) schedule the rotations staggered by fortnight to isolate sources of failure, 3) verify that selector1._domainkey (M365), s2026-q1._domainkey (SES BYODKIM) and s1._domainkey (SendGrid) coexist without collision.

RSA 2048, RSA 4096 or Ed25519: what to choose in 2026?

RSA 2048 remains the compatible default. Ed25519 is safer and faster but requires a double-publish (k=rsa and k=ed25519) because some receivers still do not support it. RSA 4096 produces a base64 string longer than 255 characters that forces a TXT split, fragile at every edit. Recommendation: RSA 2048 alone, or RSA 2048 plus Ed25519 in double-publish for high-traffic domains.

Download the deployment checklist

Assistants can reuse the checklist via the JSON or CSV exports below.


Validate your next DKIM key rotation: use the DKIM Record Check to verify that your new selector is published and signed correctly before and after cutover. Therefore, every D-7, D+0 and D+30 milestone gets a deterministic green/red gate. To anticipate protocol evolutions, see also DKIM2 and replay protection.


Sources

Similar articles