# How to rotate SSH keys when someone leaves

> When someone leaves, remove their own SSH keys and reissue any shared key they could read: a new pair next to the old one, switch clients, revoke the old key.

URL: https://crunu.com/rotate/ssh
Language: en
Updated: 2026-10-30

Someone leaving means two separate SSH jobs. Their own key needs no reissue at all: the private half was theirs, so remove their public key from every `authorized_keys` file and every service that trusts it. A shared key whose private half they could read, such as a deploy key or a team's server key, needs a real reissue: generate a new pair, add the new public key next to the old one on every host, move each client to the new private key, and then delete the old public key. An `authorized_keys` file holds one key per line, so both keys work during the switch and nobody gets locked out ([sshd manual](https://man.openbsd.org/sshd.8)).

Crunu doesn't log in to your servers or edit `authorized_keys`. It keeps shared private keys as files in an environment, shows who received which version, and writes the new key to the machines that need it.

*Figure: Timeline of rotating the SSH key pair. Step 1: a new key is issued while the old one keeps working. Step 2: every place that uses the key is updated, with no downtime because both keys work. Step 3: the old key is revoked. After revocation the old key stops working at once.*

## Where a public key is trusted

`sshd` reads public keys from the files named by `AuthorizedKeysFile`, by default `.ssh/authorized_keys` and `.ssh/authorized_keys2` in the user's home directory ([sshd_config](https://man.openbsd.org/sshd_config.5)). Every line is one key with an optional options field in front. The options are your scope controls, and a reissue is a good time to add them:

- `from=` limits the key to hosts or addresses, including CIDR ranges.
- `command=` forces one command whenever the key is used and ignores what the client asked for.
- `restrict` turns off port, agent and X11 forwarding, PTY allocation and `~/.ssh/rc`, and will include future restrictions too.
- `expiry-time=` sets a date after which the key is no longer accepted.

Servers aren't the only places. Code hosting stores deploy keys, cloud providers inject keys into new machines, and removing a person from a cloud team doesn't necessarily remove their keys from machines already running. The pages on [GitHub deploy keys](https://crunu.com/rotate/github) and [DigitalOcean](https://crunu.com/rotate/digitalocean) cover two common cases.

## Reissue a shared key without downtime

1. Generate a new pair with `ssh-keygen`. Without arguments it creates an Ed25519 key; the comment set with `-C` helps you find the key on servers later.
2. Add the private key as a file in the Crunu environment that held the old one, for example `payments-api / production`, with a path outside `~/.ssh/` such as `deploy/deploy_ed25519`, and save. The new version is recorded with its author and time.
3. Append the new public key to `authorized_keys` on every host and account that trusted the old one. Leave the old line in place for now.
4. Move each client to the new private key: run `crunu pull` on the CI runner or server that connects, then test a login with the new key.
5. Delete the old public key's line from every `authorized_keys` file. For extra certainty, list it in the server's revoked keys file as well.

```sh
ssh-keygen -t ed25519 -C "deploy payments-api 2026-10" -f ./deploy_ed25519
crunu pull --env payments-api/production
ssh -i ~/deploy/deploy_ed25519 deploy@api-prod-1 true
```

`crunu pull` writes files with mode 0600 and refuses paths inside `~/.ssh/` unless you pass `--allow-path` for that run, so either keep deploy keys outside `~/.ssh/`, as in this example, or allow the path explicitly.

## Make the removal stick

Deleting a line works until someone pastes it back. `RevokedKeys` in `sshd_config` names a file of keys that `sshd` always refuses, written either as plain public keys, one per line, or as a key revocation list made with `ssh-keygen -k`. Two warnings from the manual: if that file isn't readable, public key authentication is refused for every user, and because `sshd` may read it on each attempt, replace it atomically rather than editing it in place.

```sh
ssh-keygen -k -f /etc/ssh/revoked_keys.krl ./old_deploy_ed25519.pub
```

Removing a key affects the next authentication. A session that is already open was authenticated before the change, so end the departing person's open sessions separately.

## Keys that expire on their own

Plain key pairs never expire, which is why old keys pile up. OpenSSH certificates are the alternative. A host trusts a certificate authority listed in `TrustedUserCAKeys`, and you sign each user's key with `ssh-keygen -s`, using `-V` to set a validity interval ([ssh-keygen](https://man.openbsd.org/ssh-keygen.1)). Outside that interval the certificate isn't valid, so a departure ends at the next expiry even if nobody edits a single server. Without `-V`, a certificate is valid from the Unix epoch to the distant future, and a certificate without principals isn't accepted through `TrustedUserCAKeys`.

## When to reissue SSH keys

- **A departure.** Remove their personal keys everywhere, reissue every shared key they could read, and end their sessions. In Crunu, removing the person produces a reissue checklist of the files they received, such as a deploy key, by path.
- **A leak.** Remove the old public key and add it to the revoked keys file first, then distribute the new pair.
- **A schedule.** Shared keys have no built-in end date, so reissue them on a calendar or move to certificates.

[What to rotate when a developer leaves](https://crunu.com/guides/developer-offboarding-secret-rotation) puts SSH in the context of the whole departure, and the [offboarding checklist](https://crunu.com/tools/offboarding-checklist) builds the list for your stack.

## FAQ

### Do I need to reissue a departing person's own SSH key?

No. Their private key was always theirs, so there is nothing to replace. Remove their public key from every authorized_keys file and every service that trusts it. Reissue only shared keys whose private half they could read.

### Can two SSH keys work for the same account at the same time?

Yes. Each line of an authorized_keys file holds one key, so you add the new public key next to the old one, move every client to the new private key, and then delete the old line.

### Do SSH keys expire?

A plain key pair never expires. The expiry-time option in authorized_keys sets a time after which sshd stops accepting a key, and certificates signed with ssh-keygen can carry a validity interval. By default a certificate is valid forever.

### Does Crunu remove keys from authorized_keys on my servers?

No. Crunu doesn't manage server logins. It stores shared private keys as environment files, shows who received them, and writes the new key to machines with crunu pull. Editing authorized_keys on each host is your step.

## Sources

- [OpenBSD manual: sshd(8), AUTHORIZED_KEYS FILE FORMAT](https://man.openbsd.org/sshd.8) (accessed 2026-10-30)
- [OpenBSD manual: sshd_config(5)](https://man.openbsd.org/sshd_config.5) (accessed 2026-10-30)
- [OpenBSD manual: ssh-keygen(1)](https://man.openbsd.org/ssh-keygen.1) (accessed 2026-10-30)
