Blog Cover

Accessing network apps running inside WSL2 from other devices in your LAN

Aitor Alonso

Aitor Alonso

Jul 30, 2021

4 min read

Hi guys!

Today, I come here with another mini-tutorial to make programming on Windows a little easier with the help of the WSL 2.

Imagine you are developing an API, web app, website, etc. on your Windows machine under WSL2. For example, in my case, my own website. I use the React framework Next.js, and yarn as package manager, so if I want to start the development server to check my site I just do:

yarn dev

With the above command, the development server will start and listen on port 3000. It will serve the page to every device that can reach it, as it's serving the site on the 0.0.0.0 address by default.

Starting the dev server of my website

Then, if I visit localhost:3000 on my Windows machine, everything works. The server returns my live website. However, what happens when I try to open my live site from my mobile phone? I navigate to <my_windows_local_ip>:3000 and then

The site can't be reached

The solution: forwarding ports in WSL 2 NAT

WSL 2 creates a virtualized Ethernet adapter with its own unique IP address, so in fact it creates a NAT between your WSL instance and your Windows host computer. So to be able to access to applications running in your WSL from within your Local Area Network (LAN), you must port forward the desired ports.

The solution is to add a port proxy in the Windows Firewall, so we open a PowerShell terminal with administrator rights and execute the following command:

netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=<the_wsl_ip>

You can get the WSL virtual machine IP right from PowerShell, by executing bash:

bash.exe -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"

Or the same from within the WSL instance:

ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'

After executing the netsh interface command in an administrator PowerShell, the server can now be reached without further configuration needed, nor restarting the server.

Now it works!

Bonus: a PowerShell script to simplify this task

The IP address of the WSL VM changes every time it reboots (like when you power off your Windows), so if you have already added a port proxy for an IP address, it might not work if you restart your computer.

I have created a PowerShell script to simplify this task when you need it to be done. You could also configure a Windows task to execute this PowerShell script on every reboot, if you also start WSL on boot (because WSL needs to be launched to get its IP address). Or could be a way to executing it from WSL once it starts. I'm not explaining here how to achieve this, as I don't have configured it that way. I don't usually access apps running inside WSL from my LAN, so when I need to do this, I just execute the following script in an administrator PowerShell terminal (explanation below):

$remoteport = bash.exe -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"
$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if( $found ){
  $remoteport = $matches[0];
} else{
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

#[Ports]

#All the ports you want to forward separated by coma
$ports=@(80,443,3000,3001,3002,3333,5000,5432,6000,8080,8081,19000,19001);


#[Static ip]
#You can change the addr to your ip config to listen to a specific address
$addr='0.0.0.0';
$ports_a = $ports -join ",";


#Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";

#adding Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

What this script does is:

  • Gets the IP address of the WSL VM.
  • Defines the ports you want to proxy/forward. You can therefore edit the $ports array as you wish. Here I have written all the ports I could potentially use, so the script will be valid for multiple apps/technologies.
  • Adds Windows Firewall rules to allow all inbound/outbound traffic to the WSL. Also removes old rules from previous script executions (with different IP addresses).
  • Adds the required port proxies, at the same times that removes old ones from old script executions (with different IP addresses).

If there is the first time that you execute this script, or you have added a new port, you can get a lot of not found messages. These are normal warnings that you can safely ignore, as these occur because the script is trying to delete non-existent firewall rules (first time execution) or port proxies (when you add a new port).

I hope this serves you to improve your developer experience in WSL and make you more productive. See you soon!

--
(You can find comments to this article in dev.to)