cd /news/developer-tools/bitwarden-ssh-agent-in-wsl2-via-a-sy… · home topics developer-tools article
[ARTICLE · art-37804] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Bitwarden SSH agent in WSL2 via a systemd user service (socat + npiperelay) — with the gotchas that make ssh-add hang

A developer created a systemd user service to bridge Bitwarden Desktop's SSH agent from Windows to WSL2 using socat and npiperelay, solving hanging issues with ssh-add. The solution requires the standalone Bitwarden build (not the Microsoft Store version) and disabling Windows' native ssh-agent.

read5 min views14 publishedJun 20, 2026

Use the SSH keys stored in your Windows Bitwarden Desktop vault from inside WSL2ssh

, git

, ssh-add

, etc. — managed by a systemd user service instead of a fragile .zshrc

snippet.

This bridges the Windows OpenSSH-agent named pipe that Bitwarden exposes (\\.\pipe\openssh-ssh-agent

) to a Unix socket inside WSL using npiperelay +

socat

.It is a hardened version of the excellent walkthrough at https://www.rebelpeon.com/bitwarden-ssh-agent-on-wsl2/, with fixes for the problems that make ssh-add -l

hang.

Windows                                   │ WSL2
                                          │
Bitwarden Desktop (standalone build)      │
  └─ \\.\pipe\openssh-ssh-agent  ◀────────┼──  npiperelay.exe  ◀──  socat  ◀──  ~/.ssh/agent.sock
       (one pipe instance)                │      (per connection)   (listener)     ▲
                                          │                                         │
                                          │                       ssh / git / ssh-add (SSH_AUTH_SOCK)

A systemd user service runs one persistent socat

listener. socat

forks a short-lived npiperelay.exe

for each incoming connection, which relays bytes to Bitwarden's named pipe.

The Microsoft Store / Appx build of Bitwarden Desktop runs in a UWP AppContainer sandbox and does not serve the SSH agent to external clients. The pipe appears and connections open, but Bitwarden never replies — so everything hangs, including the native Windows ssh-add.exe

.

Check what you have (PowerShell):

Get-AppxPackage *Bitwarden*      # if this prints anything, you have the Store build — uninstall it

Fix — remove the Store/Appx build, then install the standalone build:

Get-AppxPackage *Bitwarden* | Remove-AppxPackage

winget install Bitwarden.Bitwarden

Or download the desktop installer from https://bitwarden.com/download/.

Bitwarden → Settings → SSH agent → Enable SSH agent. Have at least one SSH key item in your vault, and keep the vault unlocked (the agent only answers while unlocked).

It would fight Bitwarden for the same pipe name. PowerShell (admin):

Stop-Service ssh-agent
Set-Service ssh-agent -StartupType Disabled
  • WSL2 with systemd enabled/etc/wsl.conf

:(then

[boot]
systemd=true

wsl --shutdown

from Windows once) socat

installed:sudo apt install socat

npiperelay.exe

on the Windows side. Easiest:then find the path (it lives under

winget install albertony.npiperelay

C:\Users\<you>\AppData\Local\Microsoft\WinGet\Packages\albertony.npiperelay_*\npiperelay.exe

).Enable lingering so the service runs without an interactive login:

loginctl enable-linger "$USER"

Find your npiperelay path and put it inbw-ssh-relay

(edit theNPIPERELAY=

line). From WSL:

ls /mnt/c/Users/*/AppData/Local/Microsoft/WinGet/Packages/albertony.npiperelay_*/npiperelay.exe

Install the relay script:

mkdir -p ~/.local/bin
cp bw-ssh-relay ~/.local/bin/bw-ssh-relay
chmod +x ~/.local/bin/bw-ssh-relay

Install the service:

mkdir -p ~/.config/systemd/user
cp bitwarden-ssh-agent.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now bitwarden-ssh-agent.service

Point your shell at the socket. Add to~/.bashrc

/~/.zshrc

:

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

Test(open a new shell):

ssh-add -l        # should list your Bitwarden SSH key(s)
File Goes to Purpose
bw-ssh-relay
~/.local/bin/bw-ssh-relay
Launches the socatnpiperelay bridge (the service's ExecStart ).
bitwarden-ssh-agent.service
~/.config/systemd/user/
systemd user unit that keeps one relay listener running and restarts it on failure.

This is the upstream-article command: ** -ei -s** — nothing more.

Flag Meaning
-ei
Terminate when the client (stdin) closes — clean teardown per connection. This is what prevents process leaks, so it must be able to fire.
-s
Send a 0-byte message after EOF (part of the original recipe).

⚠️

Do NOT addIt seems helpful ("poll until the pipe is free") but it is a trap with Bitwarden. Bitwarden exposes only-p

.onepipe instance; when it is busy or wedged,-p

makes npiperelay loop forever inDialPipe

. While polling it never reads stdin, so-ei

can never fire on disconnect — and the Windowsnpiperelay.exe

leaks underwslhost

,one orphan per ssh/git operation, until you kill them by hand. Without-p

, a busy pipe just makes the connection fail fast (retryable). See the troubleshooting note below.

The common .zshrc

approach has every shell try to start/manage its own socat

. That causes:

Races between shells spawning competing listeners.Hangs at shell startup if the block probes the agent withssh-add -l

while Bitwarden's single pipe instance is busy — the new terminal blocks forever.Orphaned processes left behind by closed terminals.npiperelay.exe

A single systemd user service owns exactly one listener, survives across all terminals, restarts on failure, and starts on boot (with linger).

Figure out which side is broken. The native Windows client talks straight to the pipe — no WSL, socat, or npiperelay involved:

/mnt/c/Windows/System32/OpenSSH/ssh-add.exe -l

It also hangs/fails → the problem is Bitwarden, not WSL. See below.** It works but WSL doesn't → the problem is the relay**(socat/npiperelay path, flags, or socket). Checkjournalctl --user -u bitwarden-ssh-agent

.

Bitwarden exposes a single pipe instance. If a client connection is interrupted or force-killed — a closed terminal mid-operation, a Ctrl-C'd ssh-add

, or Stop-Process -Force

on npiperelay.exe

— Bitwarden may fail to re-arm the listener. After that, every client (including native ssh-add.exe

) hangs until you kick the agent:

  • Bitwarden → Settings → SSH agent → toggle off, then on; or Fully quit Bitwarden (tray → Quit) and reopen + unlock.

This is why this unit deliberately

does notforce-kill orphanednpiperelay.exe

— abruptly killing an in-flight pipe client is one of the things that wedges the agent.

If orphaned npiperelay.exe

ever genuinely pile up, clear them by hand (PowerShell):

Get-Process npiperelay -ErrorAction SilentlyContinue | Stop-Process -Force

This means another client momentarily holds Bitwarden's single pipe instance, or the agent is wedged. Do not "fix" it with -p — that trades a fast, retryable error for an unbounded orphan leak (see the flags note above). Instead:

  • If it's transient (two ssh ops at once), just retry.
  • If it persists with nothing connected, the agent is wedged — kick Bitwarden (toggle the SSH agent setting, or fully quit + reopen).
  • If you see orphaned npiperelay.exe

piling up, you almost certainly have-p

somewhere — remove it.

systemd user services can normally launch Windows .exe

files in recent WSL. Verify:

systemd-run --user --wait --pipe /path/to/npiperelay.exe -h

If that fails, your WSL build may not expose interop to systemd services; update WSL (wsl --update

).

systemctl --user status   bitwarden-ssh-agent
systemctl --user restart  bitwarden-ssh-agent
systemctl --user stop     bitwarden-ssh-agent
journalctl  --user -u     bitwarden-ssh-agent -f

:https://github.com/albertony/npiperelay(fork of jstarks/npiperelay)

── more in #developer-tools 4 stories · sorted by recency
── more on @bitwarden 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/bitwarden-ssh-agent-…] indexed:0 read:5min 2026-06-20 ·