A new experimental feature was added by Microsoft that will resolve this issue in the future. You will need to update to Windows Insider Build for Windows 10 or latest Windows 11 and update WSL. More info here.
On WSL1
we can access windows services running on localhost
ports directly, but when WSL2
was introduced things started breaking. One of those issue is access to windows localhost ports 1
To understand this issue we need to learn what is WSL2:
- WSL2 is a Managed Virtual Machine Running on Windows contrary to WSL1.
- Windows implements a virtual NIC for WSL2 thus the distro's host ip is different from the Windows host ip. 2
To put into perspective WSL2 is just another computer connected in a private network with our host computer. The workaround to simulate to some extent the WSL1 behaviour we need to:
- ditch the
localhost
dns name, as this would be targeting the WSL2 distro and instead point to anotherDNS Name
. (eg.winhost
) - dynamically map our new
DNS Name
to the windows host ip. - setup Windows firewall configurations to allow connection from WSL2. (Optional, but mandatory to certain Apps/Services).
Note: We are only addressing the WSL2_DISTRO -> Windows_HOST
Note: Accessing WSL2 App ports is accessable on Windows localhost. 2
Note: Accessing WSL2 App ports from the local-area-network of the Windows host is not covered here, and requires manual ipv4-to-ipv4 proxy configuration on the Windows Host. I recommend David Bombal's WSL2 Networking
- Distro Side (WSL2)
- capture the windows host-ip during wsl boot
- assgin a new dns name to it
- register the dns record to distro's host file
- make it dynamic and deploy-and-forget
- Windows Side
- Manage firewall permissions
This method requires the latest WSL
as it now supports systemd
natively.
WSL version: 1.1.6.0
Kernel version: 5.15.90.1
Create a bash script that will handle the updating of the hosts file. Save the script and place it at anywhere you like. Make sure to set its execution attribute and permissions. (recomended location: /etc
)
(eg. /etc/wsl.winhost.sh
)
#!/usr/bin/bash
# wsl get windows host ip
winhost=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
if [ ! -n "$(grep -P "[[:space:]]winhost" /etc/hosts)" ]; then
printf "%s\t%s\n" "$winhost" "winhost" | tee -a "/etc/hosts"
fi
sudo chmod +x /etc/wsl.winhost.sh
Utilize the /etc/wsl.conf
boot command config and run wsl.winhost.sh
at boot.
/etc/wsl.conf
[ref.]
[boot]
command="bash /etc/wsl.winhost.sh"
1. add the command inside the wsl.conf
file to execute the script. e.g. command="bash /etc/wsl.winhost.sh"
cat /etc/hosts
look for winhostrun a node server and access your app (eg. https://winhost:3000)
Note: This might fail if windows is blocking the port for 3000. See Windows Firewall Configuration below..
Utilize the systemd
native support of WSL2
by enabling it on /etc/wsl.conf
and running the wsl.winhost.sh
as a service.
/etc/wsl.conf
[ref.]
[boot]
systemd=true
1. create a new service file at /etc/systemd/system/
and name it anything you like (eg. winhost.service
)
Useful reference for Systemd [ref]
[Unit]
Description=Map windows localhost IP to dns winhost
[Service]
Type=oneshot
ExecStart=/etc/wsl.winhost.sh
RemainAfterExit=false
[Install]
WantedBy=multi-user.target
- set the permissions
sudo chmod 640 /etc/systemd/system/winhost.service
- check if the service config is correct
systemctl status winhost
- reload the
systemctl daemon
.
sudo systemctl daemon-reload
- enable the service at boot
sudo systemctl enable winhost
- start the Service
sudo systemctl start winhost
- Check the status for error
sudo systemctl status winhost
○ winhost.service - Map windows localhost IP to dns winhost
Loaded: loaded (/etc/systemd/system/winhost.service; enabled; vendor preset: enabled)
Active: inactive (dead)
Apr 04 15:49:06 win10 systemd[1]: Starting Map windows localhost IP to dns winhost...
Apr 04 15:49:06 win10 wsl.winhost.sh[513]: 172.26.32.1 winhost
Apr 04 15:49:06 win10 systemd[1]: winhost.service: Deactivated successfully.
Apr 04 15:49:06 win10 systemd[1]: Finished Map windows localhost IP to dns winhost.
- check if winhost is enabled at boot, it should say
enabled
atSTATE
sudo systemctl list-unit-files --type=service
UNIT FILE STATE VENDOR PRESET
winhost.service enabled enabled
cat /etc/hosts
look for winhostrun a node server and access your app (eg. https://winhost:3000)
Note: This might fail if windows is blocking the port for 3000. See Windows Firewall Configuration below..
The Allow All Connection on this NIC method 3
Create a new firewall rule that allows all connection via the NIC "vEthernet (WSL)", allowing unfiltered netowrking between WSL VM and Windows Host.
Note: Run this script only once as it will create a new firewall rule with the same name.
# Requires -RunAsAdministrator
# Inbound Rule
New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias "vEthernet (WSL)" -Action Allow
# Outbund Rule
New-NetFirewallRule -DisplayName "WSL" -Direction Outbound -InterfaceAlias "vEthernet (WSL)" -Action Allow
To modify the rule open the Windows Defender Firewall with Advanced Security
Settings or run wf.msc
. Look for WSL in "Inbound" and "Outbound" tab.
The Disable Firewall for this NIC method 4
This will disable the firewall for the NIC "vEthernet (WSL)".
Note: Windows Defender will nag you about this.
# Requires -RunAsAdministrator
Set-NetFirewallProfile -Profile Public,Private -DisabledInterfaceAliases "vEthernet (WSL)"
To revert run this command.
# Requires -RunAsAdministrator
Set-NetFirewallProfile -Profile Public,Private -DisabledInterfaceAliases @()