Rotation That Applications Survive
Old, then new alongside old, then a transition window, then old revoked — and the application must tolerate the change, because one that reads a secret at boot and caches it forever breaks the moment you rotate.
The question, the obvious approach, and why it breaks
Every lesson starts where the work starts: an operational problem, a first attempt that is entirely reasonable, and the way production disagrees with it.
How do you change a credential that is in use by a running fleet without an outage?
A credential must change — on a schedule, after a suspected disclosure, when someone leaves — and every consumer of it is running right now, holding the old value.
Change the password in the database and update the value in the secret store. The services will pick it up.
Processes that read the secret at startup are still holding the old value, and they will keep using it until they restart — which may be days.
- Processes that read the secret at startup are still holding the old value, and they will keep using it until they restart — which may be days.
- The moment the old credential stops working, every one of those processes starts failing at once, fleet-wide, with no deploy on the timeline to explain it (Change Correlation).
- Connection pools hold authenticated connections. New connections fail while existing ones keep working, so the failure ramps in gradually and looks like a flaky dependency (The Connection Budget).
- Consumers you did not know about — a batch job, a reporting tool, a partner integration, a script on someone's machine — fail later, at whatever time they next run.
- Rolling restart as a workaround is a fleet-wide disruption for what should have been a background change, and it does not help the consumers you did not enumerate.
What is actually happening
Underneath the tooling, which is the part that survives a change of tool.
- Safe rotation is an overlap, and it requires the target system to accept two valid credentials at once. The four phases are: old is valid; new is created and both are valid; consumers transition to new during a window; old is revoked (The Secret Lifecycle).
- The overlap window is what removes the coordination problem. Nothing has to change at the same instant, so a consumer that picks up the new value late is fine — as long as it does so before revocation.
- The application's obligation is the half that gets forgotten. An application that reads a secret once at boot and caches it forever cannot be rotated without a restart. Tolerating rotation means re-reading on a bounded interval, and re-authenticating when a rejection is received (Build-Time and Runtime Configuration).
- Revocation is the step that must not be skipped and is skipped constantly, because everything works after the transition and nobody wants to touch it. An un-revoked old credential means the rotation achieved nothing for the case that motivated it.
- Emergency rotation after a suspected disclosure has no overlap: the old credential must die now, and the outage is accepted. That is a different procedure and it should be rehearsed separately (What Happens Between the Page and the Postmortem).
- Some credentials cannot overlap at all. A system that supports exactly one password per user forces either a second account or a hard swap with downtime — and knowing which of your dependencies are like this, in advance, is most of the preparation.
Four phases, and the one that gets skipped
Written out, the procedure looks obvious. In practice phases two and three blur together and phase four is deferred until it is forgotten — which means a rotation prompted by a suspected disclosure left the disclosed credential valid.
- 1Old valid
The steady state. Every consumer uses the current credential.
fails by Not knowing who the consumers are, which is the usual starting condition.
evidence The store's read audit and the target system's per-credential usage list the actual consumers (Audit Logs for Privileged Actions).
- 2Create new, both valid
Issue a second credential at the target system; write it to the store as a new version.
fails by The target accepting only one credential, discovered at this step rather than during planning.
evidence Both credentials authenticate successfully, tested explicitly.
- 3Transition
Consumers pick up the new value as their cache expires or on their next re-read.
fails by Applications that cache for the process lifetime, so nothing transitions until a restart.
evidence Usage of the new credential rises while usage of the old falls — two curves, not an assumption.
- 4Verify adoption
Confirm old-credential usage has reached zero and stayed there through a full cycle, including nightly jobs.
fails by Waiting a fixed period instead of observing, so a weekly job is missed entirely.
evidence Zero usage of the old credential across a period longer than your longest job interval.
- 5Revoke old
Delete or disable the old credential at the issuer.
fails by Skipping it, which is extremely common and negates the rotation.
evidence The old credential is rejected when deliberately tested.
- 6Record
Update the inventory with the rotation date and the new version.
fails by No record, so the next rotation starts with the same enumeration problem.
evidence The inventory's last-rotated date moved.
Phases one to four are reversible; phase five is not. Gate revocation on the evidence from phase four rather than on elapsed time, and expect the stragglers to be scheduled jobs rather than services (Cron Jobs in Production).
What "the application must tolerate rotation" means in code
This is the obligation on the application side, and it is the difference between a rotation that is a non-event and one that requires a fleet restart. Two behaviours are needed: a bounded cache, and re-authentication on rejection.
1// Wrong: correct for the life of the process, and only that.2const dbPassword = await secrets.get('db/app') // read once at boot3const pool = createPool({ password: dbPassword }) // captured forever4 5// Right: the current value is always fetched through something6// that knows how to get a newer one.7const password = cached(() => secrets.get('db/app'), { ttl: '5m' })8 9const pool = createPool({10 // Called per new connection, not once at construction.11 credentials: async () => ({ password: await password.get() }),12})13 14// And on rejection, invalidate rather than retrying the same value.15pool.on('authError', async () => {16 password.invalidate() // next get() goes to the store17 await pool.reconnect() // bounded, with backoff18})Three properties, none of them about the secret store. The TTL bounds how long a rotation takes to propagate. The per-connection callback means pool connections created after a rotation use the new value without a restart. And invalidate-on-rejection turns the worst case — a rotation that propagated late — from an outage into a few seconds of retries. Without the third, a fleet whose cache has not yet expired fails for the full TTL (When Secrets Fail).
A rotation that revoked too early
The overlap did its job and the revocation was gated on a clock rather than on evidence. Note how long it takes for the cause to be found, and why: there was no deploy, so the first place anyone looked showed nothing.
- Mon 10:00changeNew credential created at the database. Both old and new valid. New version written to the secret store.
- Mon 10:05signalServices with a 5-minute cache TTL begin using the new credential. Old-credential usage falls steadily.
- Mon 10:30signalOld-credential usage from services reaches zero. The dashboard looks finished.
- Tue 10:00changeAutomation revokes the old credential — 24 hours elapsed, as configured. No check on actual usage.
- Tue 10:00signalNo effect. Every service is on the new credential. The rotation is recorded as successful.
- Sun 02:00changeThe weekly reconciliation job starts. It reads its credential from a mounted file written at deploy time, six weeks ago.
- Sun 02:00signalAuthentication refused. The job retries three times, exits non-zero, and alerts to a channel that is quiet at 2am on a Sunday.
- Mon 09:15signalA finance query returns figures that are a week stale. Someone asks in a channel.
- Mon 10:40actionThe failed job is found. The deploy timeline shows nothing for the previous six weeks, so the change is not obvious (Deploys on the Same Timeline as the Symptom).
- Mon 11:20actionThe store's read audit shows the job never fetched the new version — it reads a file, not the store.
- Mon 11:50recoveryJob redeployed to fetch from the store with a TTL. Backfill run for the missed week.
The overlap worked exactly as designed. What failed was gating revocation on elapsed time rather than on observed non-use — with a consumer whose interval was longer than the window. A usage check before revocation would have shown a non-zero count from an identity nobody had enumerated (Job Scheduler Reliability).
How to do it properly
Most important first.
- Design for two valid credentials before you need to rotate: two database users, two API keys, two signing keys with a key identifier in the payload.
- Make applications re-read: fetch from the store with a bounded TTL, and re-fetch immediately on an authentication rejection rather than only on a timer (Secret Managers and What They Actually Give You).
- Verify adoption before revoking. Use the store's access log or the target system's per-credential usage to confirm the old value is no longer in use, rather than assuming the window was long enough (Audit Logs for Privileged Actions).
- Rotate on a schedule when nothing is wrong. A rotation path exercised quarterly works during an emergency; one that has never run does not (Restore Drills).
- Enumerate consumers before rotating, and expect the list to be incomplete — the store's read audit is a better source than anyone's memory.
- Prefer credentials that rotate themselves: short-lived, automatically issued credentials remove the whole procedure (Workload Identity).
- Write the revocation into the plan as a dated step with an owner, because it is the step that quietly does not happen.
How much can this affect
Every production change has a blast radius. Stated as a scale so it is comparable between changes rather than adjectival — and paired with what actually contains it, because a wide scope with a real containment mechanism is a different situation from a wide scope with none.
Contained well by the overlap window, which is the entire design: during it, a consumer that has not transitioned still works. Contained by nothing at revocation — that step is simultaneous, fleet-wide and irreversible, which is why it must be gated on evidence rather than on a timer.
What can go wrong
- An application that caches the value for the process lifetime, so rotation appears to work and the fleet fails at revocation.
- Revocation skipped, leaving the old credential valid indefinitely — very common, and it means a rotation prompted by a disclosure did not address the disclosure.
- A connection pool that authenticates once per connection, so the failure appears gradually as connections are recycled and is misdiagnosed as intermittent.
- A target system that does not support two valid credentials, discovered mid-rotation.
- A forgotten consumer — a nightly job, a partner, a dashboard — failing hours or weeks later with no obvious cause.
- Rotation automation that rotates and revokes in one step, converting a routine change into a fleet-wide outage (The Automation Trap).
- Rotating a signing key without a key identifier, so tokens or artifacts signed with the old key become unverifiable at revocation (Signing and Verifying Artifacts).
- "Rotation means changing the password." Changing it is one of four steps. Creating the new one, transitioning consumers, and revoking the old one are the rest, and the last is the one that gets skipped.
- "The application will pick it up." Only if it re-reads. A value read at boot and held in a variable is fixed for the process lifetime no matter what the store says.
- "We rotate annually, so we are compliant." Annual rotation of a credential that cannot be rotated without a fleet restart means the procedure has been exercised once and will be improvised the next time it is urgent.
- "Automated rotation means we do not need to think about it." It means creation and distribution are handled. Whether applications tolerate it, and whether revocation actually happens, are still design questions with your name on them.
- "Short-lived credentials are the same as frequent rotation." They are rotation made continuous and automatic, which removes the procedure rather than accelerating it (Short-Lived Credentials).
Operating it
Evidence is the signal, not the intention. Rollback is sometimes 'you cannot, and that is the point'.
- A rotation has been performed with no error-rate change and no restart, and the graph is available to show it.
- The old credential's usage count reached zero before revocation, observed rather than assumed.
- Every credential in the inventory has a last-rotated date, and the oldest is within policy.
- A deliberate rotation in a lower environment does not require a deploy for the application to pick up the new value.
- During the overlap phase, rollback is trivial: the old credential is still valid, so consumers that fail on the new one keep working.
- After revocation, there is no rollback. The old credential is gone at the issuer, and recovery means issuing another new one and getting it to every consumer under time pressure.
- That asymmetry is why revocation is the gated step: everything before it is reversible and it is not (Rollback: Only Useful If It Is Actually Safe).
- For a signing key, revocation can invalidate things already signed. Retain the public half for verification even after the private half is retired, or plan the re-signing explicitly (Certificate Trust Chains).
- Automate creation, distribution and adoption tracking. These are mechanical and error-prone by hand (Toil).
- Automate scheduled rotation for credential types the provider supports natively — it is the only way rotation actually happens at a regular cadence.
- Automate the reminder and the evidence for revocation: which consumers still use the old credential, updated continuously.
- Do not automate revocation on a timer alone. Gate it on observed non-use, or on a human confirming, because an automatic revocation with a straggler consumer is a self-inflicted outage.
- Overlap means two valid credentials exist for a period, which is a wider window of exposure — the trade is deliberate and usually correct, but it should be short.
- Re-reading logic in every application is real work spread across every team, and it is invisible until the day it matters.
- Frequent rotation increases the chance a rotation itself causes an incident, which is an argument for making the path boring and automated rather than for rotating less.
- Dynamic short-lived credentials remove the procedure entirely and add a hard dependency on the issuer being available for every new connection.
Where this applies
This domain is unusually tool- and organisation-dependent. These labels say what each claim is specific to, and what a different platform, provider or organisation does instead.
- GENERALThe four-phase overlap applies to any credential the target system will accept two of. Where it does not — a system with a single password field — the alternatives are a second account or accepted downtime, and knowing which of your dependencies are in that category is preparation you do before the emergency.
- CLOUD-SPECIFICManaged rotation coverage differs sharply: some providers rotate their own database and API credentials on a schedule with the overlap handled for you, others provide a hook where you supply the rotation logic, and third-party credentials are usually entirely yours to manage. Do not assume a provider that rotates one credential type rotates another (Secrets in Infrastructure).
- TOOL-SPECIFICWhether an application sees a rotated value without a restart depends on the client library, not the store. Some database drivers accept a credential provider callback and re-authenticate on reconnect; others take a connection string once at pool construction and never ask again. Check the driver before promising rotation without restarts.
Where the depth lives
This domain teaches delivery and operation, and hands the mechanism off to the domain that owns it.
- — Testing & Reliability Engineering — rehearsing rotation as a scheduled drill, so the emergency version of the procedure is not the first time it runs.