Skip to content

Instantly share code, notes, and snippets.

@dmcbane
Created June 24, 2026 02:30
Show Gist options
  • Select an option

  • Save dmcbane/498d463de13a19ee693c8ff083fa1556 to your computer and use it in GitHub Desktop.

Select an option

Save dmcbane/498d463de13a19ee693c8ff083fa1556 to your computer and use it in GitHub Desktop.
Configure a persistent SSH agent to load your tokens once when you login

Persistent SSH Agent Configuration via Systemd

This document describes how to configure ssh-agent as a user-level background service using systemctl --user. This modern setup ensures a single, persistent SSH agent runs per user session and manages keys seamlessly across all terminal instances.


Step 1: Create the User Service File

Create the required directories and the systemd unit configuration file.

mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/ssh-agent.service

Paste the following configuration into the file:

[Unit]
Description=SSH key agent
Documentation=man:ssh-agent(1)

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
# Defines the graphical prompt program if keys require a passphrase
Environment=SSH_ASKPASS=/usr/bin/ssh-askpass
ExecStart=/usr/bin/ssh-agent -D -a $SSH_AUTH_SOCK
# Optional: Automatically loads default keys into the agent on startup
ExecStartPost=/usr/bin/ssh-add

[Install]
WantedBy=default.target

Note: %t is a systemd specifier that automatically expands to your user's runtime directory (typically /run/user/UID).


Step 2: Enable and Start the Service

Reload the systemd user manager instance configuration, enable the service to start automatically at login, and trigger it immediately.

systemctl --user daemon-reload
systemctl --user enable --now ssh-agent.service

Verification

Verify that the service is active and running correctly:

systemctl --user status ssh-agent.service

Step 3: Update Environment Variables

For SSH clients and terminal tools to communicate with the background agent, the SSH_AUTH_SOCK environment variable must be exported within your shell configuration profile.

Open your shell profile (~/.bashrc, ~/.zshrc, etc.):

nano ~/.bashrc

Append the following line to the absolute bottom of the file:

export SSH_AUTH_SOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ssh-agent.socket"

Apply the changes to your active terminal session:

source ~/.bashrc

Step 4: Automate Key Loading

To prevent manual ssh-add execution across sessions, choose one or both of the automation strategies below:

Option A: On-Demand Loading (Recommended)

Instruct your SSH client to automatically pass unlocked keys into the active systemd agent upon your first connection to any host.

Open or create your local SSH configuration file:

nano ~/.ssh/config

Add the following lines to the top of the file:

Host *
    AddKeysToAgent yes

Option B: Proactive Login Prompt (Graphical Askpass)

If you included ExecStartPost=/usr/bin/ssh-add in your service file, ensure a graphical passphrase prompt tool is installed on your system to handle password-protected keys at login:

  • GNOME / Ubuntu: sudo apt install gnome-ssh-askpass
  • KDE Plasma: sudo apt install ksshaskpass
  • Generic / Lightweight: sudo apt install ssh-askpass

Ensure the path assigned to Environment=SSH_ASKPASS= inside your ssh-agent.service correctly references the chosen binary.


Step 5: Test the Configuration

Validate that your terminal can successfully communicate with the active systemd agent wrapper:

ssh-add -l
  • Success State 1: Returns The agent has no identities. (Agent is functioning perfectly, keys will populate upon your first SSH connection).
  • Success State 2: Lists your cryptographic key hashes (Agent has successfully loaded keys proactively via ssh-add).

Appendix A: Security Hardening (Key Lifetime Limits)

By default, once a key is unlocked and added to the agent, it remains in system memory indefinitely until the agent service stops. To reduce the risk of memory-scraping attacks or unauthorized access if your machine is left unattended, you can enforce an automatic expiration timeout.

Setting a Global Lifetime Limit

You can pass the -t flag to the ssh-agent binary inside your service file to specify a maximum lifetime for loaded identities.

  1. Open the service file:
    systemctl --user edit --full ssh-agent.service
  2. Modify the ExecStart line to include the -t parameter followed by a time value (e.g., 2h for 2 hours, 30m for 30 minutes, or 1d for 1 day):
    ExecStart=/usr/bin/ssh-agent -D -a \$SSH_AUTH_SOCK -t 2h
  3. Restart the service to apply the policy change:
    systemctl --user daemon-reload
    systemctl --user restart ssh-agent.service

Manually Setting Per-Key Lifetimes

If you do not want a global rule, you can specify individual lifetimes when manually adding keys to the agent:

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

Appendix B: Troubleshooting

Error: "Could not open a connection to your authentication agent"

This means your shell configuration cannot find the socket.

  1. Run echo $SSH_AUTH_SOCK and confirm it matches the path in your ssh-agent.service file.
  2. Confirm the systemd service is active: systemctl --user status ssh-agent.service.

The Askpass prompt doesn't appear at login

If you use a custom or minimal window manager (like i3, Sway, or bspwm), systemd may launch before the X11 or Wayland display server is completely initialized.

  • Fix: Remove ExecStartPost=/usr/bin/ssh-add from the systemd service file. Instead, handle key unlocking by relying entirely on the Option A: On-Demand Loading (AddKeysToAgent yes) method inside your ~/.ssh/config. This triggers the prompt directly in your window environment only when you actively try to connect to a server.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment