How often should you rotate SSH keys?

A rotation cadence for SSH keys, API keys and certificates that a small team will actually keep up with, and the three things that matter more than the interval.

· ssh, keys, rotation, runbooks

The short answer

For a small team without an ops person, this cadence holds up:

  • Shared and service keys: every 90 days. These are the keys in CI, on the deploy host, in the backup script. Nobody feels personally responsible for them, so they need a date.
  • Personal keys: yearly, if the key is tied to one person and lives in hardware (a YubiKey, a Secure Enclave, a TPM). A hardware-backed key can’t be copied, so the main risk left is the person leaving, and that has its own trigger.
  • Immediately on any suspected exposure or when the owner leaves the team. No schedule overrides this.

That is the whole answer to the question in the title. The rest of this guide is about why the interval is the least important part, and what to set up instead.

Why rotation exists

Rotation does two things.

First, it limits how long a leaked key stays useful. A key that was copied in March and rotated in June was a problem for three months. A key that was copied in March and never rotated is a problem for years, and nobody will know.

Second, and this is the one teams underrate, rotation forces you to prove you can replace a key before you have to. The first time you rotate the database key should not be the day it leaks. If the rotation goes wrong, you want it to go wrong on a Tuesday morning with coffee, not at midnight during an incident.

A cadence you don’t keep is worse than no cadence, because it looks like a process and isn’t one. Pick the longest interval you will actually meet.

Three things that matter more than the interval

1. Knowing which keys exist and who owns each one

You can’t rotate what you can’t list. Most small teams can name the obvious keys: the deploy key, the database password, the cloud access key for CI. The ones that cause trouble are the others: the read-only key a developer made for a dashboard two years ago, the SSH key on the bastion that nobody remembers adding, the API token for the email provider that lives in one person’s password manager.

Make the list before you set any schedule. For each key: what it is, where it is used, which environment, who owns it, when it was created or last rotated. If a key has no owner, assign one today. An unowned key won’t be rotated and won’t be revoked.

2. Being able to revoke in minutes

A key you can’t revoke quickly has no safe interval. If revoking the CI key means finding the one person who knows which three places it is pasted, then a 90-day schedule is theatre. The exposure window is not the interval; it is the time from “we think this leaked” to “it no longer works”.

Test this. Pick a low-stakes key, revoke it, and time how long it takes until the old one stops working everywhere. If the answer is more than an hour, that is the thing to fix before anything else.

3. A written procedure

The person who issued the key is often not the one who has to replace it. They are on holiday, or they left, or it is 2am and they are asleep. A rotation that lives in someone’s head is a rotation that happens only when that person is available and remembers.

Write the steps down. Not a policy document, a checklist: where the key is generated, where it is deployed, how you check the new one works, how you revoke the old one, where you record that it was done. The template later in this guide is one shape for that.

Cadence by key type

Key type Interval Trigger events Owner
Personal SSH key, hardware-backed Yearly Owner leaves, device lost or replaced The person
Personal SSH key, file on disk 6 months Owner leaves, laptop lost, key found in a repo or backup The person
Shared or service SSH key 90 days Anyone who knew it leaves, host compromised, key in a log A named developer, not “the team”
API key to a third party 90 days, or the vendor’s maximum if shorter Vendor breach notice, key in a repo, person who created it leaves Whoever owns the integration
Cloud access key (IAM user, service account) 90 days Anyone with access leaves, key in a repo or CI log Whoever owns the account
TLS certificate Before expiry. Let’s Encrypt issues 90-day certificates today and moves to 45 days by 2028; publicly trusted certificates are capped at 200 days since March 2026, dropping to 47 by 2029 Private key exposed, domain or host changes Whoever owns the domain

Two notes on the table. Cloud providers give you some of this for free: the AWS IAM console shows when each access key was last used, the AWS Config rule for key rotation defaults to 90 days, and Google Cloud recommends rotating user-managed service account keys every 90 days. Use that as a free reminder, but don’t rely on it: it only shows keys the provider knows about, which is not the same as the keys you have.

And TLS is the odd one out. If your certificates are issued automatically (Let’s Encrypt, a cloud load balancer, a managed CDN), rotation is already happening and the only job is to notice when it stops. The lifetimes are getting shorter across the industry, so manual renewal gets less workable each year. If they are issued manually, the expiry date is the deadline and your runbook should be due a month before it.

Triggers that override any schedule

The schedule is the default. These events replace it:

  • Someone leaves. Each key they held personally is revoked. Each shared key they knew the value of is rotated. This is a rotation, not a removal; the offboarding guide covers the ordering.
  • A laptop is lost or stolen. Any key that lived on it as a file, even behind a passphrase, is rotated. Hardware-backed keys on a separate device are the exception, which is a good argument for them.
  • A key shows up where it shouldn’t. A commit, a CI log, a Slack message, a screenshot, a support ticket. Treat it as exposed the moment you see it, even if the repo is private and you deleted the message. Rotate, then work out how it got there.
  • A vendor reports a breach. If a third party you hold a key with says their systems were accessed, rotate the key you have with them. You don’t need to wait for them to tell you to.

When a trigger fires, the runbook you wrote for the scheduled rotation is the one you run. That is the point of writing it.

What a rotation run looks like

Take a database key as the example: the credential your application uses to connect to the production database.

  1. Generate the new credential. Create a second database user, or a second password for the same user if your database supports it, with the same grants as the old one. Do not touch the old one yet.
  2. Deploy the new one. Update the secret in whatever your application reads it from: the environment, the secrets manager, the config file. Roll the application so it picks up the new value. If several services share the credential, do all of them.
  3. Verify. Check that the application is connecting with the new credential, not the old one. Most databases will show you active sessions and which user they belong to. Watch error rates for a few minutes. If anything is wrong, you still have the old credential and can roll back.
  4. Revoke the old one. Drop the old user, or expire the old password. This is the step people skip, because by now the work feels done and revoking carries risk. It is also the only step that actually ends the exposure window. If step 3 was honest, this is safe.
  5. Record it. Note the date, who did it, and anything that went differently from the written steps. Set the next due date.

The two credentials overlap for the length of steps 2 and 3. That overlap is what makes rotation safe to do during working hours. A rotation that goes “revoke old, create new, deploy” has no overlap and no rollback, and is why people fear the job.

Template: a rotation runbook

Copy this into wherever your team keeps runbooks, one per key. Fill the blanks; keep the headings.

# Rotate <key name>

**Owner:** <person, not a team>
**Backup owner:** <second person who can run this>
**Cadence:** every <90> days
**Last done:** <YYYY-MM-DD> by <who>
**Next due:** <YYYY-MM-DD>

## What this key is

- Type: <SSH key | API key | cloud access key | database credential | TLS cert>
- Used by: <service or job>
- Environment: <production | staging | bastion>
- Where the value lives: <secrets manager path | env var | CI variable>
- Where it is accepted: <host, service or vendor that checks it>

## Steps

1. Generate: <exact command or console path>
2. Deploy new: <where to update the value, and how to roll the service>
3. Verify: <how to see that the new value is in use>
4. Revoke old: <exact command or console path>
5. Record: update "Last done" and "Next due" above

## Rollback

<how to put the old value back if step 3 fails; only valid before step 4>

## Triggers besides the schedule

- Owner or anyone who knew the value leaves the team
- Value seen in a repo, log, message or ticket
- Vendor or host reports a breach

## Notes from previous runs

- <YYYY-MM-DD>: <what went differently>

The “Backup owner” line is not optional. A runbook only one person can run is a runbook that waits for that person.

Common mistakes

  • Rotating but never revoking. The new key is deployed, the old one is left “just in case”, and six months later there are four valid keys for one service. Revocation is the step that ends the exposure. Put it in the checklist and tick it.
  • Keys with no owner. “The team” owns nothing. If the owner field says a team name, nobody will do the run.
  • Rotation that needs the one person who set it up. If the steps say “ask Sam”, the procedure is Sam. Write down what Sam does, then have someone who isn’t Sam do the next run from the written steps.
  • A reminder in a personal calendar. It goes with the person when they leave, and it fires at the person rather than the team. Put the due date somewhere the team sees it and where the reminder goes to whoever owns the runbook now.
  • Treating the schedule as the goal. Ninety days is a default, not a result. The result is that you can list your keys, replace any of them in an hour, and have done so recently enough to trust the steps.

Where to start

Pick the key you would be most embarrassed to lose. Usually it is the production database credential or the cloud access key in CI. Write its runbook this week, using the template above, and run it once at a quiet moment. The first run takes longer than you expect and teaches you most of what the written steps got wrong. The second run is the one that takes ten minutes.