TL;DR
| |
| |
And in your ~/.bashrc or ~/.zshrc:
| |
Log out and back in. Every shell now shares one agent on a stable socket, and you never spawn a fresh ssh-agent per terminal.
The classic pattern (eval "$(ssh-agent)" in .bashrc) starts one agent per shell, leaks orphaned processes, and loses keys between terminals. Letting systemd own the agent fixes all three: one process, one socket, one lifecycle.
Requirements
openssh (ships ssh-agent and ssh-add) and a systemd user session (any modern distro with logind). Check:
| |
If $XDG_RUNTIME_DIR is empty you don’t have a real user session (e.g. you got here via su); log in over SSH or on the console as your own user.
1. Create the user service
A --user service lives in ~/.config/systemd/user/ and runs as your account, no root:
| |
%texpands to the user runtime dir ($XDG_RUNTIME_DIR, usually/run/user/<uid>), so the socket lands at/run/user/<uid>/ssh-agent.socket.-apins that socket path (instead of a random one under/tmp), which is what we export next.-Dkeeps the agent in the foreground; that’s correct forType=simple.
If you’d rather have it apply to all users on the box, drop the file in
/etc/systemd/user/ssh-agent.service(needs root). For your own account,~/.config/systemd/user/is the clean choice. Pick one location, not both.
2. Export the socket and load the key on login
Add this to your ~/.bashrc or ~/.zshrc:
| |
- The
exportmust come before thessh-add, or the client won’t know which agent to talk to. ssh-add -lexits 1 when the agent has no keys and 2 when it can’t reach the socket; theif !covers both and (re)loads.-t 1dmakes the key expire after a day. Drop-tif you want it to live until you restart the agent.
3. Enable and verify
| |
Open a new terminal (or source ~/.bashrc) and confirm the key is loaded:
| |
What each piece does
| Piece | What it does |
|---|---|
ssh-agent.service (--user) | Keeps one agent alive, owned by systemd |
-a %t/ssh-agent.socket | Socket at a fixed, predictable path |
SSH_AUTH_SOCK in the rc | Points every shell at that socket |
ssh-add -t 1d id_ed25519 | Loads the key on login, with optional expiry |
Traps
- Use your key’s real name. Many guides say
id_rsa; modern keys areid_ed25519. If the file doesn’t exist,ssh-addfails quietly inside theif. - Lingering: by default the user service dies when you close your last session. If you need the agent alive without an active login (cron, a CI runner on the box), enable lingering:
loginctl enable-linger $USER. - The socket must match. The unit’s
-a %t/ssh-agent.socketand the rc’sSSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"point at the same file. Change one, change the other. &>/dev/nullis bash/zsh. Under a strict/bin/sh(dash) use>/dev/null 2>&1.- Passphrase: if your key has a passphrase, the login
ssh-addprompts once per expiry. With-t 1d, once a day. - Don’t mix agents. If a profile still does
eval "$(ssh-agent)", remove it; otherwise you end up with two agents and aSSH_AUTH_SOCKthat flips between them.
See also
- Original Server Fault thread: https://serverfault.com/questions/672346/straight-forward-way-to-run-ssh-agent-and-ssh-add-on-login-via-ssh
- Reference gist (magnetikonline): https://gist.github.com/magnetikonline/b6255da90606fe9c5c25d3333c98c90d