Delete an SSH key on a Mac without locking yourself out
Deleting an SSH key sounds like deleting a file, and that is why it goes wrong in two opposite directions. Remove the file and access carries on, because the key is still listed on the servers that accept it. Remove it from the server first, while connected through it, and the session that was going to fix things is the session that just ended.
The key is not in one place. On a Mac it can be in five, and each of them takes a different command. Working through them in a fixed order makes the job dull instead of risky.
The five places a key can live
| Location | What removing it achieves | Removes access? |
|---|---|---|
The key file in ~/.ssh |
The client no longer has the private key | No |
The running ssh-agent |
The agent stops offering it this session | No |
| The login keychain | The passphrase is no longer supplied for you | No |
authorized_keys on each server |
That server stops accepting the key | Yes |
| A provider's account page, such as GitHub | That service stops accepting the key | Yes |
A sixth case applies to hardware keys. A resident credential on a FIDO authenticator stays on the token whatever happens on the Mac, and man ssh-keygen offers only -K to download resident keys, not a command to remove them.
The table is the whole point of the article. Only the last two rows revoke anything. The first three change what this Mac does, which is worth doing for hygiene and for stopping the wrong key from being offered, but nobody loses access because a file was deleted from a laptop.
One more distinction avoids a wasted hour. ~/.ssh/known_hosts holds host keys belonging to servers, not identities belonging to you. Clearing an entry there is a separate operation with a separate command, covered further down.
Identify the key before removing anything
Keys are usually deleted because there are several and one of them is wrong. Names are unreliable at that point, so work from fingerprints.
ssh-keygen -l -f <path> shows the fingerprint of the given public key file. The manual notes that ssh-keygen will try to find the matching public key file, and that adding -v prints a visual ASCII art representation alongside the fingerprint.
ssh-add -l lists the fingerprints of every identity the agent currently holds, and -E sha256 sets the hash algorithm used to display them. GitHub's own auditing documentation uses exactly that command, ssh-add -l -E sha256, and then says the keys listed on the account should match the ones on the computer. Comparing two lists of fingerprints is how a key gets identified, rather than guessing from a filename.
If the .pub file is missing, it is recoverable. ssh-keygen -y reads a private OpenSSH format file and prints the matching public key to standard output. That is worth doing before deleting anything, because the public half is what most removal commands take as their argument.
Finally, to learn which key a particular host is actually using, ssh -G host prints the client's configuration after evaluating Host and Match blocks, and ssh -v host prints the identities as they are offered. A key that no host offers is a safe key to remove.
An order of operations that cannot lock you out
The sequence matters more than any individual command.
- Generate and install the replacement first, if there is going to be one. Run without arguments,
ssh-keygenproduces an Ed25519 key. - Prove the replacement works, in a new session, while the old session stays open.
- Only then remove the old key from
authorized_keyson the server, or from the provider's account page. - Remove it from the agent and the keychain on the Mac.
- Delete the key files last.
Step 2 is the one people skip. Keeping the working session open while editing authorized_keys on the same host costs nothing and turns a lockout into a retry. If the connection is to a machine with no console access, that open session is the only recovery path there is.
Step 5 is last because the file is the only thing in the list that cannot be reconstructed. An agent entry comes back with one command, a keychain entry comes back with a passphrase, but a deleted private key with no copy is gone, and man ssh-keygen is explicit that there is no way to recover a lost passphrase either. Copying the pair somewhere safe before deleting is cheap insurance, and browsing a hidden folder while running the commands that verify it is far easier in a window that shows both than in two that keep swapping places.
Removing it from the agent and the keychain
ssh-add -d removes identities instead of adding them. The manual is specific about what the arguments mean: the argument list is interpreted as a list of paths to public key files, and if no public key is found at a given path, ssh-add appends .pub and retries. Passing - makes it read the public keys to be removed from standard input. Run with -d and no arguments at all, it removes the default identities and their certificates.
ssh-add -D deletes all identities from the agent. That is the blunt instrument for a machine where the agent has accumulated keys nobody remembers adding. Two modifiers narrow it: -k processes plain private keys only and skips certificates, and -C processes certificates only and skips plain keys.
The keychain is the step that gets forgotten on a Mac, and it is where a passphrase quietly survives. man ssh-add documents --apple-use-keychain as storing each passphrase in the user's keychain when adding identities, and states that when removing identities with -d, each passphrase will be removed from it as well. So the keychain is cleaned by removing the identity with the same flag that put it there, not by a separate command. The older -K and -A spellings still work but now print a warning, and the manual says future macOS releases will not support either without setting APPLE_SSH_ADD_BEHAVIOR.
Two alternatives to deletion are worth knowing, because sometimes the goal is to stop a key being used rather than to get rid of it. ssh-add -x locks the agent with a password and -X unlocks it, which suspends every identity at once without removing any. And ssh-add -t life sets a maximum lifetime when adding an identity, expressed in seconds or in the time format that sshd_config documents, after which the agent drops the key by itself. A key added with a lifetime never needs a deletion command.
One trap follows from all this. If AddKeysToAgent is set to yes in ~/.ssh/config and the key file still exists, the next connection that uses the key puts it straight back into the agent. Removing it from the agent is only durable once the file is gone or the config no longer points at it.
Deleting the key files
Three files, not one. The private key, the public key with .pub appended, and, if certificates are in use, the sibling named by appending -cert.pub to the private key's name. man ssh-add describes that naming rule when it explains how certificates are loaded alongside keys.
After the files go, sweep the configuration. An IdentityFile line pointing at a path that no longer exists is a line worth removing, and because Include in ssh_config expands glob wildcards in lexical order and resolves relative paths inside ~/.ssh, the reference can be in a fragment rather than in the main file. Grepping the whole directory for the key's base name finds all of them in one pass.
Spare copies deserve the same sweep. A file named id_ed25519.old is never offered by the client, so it provides nothing, and it is a second copy of a secret sitting in a folder that gets backed up.
known_hosts is a different deletion
The query that brings people here is often not about their own key at all. It is the warning that a remote host identification has changed, which is about a server's key stored in known_hosts.
ssh-keygen -R hostname removes all keys belonging to the specified host name, with an optional port number, from a known_hosts file. The manual notes that this option is useful for deleting hashed hosts, which is the case where opening the file and searching for the name gets nowhere because the names are not stored in readable form.
Before removing, ssh-keygen -F hostname searches for that host name in a known_hosts file and lists any occurrences found. It works on hashed names too, and it confirms that the entry about to be removed is the one intended. Copying the file first is worth the second it takes, since the useful history of every server ever connected to lives in it.
The deletion that actually revokes access
Two places, and both are outside the Mac.
On a server, the entry in that user's ~/.ssh/authorized_keys is what grants access. man ssh-keygen puts it plainly in its FILES section: the contents of the public key file should be added to authorized_keys on all machines where the user wishes to log in using public key authentication. Removing that line is the revocation. Where many hosts are involved and editing each is impractical, OpenSSH has a revocation list: ssh-keygen -k generates a KRL file that revokes every key or certificate presented on the command line, and the server is configured to consult it.
On a hosted service, the account page is the equivalent. GitHub's documentation gives the route as the profile menu, then Settings, then SSH and GPG keys under the Access section, where each key has a Delete control. Its auditing guidance adds two details worth carrying to any provider: deploy keys are listed separately from account keys and have to be reviewed on their own, and if the audit was triggered by a failed Git operation, the offending key is highlighted in the list.
For a hardware authenticator, neither of these reaches the credential itself. Deleting the local files removes the handle to a resident key, not the key material on the token. Clearing that requires the authenticator's own management tool, and on many tokens the only operation offered is a full reset that invalidates every credential on it. Plan for that before it becomes urgent, and see the questions that come up most often for the everyday cases.
What to change first
List what the agent holds with ssh-add -l -E sha256 and compare it against the keys on each account page. Every fingerprint that appears in one list and not the other is either a key to remove or a key to install, and until those two lists match, no deletion is safe to perform. A window where the hidden folder, the fingerprints and the commands are all visible at once, such as Atriens, keeps that comparison to one screen.
Frequently asked questions
I deleted the key file. Why can I still log in?
Because access is granted by the server, not by the file. The line in ~/.ssh/authorized_keys on that machine, or the entry on the provider's account page, is what accepts the key. Until one of those is removed, anyone holding a copy of the private key can still connect, whatever happened on this Mac.
How do I remove every key from the agent at once?
ssh-add -D deletes all identities from the agent. If only plain keys or only certificates should go, -k processes plain private keys and skips certificates, and -C does the reverse. Note that AddKeysToAgent yes in the config will reload a key on next use if its file still exists.
Does deleting the key also remove the passphrase from my keychain?
Only if the removal uses the same Apple flag that stored it. man ssh-add states that --apple-use-keychain stores each passphrase in the keychain when adding identities, and that removing identities with -d removes each passphrase from it. The older -K and -A forms still work but now warn, and are documented as going away.
How do I clear a single host from known_hosts?
ssh-keygen -R hostname removes all keys belonging to that host name, and accepts an optional port. Run ssh-keygen -F hostname first to list the occurrences and confirm the target. This is the right command even when the file's host names are hashed and unreadable.
Can a key on a security key be deleted from the Mac?
No. ssh-keygen -K downloads resident keys from a FIDO authenticator, but there is no OpenSSH command to erase them from it. Removing the downloaded files removes the handle on this computer, while the credential stays on the token until the authenticator's own management tool clears it, which on many devices means a full reset.