SSH into WSL on a Remote Windows Machine
Table of Contents
To SSH from a client into WSL on a remote Windows machine, use one of the following approaches:
- SSH Jumphost (recommended)
- Windows port forwarding feature
Prerequisites
You should already be able to SSH into the remote Windows machine as described in this article. If you have not set this up yet, please establish SSH access to the remote Windows machine first by following that guide.
Install the OpenSSH Server on the Remote WSL
Regardless of which approach you choose, you need to install the OpenSSH server on the remote Windows’s WSL side and have it listen on a port that does not collide with the Windows side.
sudo apt update
sudo apt upgrade -y
sudo apt install openssh-server -y
Change the port configuration to a port that does not collide with the Windows side (22), e.g. Port 22222:
sudo vim /etc/ssh/sshd_config
Restart the SSH server:
sudo service ssh restart
Verify that sshd is listening on 22222:
ss -tlnp | grep 22222
# Example output
# LISTEN 0 128 0.0.0.0:22222 0.0.0.0:*
# LISTEN 0 128 [::]:22222 [::]:*
Once this is configured, confirm that you can SSH from the remote Windows machine into WSL.
# WSL password required
ssh -p 22222 wsluser@localhost
Windows Configuration (Using a Jumphost)
This approach uses the SSH server on the remote Windows machine as a jumphost, and connects from the client to the remote WSL via SSH’s ProxyJump feature.
It is recommended over the port-forwarding approach because the configuration is simpler.
Prerequisites of Mirrored Mode
To use mirrored mode, the following are required:
- Host OS: Windows 11 22H2 or later
- WSL version: WSL v2.0.4 or later
(check with wsl --version)
WSL Configuration on the Host
Add the following to C:\Users\<username>\.wslconfig on the host:
[wsl2]
networkingMode=mirrored
vmIdleTimeout=-1
[experimental]
hostAddressLoopback=true
- networkingMode=mirrored: WSL is connected directly to the same network as the host, so the WSL IP address becomes the same as the host’s. This allows the client to SSH into WSL directly.
- hostAddressLoopback=true: An experimental setting that requires mirrored mode. It allows connections between the container (WSL) and the host using the IP address assigned to the host. This makes it possible to SSH into WSL using the host’s IP address. Only the IPv4 address assigned to the host is supported; IPv6 is not.
- vmIdleTimeout=-1: Disables the WSL idle timeout. With this, WSL will not automatically stop even after a period of inactivity.
After saving, restart WSL on the host.
wsl --shutdown
Additionally, configure WSL to start automatically when Windows boots.
- Open Task Scheduler with
Win + R+taskschd.msc - Click “Create Task” in the right pane
- “General” tab:
- Name: e.g. “Start WSL on Login”
- Security options: select “Run only when user is logged on”
- Check “Run with highest privileges”
- “Triggers” tab:
- Click “New”
- Begin the task: “At log on”
- Settings: check “Specific user” and select the user
- Delay task for: about 30 seconds to 1 minute (for stability)
- Enabled: checked
- “Actions” tab:
- Click “New”
- Action: “Start a program”
- Program/script:
wsl.exe - Add arguments:
-d Ubuntu --exec dbus-launch true - Start in: leave blank
With this, the command wsl -d Ubuntu --exec dbus-launch true will run at Windows startup and WSL will start automatically.
WSL Configuration
Add the following to /etc/wsl.conf. This makes sshd start automatically when WSL boots.
[boot]
systemd=true
Then enable the SSH server to start automatically with the following command.
sudo systemctl enable ssh
Restart WSL on the host.
wsl --shutdown
With this set of configurations:
- WSL starts up reliably:
- WSL starts automatically when Windows boots
- systemd is enabled inside WSL and the SSH server starts automatically
- WSL does not stop automatically after a period of inactivity
- You can SSH stably from the client to WSL on the host Windows:
- The WSL IP and the host Windows IP match (preventing IP changes on each WSL startup)
- You can specify the WSL IP as 127.0.0.1 when using ProxyJump
How to Configure ProxyJump
Add the following to the client’s ~/.ssh/config.
Note: specify 127.0.0.1 — not localhost — for the HostName of remote-wsl. If you specify localhost, depending on the environment it may connect over IPv6 and fail. Specifying 127.0.0.1 ensures the connection is made explicitly over IPv4.
Host remote-win
HostName <hostname> # or <hostname>.local or <ip-address>
User windowsuser
Host remote-wsl
HostName 127.0.0.1 # explicitly specify IPv4
User wsluser
Port 22222
ProxyJump remote-win
This lets you connect easily from the client with the following command.
ssh remote-wsl
(Optional) Updating the WSL IP Address When Not Using Mirrored Mode
If you do not use mirrored mode and WSL has a different IP address from the host, some extra care is needed.
WSL runs on a virtual NIC and its IP changes every time it starts, so the HostName in the client’s ~/.ssh/config also needs to be updated.
It is convenient to create an update script on the client side.
First, check the path of your profile.
$PROFILE
It typically looks something like C:\Users\kouki\Documents\PowerShell\Microsoft.PowerShell_profile.ps1.
If the file does not exist, create it with the following command.
if (-not (Test-Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
Next, open the profile in VSCode or similar,
code $PROFILE
and add the following function to the end of the file.
function Update-WslIp {
[CmdletBinding()]
param(
[string]$JumpHost = 'winusername@hostname', # Jumphost username and hostname
[string]$ConfigPath = "$HOME\.ssh\config",
[string]$TargetHost = 'remote-wsl'
)
Write-Host "Fetching WSL IP via $JumpHost..." -ForegroundColor Cyan
$wslIp = (ssh $JumpHost 'wsl hostname -I' 2>$null).Trim().Split(' ')[0]
if (-not $wslIp) {
Write-Error "Failed to get WSL IP address"
return
}
if (-not (Test-Path $ConfigPath)) {
Write-Error "$ConfigPath not found"
return
}
# Get current IP (early return if no change is needed)
$content = Get-Content $ConfigPath -Raw
$pattern = "(?ms)(Host\s+$TargetHost\s*\r?\n(?:[^\r\n]*\r?\n)*?\s*HostName\s+)(\S+)"
$currentMatch = [regex]::Match($content, $pattern)
$currentIp = if ($currentMatch.Success) { $currentMatch.Groups[2].Value } else { $null }
if ($currentIp -eq $wslIp) {
Write-Host "IP is already up to date: $wslIp" -ForegroundColor Green
return
}
# Back up and replace
Copy-Item $ConfigPath "$ConfigPath.bak" -Force
$updated = $content -replace $pattern, "`${1}$wslIp"
Set-Content -Path $ConfigPath -Value $updated -NoNewline
Write-Host "Updated: $currentIp -> $wslIp" -ForegroundColor Green
}
# Short alias (optional)
Set-Alias uwsl Update-WslIp
Restart PowerShell or reload the profile.
. $PROFILE
You can then use it with the following commands.
# Alias
uwsl
# Full command
Update-WslIp -JumpHost 'windowsuser@hostname' -TargetHost 'remote-wsl'
Windows Configuration (Using Port Forwarding)
If you are in an environment where using a Jumphost is not possible, you can instead use the Windows portproxy feature to forward connections to a specific port on the remote Windows machine (e.g. 22222) to the WSL SSH server.
The following commands are run in an Administrator PowerShell on the remote Windows machine.
Configuring the portproxy Listening Port
Get the IP address of WSL and configure port forwarding via Windows portproxy.
WSL may report two or more IP addresses (e.g. IPv4 and IPv6, Docker), so take the first one (IPv4).
# Get the WSL IP address (IPv4)
$wslIp = (wsl hostname -I).Trim().Split(' ')[0]
# Configure port forwarding with Windows portproxy
netsh interface portproxy add v4tov4 listenport=22222 listenaddress=0.0.0.0 connectport=22222 connectaddress=$wslIp
# Verify
# It is OK if it shows something like: Listen on ipv4: 0.0.0.0:22222 → Connect to ipv4: 172.x.x.x:22222
netsh interface portproxy show v4tov4
Allow Port 22222 in the Firewall
Opening this port in Windows Defender Firewall allows external clients to connect to the WSL SSH server.
New-NetFirewallRule -DisplayName "WSL SSH" -Direction Inbound -LocalPort 22222 -Protocol TCP -Action Allow
Connecting via SSH from the Client to the Remote WSL
Verify the connection as follows.
Note: explicitly specify the -4 option to connect over IPv4. Without it, IPv6 may be used depending on the environment, and the connection may fail.
ssh -4 -p 22222 wsluser@<hostname>
If the connection succeeds, you are now SSH’d into WSL on the remote Windows machine.
If you want to connect without a password, generate an SSH key and place the public key on the remote WSL.
Note: this assumes the SSH public key has already been generated. See this article for details.
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh -4 -p 22222 wsluser@<hostname> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
To simplify the connection, you can also add the following to the client’s ~/.ssh/config.
Host remote-wsl
HostName <hostname> # or <hostname>.local or <ip-address>
Port 22222
User wsluser
AddressFamily inet # Force IPv4
This allows you to easily connect with the following command.
# You can connect with the following instead of ssh -4 -p 22222 wsluser@<hostname>
ssh remote-wsl
Troubleshooting
Isolating the problem step by step makes the root cause easier to find:
- Remote WSL: check that sshd is listening on 22222 with
ss -tlnp | grep 22222 - Remote Windows: check the forwarding configuration with
netsh interface portproxy show v4tov4 - Remote Windows: try a local connection to WSL with
ssh -p 22222 wsluser@localhost - Client: try SSH into the remote Windows machine with
ssh username@hostname - Client: try the external connection with
ssh -4 -v -p 22222 wsluser@<Windows-IP>(use-vfor verbose logs)
Automatically Updating the WSL IP Address
WSL runs on a virtual NIC, and its IP changes every time it starts, so the Windows portproxy setting also needs to be updated accordingly.
Create the following update PowerShell script on the remote Windows machine.
# update-wsl-portproxy.ps1
$wslIp = (wsl hostname -I).Trim().Split(' ')[0]
netsh interface portproxy delete v4tov4 listenport=22222 listenaddress=0.0.0.0 2>$null
netsh interface portproxy add v4tov4 listenport=22222 listenaddress=0.0.0.0 connectport=22222 connectaddress=$wslIp
Write-Host "Forwarded 22222 -> $wslIp:22222"
Because the script uses netsh, it must be run from an Administrator PowerShell on the Windows side. It does not work inside WSL. There are two common ways to run it.
Method 1: Run Automatically at Windows Logon via Task Scheduler
The WSL IP may change every time Windows restarts, so it is convenient to update it automatically at logon.
-
Run the following in an Administrator PowerShell to register the task (replace
C:\Scripts\update-wsl-portproxy.ps1with the actual path where you placed the script).$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\update-wsl-portproxy.ps1" $trigger = New-ScheduledTaskTrigger -AtLogOn $principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -RunLevel Highest Register-ScheduledTask -TaskName "Update WSL PortProxy" -Action $action -Trigger $trigger -Principal $principal -
After registration, you can find “Update WSL PortProxy” in the Task Scheduler GUI (
taskschd.msc). -
To verify it works, right-click the task and choose “Run” to start it manually.
Note: because the script calls wsl hostname -I, WSL will be started automatically even if it was stopped.
Method 2: Run Manually After Restarting WSL
The IP may have changed immediately after restarting WSL with wsl --shutdown or similar. In that case, run the script manually from an Administrator PowerShell.
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\update-wsl-portproxy.ps1
After running, check netsh interface portproxy show v4tov4; if the forwarding destination IP is updated to the latest WSL IP, you are done.