SSH key best practices: Ed25519, agents, rotation

Which SSH key type to use in 2026, how to protect it with a passphrase and an agent, when to rotate, and how to find the weak keys you forgot you had.

On this pageUse Ed25519What about RSA, ECDSA and DSA?Always set a passphrase, and let an agent hold itOne key per deviceDo not forward your agentConsider a hardware keyTighten authorized_keysRotate, and removeAudit what you haveAt fleet scale, use certificatesThe Keychain view in Gatesys SSHFAQ

An SSH key is a credential that never expires by itself, is rarely looked at twice, and often opens production. Most teams treat it with less care than a password. This is a short, practical set of rules for 2026: which key to create, how to hold it, and how to find and retire the ones that should have gone years ago.

Use Ed25519

ssh-keygen -t ed25519 -a 100 -C "alice@laptop-2026"
  • -t ed25519 picks the algorithm. Keys are small, signing is fast, and there are no parameters to get wrong.
  • -a 100 raises the number of key-derivation rounds, which slows down anyone guessing the passphrase of a stolen key file.
  • -C is a comment. Put the owner and the device in it. In two years it will be the only clue to what a line in authorized_keys is for.

Ed25519 has been in OpenSSH since 6.5, released in 2014, so anything still in support accepts it.

What about RSA, ECDSA and DSA?

TypeVerdict
Ed25519The default choice
RSA, 3072 bits or moreFine where Ed25519 is not accepted. ssh-keygen -t rsa -b 4096
RSA, 2048 bitsNot broken, but below the 3072 bits OpenSSH has generated by default since 8.0. Replace it at your next rotation
RSA, 1024 bitsReplace now
ECDSAWorks. There is no reason to prefer it to Ed25519
DSAGone. OpenSSH switched it off by default in 9.8 and removed it in 10.0

A separate matter is the signature algorithm. Since OpenSSH 8.8, RSA keys sign with SHA-2, and the old SHA-1 ssh-rsa signature is off by default. An RSA key still works, but a very old server may not understand the new signature. That shows up as no mutual signature algorithm; see Permission denied (publickey).

Always set a passphrase, and let an agent hold it

A key file without a passphrase is a password in a text file. With one, a copied file is useless without it.

The agent means you type the passphrase once a session, not once a connection:

Host *
    AddKeysToAgent yes
    # macOS: keep the passphrase in the login keychain
    IgnoreUnknown UseKeychain
    UseKeychain yes

To limit how long a key stays loaded:

ssh-add -t 8h ~/.ssh/id_ed25519

One key per device

Do not copy one private key to your laptop, your desktop and a build server. Give each device its own key and authorise each on the servers it needs. When a laptop is lost, you remove one line from authorized_keys, and nothing else stops working.

For the same reason, a private key has no place on a bastion. Reach hosts behind it with ProxyJump, which authenticates every hop from your own machine.

Do not forward your agent

ssh -A makes your agent usable from the remote host, by you and by anyone with root there, for as long as you are connected. ProxyJump covers nearly every case agent forwarding was used for and exposes nothing. If you truly need to forward, do it for one host, and load the key with ssh-add -c so each use asks you to confirm.

Consider a hardware key

OpenSSH 8.2 added keys backed by a FIDO2 security key:

ssh-keygen -t ed25519-sk -O verify-required -C "alice@yubikey"

The file on disk is only a handle. The secret stays in the hardware, and a login needs a touch, plus a PIN with verify-required (OpenSSH 8.4 or later). A stolen laptop gives away nothing usable.

Tighten authorized_keys

Each line in authorized_keys can carry options in front of the key. For a key that exists to run one job:

restrict,from="10.0.3.0/24",command="/usr/local/bin/backup" ssh-ed25519 AAAAC3... backup@ci
  • restrict turns off port forwarding, agent forwarding, X11 and the terminal in one word.
  • from= accepts the key only from those addresses.
  • command= runs that command whatever the client asks for.

Rotate, and remove

Rotation matters less for the mathematics than for the housekeeping. A key that is three years old has been on machines you no longer own and in backups you forgot.

  1. Create the new key.
  2. Add its public half to each server: ssh-copy-id -i ~/.ssh/id_ed25519_2026.pub deploy@server.
  3. Test with the new key only: ssh -i ~/.ssh/id_ed25519_2026 -o IdentitiesOnly=yes deploy@server.
  4. Remove the old line from authorized_keys on each server.
  5. Delete the old private key and take it out of the agent with ssh-add -d.

Step 4 is the one that gets skipped. A rotation that leaves the old key authorised has added a key and retired none.

Audit what you have

On your own computer:

for f in ~/.ssh/*.pub; do ssh-keygen -lf "$f"; done
ssh-add -l

Each line begins with the key’s size and ends with its type, such as 256 SHA256:... alice@laptop (ED25519) or 2048 SHA256:... (RSA). Anything that says DSA, or RSA with fewer than 3072 bits, goes on the list to replace.

On a server, the same command on authorized_keys shows who can get in:

ssh-keygen -lf ~/.ssh/authorized_keys

A fingerprint nobody can put a name to is a finding.

At fleet scale, use certificates

Past a few dozen servers and people, managing authorized_keys files stops scaling. SSH certificates turn it around: servers trust one certificate authority, set with TrustedUserCAKeys, and people are issued short-lived certificates for their keys with ssh-keygen -s. Access that expires by itself removes most of the need to rotate.

The Keychain view in Gatesys SSH

Gatesys SSH does the audit for you. Its Keychain, part of the free plan, lists every key in ~/.ssh and whatever your agent holds, each with its type, its fingerprint and the saved hosts that use it. Each key carries a status:

StatusMeaning
InsecureA DSA key, or an RSA key shorter than 3072 bits
RotateOlder than three years
In agentLoaded in the running agent
On diskA key file that is not loaded

The Used by column answers the question that makes rotation slow: which hosts will I have to touch if I replace this key? The app reads keys from the agent the way OpenSSH does, including 1Password, gpg-agent, Pageant and the Windows OpenSSH agent, and it never copies a private key anywhere. See Authentication.

FAQ

Is Ed25519 better than RSA?

For new keys, yes. Ed25519 keys are shorter, faster to use and have no size parameter to choose badly. RSA at 3072 bits or more is still secure, and is the fallback for old systems that do not support Ed25519.

Should an SSH key have a passphrase?

Yes, for any key a person uses. The agent removes the inconvenience. Keys for unattended jobs are the exception. Limit those with restrict, from= and command= in authorized_keys.

How often should I rotate SSH keys?

There is no single standard. Every one to three years is a common rule, and immediately when a device is lost, a person leaves, or a key may have been copied. Certificates with short lifetimes make the question go away.

Is a 2048-bit RSA key still safe?

It is not broken. It is below the size OpenSSH now generates by default, so plan to replace it at your next rotation, not in a panic.

Where should I store my private key?

In ~/.ssh on the device that uses it, with mode 600 and a passphrase, or on a hardware security key. Not in cloud storage, not in a repository, and not on a bastion.

Keep reading

All posts
Guides

SSH ProxyJump: a practical guide to jump hosts

Reach servers behind a bastion with ssh -J and ProxyJump: config examples, multi-hop chains, scp and rsync through a jump host, and bastion hardening.

5 min read

Guides

Debugging a failed ProxyJump chain, hop by hop

An SSH connection through a bastion can fail at five steps on every hop. How to read the error, find the hop that broke, and test from the hop in front of it.

6 min read

Your servers, understood.

Download Gatesys SSH free. Start Pro when you want Farabi — the first 3 months are on us.