You gave your SSH key a passphrase, which is the right call. Now you type it forty times a day, or you quietly removed it last week because it got in the way. ssh-agent exists so you don't have to pick between those two. It holds your decrypted key in memory, signs authentication requests for ssh, git and scp, and never hands the key itself to anyone.
This guide covers how to use ssh-agent on each platform, the handful of ssh-add flags worth knowing, the two errors people actually hit, and the settings that keep a running agent from becoming a liability.
Quick Summary: What You'll Learn
- How to start ssh-agent and add a key on Linux, macOS and Windows 11.
- How to list, test and remove keys with
ssh-add. - How to fix "unable to start ssh-agent service, error :1058" on Windows.
- Why an agent full of keys causes "Too many authentication failures", and the two-line fix.
- ssh-agent best practices: key lifetimes, confirmation, destination constraints and locking.
How do you use ssh-agent?
Three steps, the same on every platform:
-
Make sure an agent is running and that your shell knows where its socket is (the
SSH_AUTH_SOCKenvironment variable on Linux and macOS, a Windows service on Windows). -
Add your key with
ssh-add. You type the passphrase once. -
Connect as usual.
sshfinds the agent throughSSH_AUTH_SOCKand asks it to sign the login challenge. The private key never leaves the agent process.
On Linux, in a shell where no agent is running yet:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l
ssh-agent -s prints Bourne-shell commands that set SSH_AUTH_SOCK and SSH_AGENT_PID; eval runs them in your current shell. Use -c instead of -s if you live in csh or tcsh. ssh-add -l should print one line with the key's SHA256 fingerprint.
If you run ssh-add without a file name, it tries the default identities: ~/.ssh/id_rsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ecdsa_sk, ~/.ssh/id_ed25519, ~/.ssh/id_ed25519_sk, and on current OpenSSH also ~/.ssh/id_mldsa44_ed25519.
What does ssh-agent actually protect?
It keeps the decrypted key out of files and out of the processes that use it. Your passphrase-protected key stays encrypted on disk; only the agent holds the plaintext version, in memory. Clients talk to it over a Unix socket that only your user can open.
What it does not protect against is someone who can open that socket. The ssh-agent man page is blunt about it: the socket "is accessible only to the current user, but is easily abused by root or another instance of the same user." Anyone who can reach the socket can't copy your key, but they can use it to log in wherever the key works. Keep that in mind for the best-practice section below.
How do you start ssh-agent automatically on Linux?
Most desktop sessions (GNOME, KDE and others) already start an agent or an agent-compatible keyring at login. Check before adding another one:
echo "$SSH_AUTH_SOCK"
ssh-add -l
ssh-add exits with status 2 when it can't reach an agent at all, and 1 when the agent is running but holds no keys (it prints "The agent has no identities."). That difference is useful in scripts.
On a server or a minimal desktop with no agent, running eval "$(ssh-agent -s)" in every new terminal gives you one agent per shell, each with its own keys. A cleaner pattern is one agent per login on a fixed socket path, reused by every shell:
# in ~/.bashrc or ~/.zshrc
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
ssh-add -l >/dev/null 2>&1
if [ $? -eq 2 ]; then
rm -f "$SSH_AUTH_SOCK"
eval "$(ssh-agent -s -a "$SSH_AUTH_SOCK" -t 8h)" >/dev/null
fi
-a binds the agent to a socket path you choose, so every shell finds the same agent. The rm -f clears a stale socket left over from a crash, because ssh-agent won't bind to a path that already exists. -t 8h sets a default lifetime for every key added to this agent; more on that below.
Where does the socket live if you don't pick one? Recent OpenSSH releases create it under ~/.ssh/agent/ with a random s.* name. Older releases, which many distributions still ship, use a random directory under /tmp instead. Either way, SSH_AUTH_SOCK tells you the real path.
How do you use ssh-agent on macOS?
macOS starts an agent for your login session through launchd, so SSH_AUTH_SOCK is usually set already and you skip the eval step. Apple's build of ssh-add adds one option the upstream tool doesn't have: it can store your key's passphrase in the macOS Keychain.
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
To have keys loaded on first use instead of by hand, add this to ~/.ssh/config:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Two things to know:
-
UseKeychainonly exists in Apple's OpenSSH. If you also use Homebrew'sssh, or share this config file with Linux machines, addIgnoreUnknown UseKeychainabove it so other builds don't fail with "Bad configuration option: usekeychain". - Before macOS Monterey (12.0), the flags were
-Kand-Ainstead of--apple-use-keychainand--apple-load-keychain. Old blog posts that tell you to runssh-add -Kare describing that older syntax.
How do you use ssh-agent on Windows 11?
Windows ships OpenSSH as a system component, and its agent runs as a Windows service called ssh-agent. Microsoft's docs note that the service is disabled by default. Enable and start it from an elevated PowerShell:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
Get-Service ssh-agent
The last line should show Running. Then, from a normal (non-admin) PowerShell, add your key:
ssh-add $env:USERPROFILE\.ssh\id_ed25519
ssh-add -l
Unlike the Unix agent, the Windows service stores added keys under your Windows account, so they are still there after a reboot. Microsoft's guide goes as far as suggesting you back up the key file and delete it locally once it's in the agent. If you do that, make sure the backup really exists first: the agent will not give the private key back to you.
How do you fix "unable to start ssh-agent service, error :1058"?
Error 1058 is Windows saying the service can't be started because it is disabled. That's the default state of ssh-agent, so this error almost always means nobody ran the Set-Service line above. Open PowerShell as administrator, run:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
and try ssh-add again. Use -StartupType Manual instead if you'd rather start the service yourself each time.
Why does Git on Windows keep asking for my passphrase?
Git for Windows can bring its own ssh.exe (from its MSYS2 environment). That binary doesn't talk to the Windows ssh-agent service, so the key you added in PowerShell is invisible to git push. Point Git at the system OpenSSH client:
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
Or reinstall Git for Windows and pick the "Use external OpenSSH" option.
How do you list, test and remove keys with ssh-add?
The day-to-day flags, all from the ssh-add man page:
-
ssh-add -llists fingerprints of every key the agent holds.ssh-add -Lprints the full public keys instead, handy for pasting intoauthorized_keys. -
ssh-add -T ~/.ssh/id_ed25519.pubtests whether the agent can actually sign with the private key behind that public key. -
ssh-add -d ~/.ssh/id_ed25519removes one key. It wants the public key file; if you pass the private key path, it appends.puband tries again. -
ssh-add -Dremoves every key. Good habit at the end of the day or before you hand your laptop to someone. -
ssh-add -xlocks the agent with a password,ssh-add -Xunlocks it. While locked, the keys stay loaded but can't be used. -
ssh-add -v(up to-vvv) prints debug output when something doesn't load.
ssh-add also refuses to load a key file that other users can read. If it complains about permissions, chmod 600 ~/.ssh/id_ed25519 fixes it.
Why does ssh-agent cause "Too many authentication failures"?
When ssh connects, it offers the server every key the agent holds, one after another, before it gets to the one you meant. Each rejected key counts as a failed attempt. The server's MaxAuthTries defaults to 6, so once six keys that are wrong for this server have been offered, you get disconnected with "Too many authentication failures" before the right key is ever tried. With a handful of project keys loaded, that happens sooner than you'd think.
The fix is to tell ssh which key belongs to which host, and to stop it from offering the rest:
Host prod-db
HostName 10.0.4.12
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
With IdentitiesOnly yes, ssh only uses the identity files named in the config or on the command line, even if the agent offers more. The agent still does the signing, so you keep the no-retyping benefit. Our post on managing multiple SSH keys across devices has a naming scheme that makes these blocks easier to maintain, and ssh config file organization covers how to keep the file readable once it grows.
What are ssh-agent best practices in 2026?
An agent that holds every key forever, for every host, is convenient and a decent target. These settings cut the risk without bringing back the passphrase prompts.
Give keys a lifetime
By default a key stays in the agent until the agent exits. Set a limit, either for the whole agent or per key:
ssh-agent -s -t 8h # default lifetime for every key added
ssh-add -t 1h ~/.ssh/id_ed25519_prod # this key only, overrides the default
Times use the sshd_config format: plain seconds, or units like 30m, 8h, 1h30m. The same works from ~/.ssh/config, which is the least effort option:
Host *
AddKeysToAgent 4h
AddKeysToAgent accepts no (the default), yes, ask, confirm (optionally followed by a time), or a time interval on its own. With a time interval, a key is added the first time you use it and removed after that interval.
Confirm each use for your most sensitive key
ssh-add -c ~/.ssh/id_ed25519_prod
With -c, the agent asks for confirmation through an ssh-askpass program every time the key is used. AddKeysToAgent confirm does the same from the config file. This needs a working askpass program on the machine running the agent, which most Linux desktops have and minimal servers usually don't. It's the strongest guard if you ever forward the agent, because a hijacked socket can't use the key without you clicking yes.
Restrict where a key can be used
Since OpenSSH 8.9, ssh-add -h attaches destination constraints to a key:
ssh-add -h git@github.com ~/.ssh/id_ed25519_github
That key can now only be used from this machine to github.com as user git. Hosts are identified by their host keys from your known_hosts files, so a typo-squatted or spoofed host doesn't match. Constraints also apply across forwarded agents, as long as each hop's client and server support them.
Don't forward the agent by default
ForwardAgent is no by default for good reason: anyone with root on the remote host can use your forwarded agent to authenticate as you while you're connected. Use ProxyJump to reach hosts behind a bastion instead. Our post on ssh agent forwarding security risks walks through the attack and the alternatives.
Keep a passphrase on the key file anyway
The agent is the reason a passphrase stops being annoying, not a reason to drop it. If the laptop is stolen while powered off, the agent is empty and the key on disk is still encrypted. ssh-keygen best practices for 2026 covers the key type and -a rounds to pick when you generate it. If an existing key keeps rejecting its passphrase, see ssh key passphrase not working.
Clear it when you step away
ssh-add -D before you leave, or ssh-add -x to lock without unloading. On Linux you can also send the agent SIGUSR1, which deletes all loaded keys.
Where does termique fit?
ssh-agent solves the "type the passphrase once" problem for command-line tools. termique, the SSH client we build, takes a different route for the keys you store in it.
- Keys live in an encrypted vault. Private keys you generate or import in termique are encrypted with a data key that is unlocked by your master password, and only ciphertext is stored or synced. Details are on the security page.
-
One key per host, by design. When you add a host, you set the auth type to key and pick which key it uses. That's the same idea as
IdentitiesOnly yes: no client offering every key to every server. -
Locking clears the data key from memory. Locking the app drops it from memory until you unlock again, which is the same habit as
ssh-add -Dat the end of the day. - Passphrase-protected keys can be imported, so you don't have to strip the passphrase to move a key in.
termique doesn't replace ssh-agent for your terminal, git or CI. Keep the agent setup above for those, and use termique for interactive sessions across your machines.
FAQ
How do I check if ssh-agent is running?
Run ssh-add -l. Exit status 2 (with a "Could not open a connection to your authentication agent" message) means no agent is reachable from this shell. Exit status 1 with "The agent has no identities." means it's running but empty. On Windows, Get-Service ssh-agent shows whether the service is running.
How do I list the keys in ssh-agent?
ssh-add -l prints fingerprints; ssh-add -L prints the full public keys.
How do I remove a key from ssh-agent?
ssh-add -d path/to/key.pub removes one key, ssh-add -D removes all of them.
Is it safe to leave keys in ssh-agent?
It's reasonably safe on a single-user machine you control, and much safer with a lifetime (-t or AddKeysToAgent 4h). The risk is anyone who can reach the agent socket, which includes root on that machine and root on any host you forward the agent to.
Why does ssh-agent forget my keys after a reboot on Linux but not on Windows?
The OpenSSH agent on Linux and macOS keeps keys only in memory, so they're gone when it exits. The Windows ssh-agent service stores added keys under your Windows account, so they persist until you remove them with ssh-add -d or ssh-add -D.
Try termique free
If you juggle keys across several machines and want one key per host without maintaining the config by hand, download termique and import the keys you already use. Keep ssh-agent running for your terminal and Git; the two work side by side.
Top comments (0)