Skip to main content
Use the Windows ssh-agent in WSL without breaking auto-poweroff
  1. Posts/
  2. Windows/

Use the Windows ssh-agent in WSL without breaking auto-poweroff

·4 mins

I prefer WSL to shut down when I close the last terminal. I also want easy access to the keys I keep in my Windows ssh-agent. For a long time, trying to get both meant compromising. I tried a few hacks that kept WSL running forever, forcing me to always remember to manually terminate the WSL session. I finally settled on a tidy user-scoped bridge. It gives WSL access to the Windows agent and still lets WSL power off automatically.

The problem with persistent background daemons
#

The Unix socket trick (~/.ssh/agent.sock) is a common pattern for linking Windows keys to WSL. You run a background daemon, bridge the connection, and forget about it.

The struggle is that a persistent system daemon keeps WSL alive. If you close your terminal, the daemon is still listening. WSL stays on, consuming memory.

I knew I needed three things:

  • Use the Windows ssh-agent from WSL without manually copying keys around.
  • Avoid persistent background daemons that prevent auto-poweroff.
  • Keep the setup simple.

The solution is straightforward: create a Unix socket at ~/.ssh/agent.sock inside WSL and bridge it to the Windows OpenSSH agent named pipe using npiperelay and socat. Crucially, we run the bridge as a systemd user service. It lives in your user session and goes away when you log out or close your terminal. This preserves WSL’s automatic shutdown behaviour.

Step 1: Preparing Windows
#

First, we will take this opportunity to install Microsoft’s OpenSSH Preview and replace the default Windows OpenSSH client. This gives you a recent native Microsoft build of OpenSSH, avoiding path confusion between different ssh.exe installations.

If you previously added the built-in OpenSSH capability, remove it first:

dism /online /Remove-Capability /CapabilityName:OpenSSH.Server~~~~0.0.1.0
dism /online /Remove-Capability /CapabilityName:OpenSSH.Client~~~~0.0.1.0

Next, reboot your machine and, once back, install the new ssh and npiperelay so WSL can forward Windows named pipes:

winget install Microsoft.OpenSSH.Preview --custom ADDLOCAL=Client
winget install --id albertony.npiperelay --exact

Restart your terminal to ensure the new paths load. Check ssh with Get-Command ssh, and add your keys in PowerShell with ssh-add. Confirm they are listed:

ssh-add -l

You should see an output similar to this:

256 SHA256:... yourkey@yourmachine (ED25519)

Step 2: Creating the WSL bridge
#

Let’s dive into the Linux side of things.

First, install socat:

sudo apt update && sudo apt install -y socat

Next, point your shell to the new socket by adding this to your ~/.bashrc (or ~/.zshrc):

export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"

Now we create the systemd user service. Open or create ~/.config/systemd/user/ssh-agent-bridge.service and paste the following snippet. Make sure to replace <YourWindowsUser> with your actual Windows username:

Note: you can run wslpath -u $(pwsh.exe -NoLogo -NoProfile -Command 'Write-Output ${env:USERPROFILE}'), inside WSL, if you don’t know your Windows profile path.

[Unit]
Description=WSL SSH agent bridge

[Service]
ExecStart=/usr/bin/socat UNIX-LISTEN:%h/.ssh/agent.sock,fork EXEC:"/mnt/c/Users/<YourWindowsUser>/AppData/Local/Microsoft/WinGet/Links/npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork
Restart=on-failure

[Install]
WantedBy=default.target

Anti-pattern warning: Do not install this as a system-wide service in /etc/systemd/system/. Doing so is exactly what keeps WSL awake forever.

Enable and start your new user service:

systemctl --user enable --now ssh-agent-bridge.service

Let’s verify it works:

ssh-add -l

You should see the exact same keys you loaded on Windows. Congratulations, the bridge is up! 🎉

Step 3: Verify WSL actually shuts down
#

This is the whole point of our setup. Let’s make sure WSL still goes to sleep.

From PowerShell, check the WSL state:

wsl -l -v
  NAME              STATE           VERSION
* Ubuntu            Running         2

Close all your WSL terminal windows. Make sure no VS Code Remote sessions or other tools are keeping a connection open to WSL.

Wait about 15 seconds, and check again:

wsl -l -v
  NAME              STATE           VERSION
* Ubuntu            Stopped         2

If it says Stopped, you are completely done.

Pro tip: You can also confirm this in the Windows Task Manager. Wait about 60 seconds after closing your terminals. Look for vmmemWSL (or vmmem) in the Details view. Once it disappears, WSL has fully shut down and your RAM is free. Of course this only happen if you don’t have anything else running like Docker Desktop, keeping a WSL instance alive.

Troubleshooting cheat sheet
#

If things aren’t working as expected, here are a few signals to check:

  • WSL says it cannot connect to the agent: Check that SSH_AUTH_SOCK is correctly set and the socket file exists in ~/.ssh/.
  • The bridge fails to start: Inspect the service logs with journalctl --user -u ssh-agent-bridge.service. Look for socat errors.
  • Path errors: Ensure the npiperelay.exe path is correct in your systemd unit file under /mnt/c/Users/..., and that you replaced <YourWindowsUser>.
  • No keys visible in WSL: Run ssh-add -l in PowerShell to confirm your keys are actually loaded on the Windows side.

By keeping the bridge in your user session, you get the best of both worlds: unified SSH keys and proper resource management.

Happy hacking! 🐧