Skip to content

Alternative Approaches & Community Comparison

How our setup compares to what others have done, and what we could steal.


Our Stack vs The Field

Aspect Our approach Most common alternative
VPN/tunnel Tailscale on Windows host Tailscale inside WSL2, or WireGuard direct
Networking Mirrored mode (.wslconfig) netsh portproxy (NAT mode)
SSH server openssh-server inside WSL2 Windows OpenSSH with bash.exe as default shell
Persistence Task Scheduler + sleep infinity NSSM Windows service, or [boot] command=
Keep-alive instanceIdleTimeout=-1 + sleep dbus-launch true, or rely on systemd (unreliable)
Key mgmt ed25519 keypair, manual transport Tailscale SSH (no keys at all)
Firewall Windows FW + Hyper-V FW, CGNAT only Often omitted entirely
DNS Static resolv.conf + chattr +i dnsTunneling=true (newer WSL), or re-generate each boot

Notable Projects

dhermes/tailscale-wsl2

URL: https://github.com/dhermes/tailscale-wsl2

Go reverse-proxy pair that bridges the Windows Tailscale daemon's HTTP API into WSL2 via a Unix socket. Lets you run tailscale status etc. from bash.

Verdict: Obsoleted by mirrored networking. Before mirrored mode, WSL2 couldn't see the host's Tailscale IP. Now it can. We don't need this.

peppy0510/wsl-service

URL: https://github.com/peppy0510/wsl-service

Python-based WSL startup manager. Supports both NSSM (Windows service) and NirCmd (Task Scheduler). JSON config for declaring services + firewall rules.

What they have that we don't: NSSM registration runs at boot without user login. Eliminates the auto-login hack.

Gotcha: NSSM on Win11 causes file permission issues in Explorer.

nullpo-head/winsomnia-ssh

URL: https://github.com/nullpo-head/winsomnia-ssh

Prevents Windows from sleeping during active SSH sessions. Detects SSH via process tree, blocks sleep with SetThreadExecutionState.

We should steal this idea -- our remote PC could sleep and become unreachable. A powercfg command in the setup scripts would be simpler:

powercfg /change standby-timeout-ac 0
powercfg /change hibernate-timeout-ac 0

CzBiX/WSLHostPatcher

URL: https://github.com/CzBiX/WSLHostPatcher

Injects into wslhost.exe to make WSL2 listen on all interfaces in NAT mode, without netsh portproxy. Obsoleted by mirrored networking.


Alternative Architectures We Considered

A. Windows OpenSSH as Jump Host (ProxyJump)

you --> SSH --> Windows OpenSSH (port 22) --> ProxyJump --> WSL2 sshd (port 2222)

SSH config:

Host win-jump
    HostName 100.125.78.30
    User Admin

Host wsl-dev
    HostName localhost
    Port 2222
    User simon
    ProxyJump win-jump

Pros: Works without mirrored networking. No special firewall rules for WSL2. Cons: Need Windows OpenSSH + WSL2 sshd running. Double encryption overhead. Two credential sets. Windows OpenSSH has its own quirks.

When to use: Fallback if mirrored networking doesn't work on this Win11 build.

B. Tailscale SSH (no OpenSSH at all)

Run Tailscale inside WSL2 with tailscale up --ssh. Tailscale owns port 22, authenticates via WireGuard node keys. Zero SSH key management.

Pros: No keys to manage. No authorized_keys. No private key transport problem. Cons: Must disable Windows Tailscale (double encapsulation). MTU issues. Officially discouraged by Tailscale.

C. Windows OpenSSH + bash.exe as Default Shell

Set HKLM\SOFTWARE\OpenSSH\DefaultShell = C:\WINDOWS\System32\bash.exe. SSH into Windows and you land in a WSL2 bash shell directly.

Status: Broken since Microsoft moved WSL to the Store. bash.exe from Store WSL fails with "The file cannot be accessed by the system" when invoked by the OpenSSH service. Was Hanselman's recommended approach until it broke.

D. Task Scheduler AtStartup (vs our AtLogOn)

Change the trigger from AtLogOn to AtStartup with "Run whether user is logged on or not". Would eliminate the auto-login requirement entirely.

Gotcha: WSL2 started before user login creates a separate session context. File Explorer and \\wsl.localhost break when the user later logs in (WSL#7365). Only safe for purely headless/SSH-only use. If anyone ever sits at the Windows desktop, this causes problems.

Our auto-login + AtLogOn approach avoids this at the cost of storing a password in the registry.


What Others Do That We Should Add

  1. WSL_INTEROP fix in .bashrc -- so notepad.exe, clip.exe, etc. work over SSH. See wslg-architecture.md.

  2. Disable Windows sleep -- powercfg /change standby-timeout-ac 0. Our remote PC could go to sleep and become unreachable.

  3. Boot delay -- jmmv.dev's guide adds a 30-second delay in Task Scheduler to ensure WSL2 networking is ready. Could help on slow-booting systems.

  4. Tailscale auth keys -- for fully unattended Tailscale setup. The rmrazeen/remote-access-tailscale project uses tailscale up --authkey=... for zero-browser-interaction deployment.


What We Do Better Than Most

  1. Mirrored networking -- most guides still use netsh portproxy which breaks on every WSL restart (IP changes). We avoid this entirely.

  2. Hyper-V firewall -- almost nobody handles this. It silently blocks inbound traffic in mirrored mode. We have the rule.

  3. Tailscale CGNAT restriction -- our firewall rules scope to 100.64.0.0/10. Most guides use 0.0.0.0/0 or skip firewalls entirely.

  4. sleep infinity keepalive -- we don't rely solely on systemd or timeouts, which have known regressions. Our Task Scheduler entry chains service ssh start; exec sleep infinity.

  5. DNS hardening -- chattr +i /etc/resolv.conf prevents Tailscale and WSL from overwriting DNS. Others use boot commands to regenerate resolv.conf each restart, which is fragile.

  6. Comprehensive diagnostics -- our 01-tryout.ps1 prints full status of every component. Most scripts just hope for the best.


Created: 2026-03-16

Sources: - dhermes/tailscale-wsl2 - peppy0510/wsl-service - nullpo-head/winsomnia-ssh - CzBiX/WSLHostPatcher - rmrazeen/remote-access-tailscale - Hanselman: SSH into WSL2 (easy way) - Hanselman: Tailscale + WSL2 - jmmv.dev: SSH access into WSL - microsoft/WSL#7365 (pre-login session isolation) - microsoft/WSL#13416 (systemd keep-alive regression)