Signing commits on Windows and WSL Jump to heading

On a Windows workstation there are often three Gits: Git for Windows, the Git inside each WSL distribution, and whichever one the editor decided to call. Each has its own configuration, its own idea of where ssh-keygen or gpg lives, and its own agent. Signing works perfectly in one terminal and fails in the editorโ€™s commit box, or prompts for a passphrase on every commit in WSL while Windows never asks. The fix is not more configuration on each side; it is choosing one agent that holds the key and making every Git talk to it. This page does that for SSH and GPG signing, within the GPG vs SSH commit signing topic.

When to use this approach Jump to heading

  • You commit from both Windows and a WSL distribution on the same machine.
  • Signing works in one place but fails, or prompts constantly, in another.
  • Your editor runs on Windows but opens repositories that live inside WSL, or the reverse.
  • You want the private key in one place โ€” ideally the Windows side, where the OpenSSH agent or a password manager agent already runs.
  • On a Linux or macOS machine the plain setup in setting up SSH commit signing from scratch is enough.

Step 1 โ€” Find out which Git each tool is using Jump to heading

Before changing anything, list the Gits involved and where each reads its configuration. Most โ€œsigning works here but not thereโ€ reports are two different configuration files.

# In WSL
which git && git --version && git config --show-origin --get user.signingKey
# In PowerShell
Get-Command git | Select-Object Source
git config --show-origin --get user.signingKey
Three Gits, three configurationsGit for Windows reads the Windows home directory configuration, WSL Git reads the Linux home directory configuration, and an editor uses whichever Git binary it finds first on its own path. Each needs to agree on the signing setup.Git for Windows%USERPROFILE%\.gitconfigWindows OpenSSH agentWSL Git~/.gitconfig in Linuxno agent by defaultEditorfirst git on its PATHmay be eitherconfigure the agent once and make every Git point at it

Step 2 โ€” Hold the SSH key in the Windows OpenSSH agent Jump to heading

Windows ships an OpenSSH agent service. Enable it, load the signing key into it once, and the key is available to every Windows process without a prompt.

# Run once in an elevated PowerShell
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519_signing
ssh-add -l

Configure Git for Windows to sign with SSH using the Windows ssh-keygen, so it reaches the same agent.

git config --global gpg.format ssh
git config --global user.signingKey "$env:USERPROFILE\.ssh\id_ed25519_signing.pub"
git config --global gpg.ssh.program "C:/Windows/System32/OpenSSH/ssh-keygen.exe"
git config --global commit.gpgSign true
# Verification: a commit from PowerShell is signed without a prompt
git commit --allow-empty -m "windows signing test"
git log -1 --format='%G? %GS'

Step 3 โ€” Let WSL sign through the Windows agent Jump to heading

WSL processes cannot reach the Windows agentโ€™s named pipe directly. The simplest bridge is to have WSL Git call the Windows ssh-keygen.exe for signing โ€” WSL can run Windows executables, and that binary talks to the Windows agent.

# In WSL
git config --global gpg.format ssh
git config --global gpg.ssh.program /mnt/c/Windows/System32/OpenSSH/ssh-keygen.exe
# The key path must be readable by the Windows binary, so use the Windows-side file
git config --global user.signingKey "$(wslpath -w "/mnt/c/Users/$WINUSER/.ssh/id_ed25519_signing.pub")"
git config --global commit.gpgSign true

Replace $WINUSER with your Windows user name. The wslpath -w call converts the path to the form the Windows binary expects.

A WSL commit signed by the Windows agentWSL Git invokes the Windows ssh-keygen executable, which asks the Windows OpenSSH agent to sign with the key it already holds. The signature returns to WSL Git and is written into the commit, with no key material inside WSL.WSL gitssh-keygen.exeWindows agent-Y sign, namespace gitsign request via pipesignaturesignature on stdoutone copy of the key, one agent, one prompt per Windows login
# Verification: commit inside WSL, no passphrase prompt, good signature
git commit --allow-empty -m "wsl signing test"
git log -1 --format='%G? %GS'

Local verification inside WSL still needs an allowed signers file on the Linux side, because verification runs ssh-keygen too. Point gpg.ssh.allowedSignersFile at a Linux path or at the Windows file through /mnt/c.

Step 4 โ€” If you use GPG, share one gpg-agent Jump to heading

GPG works the same way in principle: run the agent on one side and forward to it. Gpg4win on Windows provides the agent; WSL Git calls the Windows gpg.exe directly.

# In WSL, use the Windows gpg so the Windows agent and pinentry handle the key
git config --global gpg.format openpgp
git config --global gpg.program "/mnt/c/Program Files (x86)/GnuPG/bin/gpg.exe"
git config --global user.signingKey 0x1122334455667788!
# Verification: the Windows gpg sees the secret subkey
"/mnt/c/Program Files (x86)/GnuPG/bin/gpg.exe" --list-secret-keys --keyid-format long

Mixing a Linux gpg in WSL with a Windows keyring does not work: the two have separate home directories and separate agents, and keeping two keyrings in sync is exactly the problem this page avoids.

Step 5 โ€” Make the editor use a Git that is configured Jump to heading

Editors running on Windows use Git for Windows for repositories on the Windows filesystem. Editors connected to WSL through a remote extension run WSL Git inside the distribution. Both are now configured, but check that the editor is not pointing at a third, bundled Git.

# A git wrapper that logs which binary ran โ€” temporarily put it first on PATH to find out
printf '#!/bin/sh\necho "$0 $*" >> /tmp/git-calls.log\nexec /usr/bin/git "$@"\n' > ~/bin/git
chmod +x ~/bin/git
Which side signs a given commitA commit made from a Windows terminal or a Windows editor on a Windows path signs through Git for Windows. A commit made inside WSL, including from an editor's WSL remote session, signs through WSL Git calling the Windows binary.Where was git commit run?Windows shell or editorGit for WindowsWindows ssh-keygen + agentinside WSLWSL gitcalls ssh-keygen.exebundled editor gitUnknown configconfigure or disableboth legitimate paths end at the same agent, which is the point

Remove the wrapper once you know the answer. If the editor turns out to use a bundled Git, point its Git path setting at the system Git rather than configuring a third copy.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Is it slower to call a Windows binary from WSL? Jump to heading

Slightly โ€” starting a Windows process from WSL adds a few tens of milliseconds. For signing, which happens once per commit, that is not noticeable. It would matter for a hot loop, which signing is not.

Can I keep the key inside WSL instead? Jump to heading

Yes: run ssh-agent inside WSL and configure Git for Windows to call WSLโ€™s binary instead. It works, but Windows tools then depend on WSL being running. Keeping the key on the Windows side is usually more robust because the Windows agent is a service that starts with the machine.

Why does signing fail only when committing from the editor? Jump to heading

The editor is probably launching Git without the environment your shell sets up โ€” no GPG_TTY, no agent socket, or a different PATH. Configuring signing through gpg.ssh.program or gpg.program with absolute paths removes the dependency on environment variables.