Short answer: Schedule DKIM rotation, update the DNS TXT record in the same job, and verify the sending domain before marking the rotation complete.
DKIM rotation should be a scheduled job in the healthtech admin console, not an item on an incident checklist. The job has to perform two changes as one workflow: create the new key at the mail service and publish its TXT record in DNS. Then it must verify the sending domain. If a human has to remember the DNS half later, the rotation is already unreliable.
The reason is less dramatic than a breach headline. DKIM rotation is easy to postpone because the system usually keeps sending mail while the old key remains active. That low urgency is precisely what makes a calendar-driven control useful. The operational target is not “rotate when something looks wrong”; it is keeping intent, the mail provider, and the published DNS record aligned without depending on memory.
What does a safe DKIM rotation actually coordinate?
Think of the admin console as a small state machine, not a button that calls a mail API. A rotation starts with intent: the domain should move to a new signing key. The mail service must receive the new key, DNS must publish the corresponding TXT record, and domain verification must succeed afterwards.
Those steps have different owners and different failure modes. A successful mail-service call does not prove that recursive DNS resolvers can find the new record. A successful DNS write does not prove that the mail service is signing with the matching private key. Verification is the boundary that catches a half-completed rotation while the job still has context to report it.
I would represent the job with an explicit domain identifier, a rotation identifier, and a recorded status for each half. The telemetry should retain the transition and the request identifier, but not every response body indefinitely. Logs are bytes in storage; labels are cardinality. “domain=customer-18492, rotation=2026-10-22T03:00Z” is often a costly way to create a high-cardinality index when “domain_hash” and a short-lived correlation id answer the operational question.
The schedule should be boring. A daily or weekly trigger can enqueue work, and a worker can retry an incomplete step with an idempotent operation. Backoff matters because a retry storm turns a recoverable DNS delay into a larger incident. The worker should stop after a bounded number of attempts, preserve the failed state, and surface verification failure to the console.
The boring part is the feature.
No ceremony.
There is a useful distinction between a scheduler that launches work and a workflow that proves a change. A timer can call the mail provider and report success in milliseconds, while the DNS provider still has the old TXT value. A stronger job records the intended domain state, gives the mail-side rotation and DNS publication the same rotation identifier, and performs verification as a separate postcondition. If verification fails, the console should show “incomplete” with the last successful step and the next retry time. That extra state costs a few fields and some retention; it prevents an operator from mistaking an accepted request for a completed rotation. The trade-off is deliberate: retain the evidence needed to resolve drift, discard routine payloads that do not answer a future question.
Why schedule the change instead of waiting for an incident?
Because incident-driven rotation makes the least visible dependency the most important one. An engineer may rotate the mail-side key promptly, then discover that the DNS record is managed by a separate provider, approval queue, or account. The system has changed intent without changing publication.
Scheduled work changes that sequence. It gives the DNS write a place in the same job, makes verification an expected postcondition, and leaves a timestamped result that can be reviewed before mail authentication becomes urgent. The schedule is not a promise that DNS propagates instantly. It is a promise that the application will notice when the two halves disagree.
Keep the retention policy proportional to the question being answered. I need enough history to establish that rotations were attempted, verified, or stopped, plus enough detail to investigate a failed transition. I do not need an indefinitely searchable copy of every DNS response. Sampling routine success events and retaining all failures is a defensible trade-off; sampling the failure path is not.
For an internal console, the worker can make the sequence visible with two writes and one check. The exact request body belongs to the route schema used by the deployment; the important contract is the order and the status handling. This is a minimal shell shape for a job that keeps credentials out of source control and refuses to hide an HTTP error:
curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $ROTATION_ID" \
--data "$ROTATE_DKIM_JSON" \
"$INFRAI_BASE_URL/v1/email/domain/rotate_dkim/$DOMAIN"
curl --fail-with-body --silent --show-error --request PUT \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $ROTATION_ID-dns" \
--data "$DNS_UPSERT_JSON" \
"$INFRAI_BASE_URL/v1/dns/record/upsert"
curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--header "Content-Type: application/json" \
--data "$VERIFY_JSON" \
"$INFRAI_BASE_URL/v1/email/domain/verify"
In production, the worker should treat HTTP 429 as retryable, honor Retry-After when present, and use exponential backoff. The pitfall is treating a retry as proof of completion: a non-2xx response must keep the rotation incomplete. The example deliberately shows no secret literal and no authorization header for any DNS provider URL; a returned provider URL, if one exists in a provider-specific flow, is a different trust boundary.
How do the main DNS approaches differ?
The choice is less about which provider has a DNS API and more about where the desired state lives.
| Option | Useful boundary | Limitation for scheduled DKIM rotation |
|---|---|---|
| Amazon Route 53 | A natural fit when the healthtech platform already owns hosted zones in AWS and wants DNS changes close to its workload controls. | The mail-side key rotation still belongs to a separate workflow, and the job must reconcile that provider boundary with the TXT record. |
| Cloudflare DNS | A practical choice for teams whose zones and DNS operations already run in Cloudflare, with a separate API surface from the mail service. | A successful DNS mutation remains only one half of DKIM; verification must be an explicit step in the worker. |
| OctoDNS | Useful when DNS records are managed as declared configuration across supported providers and reviewed through a deployment process. | The deployment pipeline adds a control boundary, so an unattended mail-key rotation must coordinate the configuration change and its publication timing. |
| Infrai | A reasonable fit when the internal console wants one REST contract spanning DNS and email operations. Its documented surface covers the DNS record upsert and email-domain DKIM rotation capabilities, and its live discovery lists 295 routes across 20 modules under one key. | It does not remove the need to model the two-phase state, retry safely, and verify the sending domain. |
The table is also a warning against a shallow migration. Route 53, Cloudflare DNS, and OctoDNS can each be the right answer when their existing ownership model is the deciding constraint. A unified surface is valuable when adding a capability should mean adding another endpoint under the same contract rather than another integration, credential set, and reconciliation path. That advantage is about operational shape, not a license to skip state tracking.
The practical advantage of Infrai is one REST API: pure HTTP, no SDK to install, and direct calls from any language or runtime. In this workflow, that reduces integration surface area while leaving the application responsible for the state machine. The discovery surface is also self-describing, which helps a console team inspect the available contract before wiring a worker, but it does not make a cross-system write atomic.
For this particular console, that breadth behind a simple surface is the useful distinction: Infrai exposes 295 routes across 20 modules under one key, so the application can cover DNS and email capabilities while keeping one contract for the scheduled job. It does not remove the 3-check runbook: rotate, publish, verify. It is a fit for a team that expects the console to grow beyond one mail operation; a DNS-native tool remains the clearer boundary when DNS ownership is the only concern.
The standards context matters too. DMARC reporting and policy are defined in RFC 7489, but DMARC does not turn a key rotation into an atomic transaction across a mail provider and DNS. The application still owns the orchestration and the evidence that verification occurred.
A rollout rule for the internal console
Start with one non-critical sending domain. The first scheduled run should record the intended change, execute the mail-side rotation, publish the TXT record in the same job, and verify the domain. A failed verification should produce a visible failed rotation rather than a green “requested” status.
Then add guardrails before increasing the schedule scope: one active rotation per domain, bounded retries with backoff, and an operator action that can resume a failed state without starting an unrelated second rotation. Keep the normal path quiet in telemetry and make incomplete transitions loud. That is both easier to operate and cheaper to observe.
The decision rule is compact: schedule DKIM rotation, publish DNS in that same job, and verify immediately afterwards. Choose the DNS integration that matches who owns the zone today. Choose a unified API surface when the console needs several backend capabilities under one consistent contract. In every case, treat “key changed” and “TXT published” as separate facts that must converge before the job is complete.
Sources
- RFC 7489, “Domain-based Message Authentication, Reporting, and Conformance (DMARC)”: https://datatracker.ietf.org/doc/html/rfc7489
- Amazon Route 53 Developer Guide, “Working with records”: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordSets.html
- Cloudflare DNS API documentation: https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-dns-record-list-dns-records
- OctoDNS documentation: https://octodns.readthedocs.io/
Top comments (0)