termal.in

← Blog

ssh-agent and ssh-add, in practice: stop typing your passphrase

· updated 24 Oct 2026 · Termalin Team sshssh-agentssh-keystutorial

ssh-agent is a background program that holds your unlocked private keys in memory and signs authentication challenges with them, so you enter a key’s passphrase once per session instead of once per connection. ssh-add is the command that loads keys into it.

You’ve done the right thing and put a passphrase on your SSH key — and now you’re typing it for the fifth time before lunch. The fix has existed for decades: the SSH agent. Unlock the key once, and every connection after that authenticates silently. The catch is that “once” only works if the agent is actually running, your shell can find it, and the key is loaded — three separate things, each with its own way of failing. This is the practical guide: starting the agent on each OS, driving it with ssh-add, making the whole thing automatic, and decoding the two errors that account for nearly all agent trouble.

What the agent is, in one paragraph

ssh-agent is a small background process that holds your decrypted private keys in memory — never on disk. When a server challenges you, ssh hands the challenge to the agent, the agent signs it with the in-memory key, and only the signature goes back — the key itself never leaves the agent, never touches the wire, and stays encrypted on disk. One passphrase prompt to load the key, then every later connection is served from memory. That’s the whole mental model: the agent is a signing service, not a key vault anyone reads from — it guards the secret and only ever hands out signatures. (That one property is also the security catch, and it gets its own section at the end.)

On Windows? Read the Windows guide first

If you landed here from error connecting to agent: No such file or directory, that message is Windows-specific: it comes from the Git Bash / MSYS2 ssh, and so do its fixes — the OpenSSH Authentication Agent service, Git Bash’s own agent, and error 1058. They’re all in ssh-agent on Windows. Everything below is the cross-platform ssh-agent and ssh-add workflow, and the errors it decodes are the OpenSSH client’s own: Could not open a connection to your authentication agent and The agent has no identities.

Starting the agent, per OS

Linux — eval "$(ssh-agent -s)"

On a desktop login you almost certainly have an agent already — either a systemd user service or GNOME Keyring playing the role — with SSH_AUTH_SOCK exported for your whole session. Test with ssh-add -l. If nothing answers (common over a bare SSH login, in containers, or on minimal servers), start one by hand:

eval "$(ssh-agent -s)"

The eval matters. ssh-agent -s only prints the SSH_AUTH_SOCK and SSH_AGENT_PID variables; the eval is what sets them in your current shell so ssh can find the socket. Running ssh-agent bare and wondering why nothing changed is the single most common way to “start” an agent that no tool can see.

macOS — launchd runs it for you

launchd runs an agent for you from login — there is nothing to start. The macOS-specific win is the Keychain integration below.

Start ssh-agent on Windows

Modern Windows ships an OpenSSH Authentication Agent service, but it’s set to manual start and isn’t running by default. Enable it once, from an elevated PowerShell:

Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent

After that, ssh-add works in any terminal — PowerShell, cmd, or the Windows OpenSSH client under Git Bash — and the agent survives reboots. One trap: Git for Windows ships its own ssh and ssh-agent, and inside Git Bash the ssh on your PATH may be that one, talking to a separate agent. If ssh-add -l disagrees between Git Bash and PowerShell, point Git at the system client so both share one agent:

git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

The full walkthrough — the service, Git Bash, and loading keys at login — is in ssh-agent on Windows.

Driving it with ssh-add

Load a key: ssh-add ~/.ssh/id_ed25519

ssh-add                          # load the default keys (id_ed25519, id_rsa, …)
ssh-add ~/.ssh/id_ed25519        # load a specific key (prompts for the passphrase once)

With no path, ssh-add loads the default key names from ~/.ssh; give it a path to load a specific key. Either way it asks for the passphrase once and then holds the decrypted key in memory.

List what’s loaded: ssh-add -l

ssh-add -l                       # list fingerprints of loaded keys
ssh-add -L                       # print the full public keys
ssh-add -d ~/.ssh/id_ed25519     # remove one key
ssh-add -D                       # remove all keys

ssh-add -l is the first diagnostic for any auth problem: if the key you expect isn’t in that list, the agent can’t sign with it, whatever else looks right.

Set an ssh-agent timeout: ssh-add -t

Two flags are worth making habits:

  • -t sets a lifetime. ssh-add -t 3600 ~/.ssh/id_ed25519 loads the key for one hour; after that the agent forgets it and the next use re-prompts. A key that sits unlocked in memory for a three-week uptime is a key usable long after you stopped thinking about it.
  • -c requires confirmation. Every signing request pops a local prompt before the agent answers. Slightly annoying, exactly right for high-value keys.

Make it automatic: AddKeysToAgent

The manual ssh-add ritual disappears with one line in ~/.ssh/config:

Host *
    AddKeysToAgent yes

Now the first ssh that needs a key prompts for the passphrase once and loads the key into the agent as a side effect. No separate command to remember — the prompt simply stops recurring. You can set it to a lifetime too (AddKeysToAgent 1h) to get the -t behaviour without the flag.

macOS: survive reboots with the Keychain. By default the agent forgets keys when it restarts. macOS can store the passphrase in your login keychain so keys reload without retyping:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

and in ~/.ssh/config:

Host *
    AddKeysToAgent yes
    UseKeychain yes

After that, the passphrase question is answered by the Keychain at login and you never see it again — while the key file on disk stays encrypted.

The two errors everyone hits

Could not open a connection to your authentication agent — ssh-add can’t find an agent. Either none is running, or one is running but this shell doesn’t have SSH_AUTH_SOCK set. The fix is the per-OS start above: the eval "$(ssh-agent -s)" line on Linux (check first whether your desktop session already provides one — starting a second agent per terminal just scatters loaded keys across processes), the service start on Windows. You’ll also meet this in cron jobs and CI, where no login session ever exported the variable.

The agent has no identities. — the opposite problem: the agent is fine, but nothing is loaded. ssh-add your key. If it was loaded and vanished, a -t lifetime expired or the agent restarted — which is precisely what AddKeysToAgent yes and the macOS Keychain are for.

And the third case: the agent looks perfect — ssh-add -l shows the key — but the server still says Permission denied (publickey). That’s no longer an agent problem; work through the publickey checklist — wrong user, wrong key offered, authorized_keys permissions.

An unlocked agent is a live credential

The agent never leaks the key — but the socket is a signing oracle: it signs for whoever can reach it, and never asks who is asking. On your own machine that’s fine — the socket sits in a private per-user directory only you can read. The danger is the confused-deputy shape: when the socket becomes reachable by something you didn’t intend — a forwarded agent on a shared bastion, or a process running as your user that you don’t trust — that thing can authenticate as you to every server your keys reach, without ever seeing the key. The key never leaking is only half the guarantee; controlling who can use it is the other half, and it’s why agent hygiene is worth the small effort.

  • Give keys a lifetime (-t, or a lifetime on AddKeysToAgent) so an idle agent goes cold on its own.
  • Lock the agent when stepping away: ssh-add -x locks it behind a password, ssh-add -X unlocks; ssh-add -D simply drops everything.
  • Use -c for the keys that reach production.
  • Don’t forward the agent by default. ssh -A — ssh agent forwarding — exposes live use of your keys on the remote host, so anyone with root there can sign as you for the length of your session. That one deserves its own read: ssh agent forwarding is convenient and dangerous.

How Termalin handles this

Termalin builds this whole workflow in, with the sharp edges filed down. Its key agent unlocks your keys once and signs on your behalf — no OS service to enable, no eval line, no per-shell socket to lose. Passphrase caching comes with a re-lock timeout by default, which is the ssh-add -t habit enforced for you rather than remembered by you. And because it speaks the standard ssh-agent protocol, git and your existing CLI tools sign through it unchanged — it replaces the ritual, not your toolchain.


Termalin is a free, cross-platform SSH client whose built-in agent unlocks your keys once and re-locks on a timer — download it, or read how it handles your keys safely.

Try it on one host.

Termalin is a fast SSH client for you — and your agents.

Free tier · 14-day Pro trial · pricing

Add Termalin as a preferred source on Google