Every ssh key rotation best practices checklist floating around the internet assumes you already know two things: every key that has access to your infrastructure, and who is holding the private half of each pair. Most small teams have neither. A contractor’s key from eight months ago is still sitting in authorized_keys on three servers. Nobody remembers which server belongs to which project, let alone which key belongs to the contractor who left. That gap, not the rotation schedule itself, is the reason rotation gets skipped.
This guide covers the actual policy: how often to rotate, what triggers an out-of-cycle rotation, and how to keep a record that satisfies SOC 2 or ISO 27001 without hiring a dedicated security team. It’s one piece of a broader ssh key management guide for teams, worth reading first if you’re setting up key handling from scratch rather than fixing an existing mess.
How often should you rotate ssh keys?
For a key tied to a single person, 90 to 180 days is a reasonable baseline. For service accounts, deploy keys, and anything with root or admin-equivalent access, tighten that to 30 to 90 days. Regulated environments (finance, healthcare, government contracts) sit at the low end of both ranges; a two-person startup running a handful of VPS boxes can sit at the high end. The exact number matters less than picking one, applying it consistently, and pairing it with event-based rotation, which overrides the schedule entirely and rotates a key the same day something changes, not at the next quarterly review.
- User keys tied to one person: 90-180 days
- Admin or root-equivalent keys: 30-90 days
- Service and deploy keys: 30-90 days, ideally rotated by automation, not by hand
- Any key touched by someone who just left: immediately, regardless of schedule
Why ssh keys go stale in the first place
Rotation gets skipped for a boring reason: most teams don’t have a live inventory of which keys exist and where each one is authorized. A key added to ~/.ssh/authorized_keys on a production box two years ago doesn’t announce itself. It just keeps working, silently, until someone audits the file by hand and finds it.
Departed employees and forgotten contractors
The most common finding in any ssh access review is a key belonging to someone who no longer works there. SSH security writeups tend to focus on cipher choice and encryption strength, but the bigger real-world risk is access that was never revoked in the first place. If an offboarding checklist doesn’t include ‘remove this person’s ssh key from every host’, keys quietly outlive employment by months.
Shared bastion hosts and everyone-has-the-same-key sprawl
Bastion hosts and shared jump boxes tend to accumulate one key per person who has ever needed access, added by whoever had root at the time, documented nowhere. Rotating a key on a shared host without locking out everyone else requires the exact swap sequence covered in rotating ssh keys without downtime: add the new key, verify it works, then remove the old one, never the reverse.
What ssh key rotation best practices actually require
A rotation policy that holds up has three parts: an inventory, a set of triggers, and an algorithm standard. Skip any one of them and the schedule becomes theater, something written down and never checked.
Inventory first
You can’t rotate a key you don’t know exists. Before setting a schedule, pull every public key currently listed in every authorized_keys file across your infrastructure and map each one to a person or a service. This is tedious once and maintainable forever after, provided new keys get logged the moment they’re added instead of reconstructed later from memory.
A quick way to start that inventory across a handful of hosts, assuming a plain list of hostnames in a local hosts.txt and existing ssh access to each:
while read -r host; do
echo "== $host =="
ssh "$host" 'cat ~/.ssh/authorized_keys 2>/dev/null | awk "{print \$NF}"'
done < hosts.txt
The last field of each line in authorized_keys is the comment set at generation time with -C, typically a name or purpose. If that field is blank or unclear on keys you find, that’s a finding in itself: label every key when you create it, not after the fact.
Scheduled triggers vs event-based triggers
A calendar reminder covers routine rotation. It does nothing for a key that needs to die today: someone leaves the team, a laptop gets stolen, a key turns up in a public repository by accident. Event-based rotation has to bypass the schedule entirely and revoke access the same day the event happens, not at the next scheduled review.
Why ed25519 over RSA for new keys
Every key you rotate is a chance to upgrade the algorithm along the way. ed25519 keys are shorter, faster to verify, and avoid several implementation weaknesses that older RSA key sizes carry. Generate a new one with:
ssh-keygen -t ed25519 -C "name@host-purpose" -f ~/.ssh/id_ed25519_new
If a legacy device or service genuinely can’t support ed25519 yet, RSA at 4096 bits is the fallback, not 2048.
Automating the swap with a config management tool
Doing steps two through six of the rotation checklist by hand across a dozen hosts is exactly where most policies quietly stop happening. A config management tool handles the repetitive part: push the new public key to every host’s authorized_keys, wait for a confirmation window, then remove the old key in a second pass once every service has proven it switched over. An Ansible task for the push step looks like this:
- name: add new ssh key for rotation
ansible.posix.authorized_key:
user: deploy
state: present
key: "{{ lookup('file', 'keys/id_ed25519_new.pub') }}"
The removal task is the same module with state: absent and the old key, run only after verification confirms nothing is still authenticating with it. Puppet and Chef have equivalent resources; the sequencing matters more than which tool runs it.
How does ssh key rotation map to SOC 2, ISO 27001, and PCI DSS?
None of these frameworks use the phrase “ssh key” directly. They describe outcomes: credentials are rotated on a defined interval, access is revoked promptly once it’s no longer needed, and there’s a record proving both happened. Read through that lens, ssh keys are just another credential type auditors expect to see governed the same way as passwords or API tokens.
What auditors actually check
An auditor isn’t going to test whether your rotation interval is 60 or 90 days. They’re going to ask for a written policy, evidence the policy was followed (timestamps, tickets, commit history), and evidence that access was removed within a reasonable window of an employee’s last day. A documented 90-day cadence with proof of enforcement passes far more audits than an undocumented 30-day cadence nobody can prove ever happened.
In practice, PCI DSS’s access control requirements map to service-account keys with elevated privileges, ISO 27001’s access management controls map to the offboarding trigger, and SOC 2’s logical access criteria map to the inventory and audit trail. None of the frameworks care which specific ssh flag you passed. They care that a policy exists, is followed, and is provable after the fact.
An ssh key rotation checklist you can run this quarter
This is the minimum version. It assumes a handful of servers and no dedicated security team, which describes most of the teams that actually need this article.
- List every host and every key currently authorized on it.
- Generate a new ed25519 key pair for each person or service that needs one.
- Add the new public key to
authorized_keysalongside the old one. Don’t replace it yet. - Test the new key from a fresh connection before touching anything else.
- Update configs, deploy scripts, and CI secrets to reference the new key.
- Remove the old public key from every host once every reference is confirmed switched.
- Archive or securely delete the old private key. Don’t keep it ‘just in case.’
- Log the rotation date, the reason, and who performed it.
Steps three through six are exactly the sequence that avoids locking yourself out mid-rotation. The full walkthrough, including what to do when a host is unreachable during the swap, is in how to rotate ssh keys without downtime.
Common mistakes that undo a rotation policy
A rotation policy tends to fail less because the schedule was wrong and more because of a handful of repeatable mistakes:
- Removing the old key before confirming the new one works everywhere it’s needed, including cron jobs and CI runners that don’t log in interactively.
- Rotating the key but not the deploy scripts, config files, and secrets managers that still reference the old private key path.
- Treating rotation as a security-team task instead of assigning an owner per host or per service.
- Rotating on schedule but never wiring in the event-based trigger, so a departure or a leaked key still waits for the next quarterly cycle.
- Storing the new private key in the same place as the old one without deleting the old one, so a single compromise still exposes both.
Where manual rotation breaks down for small teams
Most of the content ranking for this topic assumes an enterprise PAM (privileged access management) deployment: a vault product, a dedicated security engineer, and a compliance tooling budget. That’s a reasonable setup for a 500-person org. It’s overkill, and often simply unavailable, for a five-person startup running a dozen VPS instances. The checklist above still holds, but the failure mode for a small team is different: not “the enterprise tool is misconfigured” but “nobody wrote any of this down, and the person who set up the servers left eleven months ago.”
The fix isn’t a five-figure vault contract. It’s a place to see, per host, which keys exist, what algorithm they use, and when each one was last rotated, kept current without depending on one person’s memory.
Keeping rotation sane without an enterprise vault
termique’s ssh key vault, built on the same end-to-end encryption model as its credential storage, records the fingerprint, algorithm, and creation date of every key you generate or import, so a rotation review doesn’t start from a blank spreadsheet. Because it’s built on Tauri rather than Electron, keeping it open alongside actual SSH sessions doesn’t add meaningful overhead. Per-command audit logs record which host a key was used against and when, useful context when deciding whether an old key is actually still in use before you revoke it.
Start with the inventory, pick one rotation interval, and let the tooling carry the parts that don’t need a human doing them from memory.