If you’re a web developer and using Windows, then WSL2 is an exciting upgrade. It’s Microsoft’s completely new Windows subsystem for Linux and comes with some pretty fantastic performance improvements. This week I upgraded from WSL v1 to v2 and hit a few roadblocks with Local Lightning. WSL v2 switched to a virtualized environment which introduces some networking complexities. Here is how I worked around them.
Local Lightning with WSL v1 worked with a minor tweak.
Before I talk about WSL v2, let’s look at v1 and Local Lightning. Local Lightning is my go to for hosting local WordPress websites. It’s cross platform, fast and super easy to use. However attempting to use WP-CLI over WSL would give the following error message.
Error: Error establishing a database connection.
With WSL v1 this could easily be worked around by modifying the wp-config.php file to use define( 'DB_HOST', "127.0.0.1:<db-port>" );
rather then the default define( 'DB_HOST', 'localhost' );
. This allowed WSL to talk directly to Local Lightning’s database which runs on the Windows side.
WSL v2 networking makes talking to Local Lightning really tricky.
Since WSL v2 is running within a virtual environment, this adds a layer of separation between Windows and WSL. The above workaround fix for WSL v1 will not work with v2. This appears to be a hot topic of discussions as it prevents a wide range of services: https://github.com/microsoft/WSL/issues/4619. Rather then downgrading to WSL v1, I came up with the following workaround hack to get WSL v2 working with Local Lightning. This method could also be used to allow WSL v2 talk to other localhost ports served from Windows.
Steps to allow WSL v2 to talk directly to Window’s localhost ports.
The IP address that WSL uses changes whenever the computer is restarted. We can grab that new IP using some fancy windows commands within linux and store it as an shell environment variable. Add the following code to your .zshrc
or alternative startup bash profile.
export WSL_HOST_IP=$( cmd.exe /C netsh interface ip show addresses "vEthernet (WSL)" | grep "IP Address" | sed -e "s/\s*IP Address:\s*//g" )
This means $WSL_HOST_IP
will be the IP address that we’ll need to talk to instead of 127.0.0.1
. Now let’s adjust our wp-config.php
to using this IP address, but only if the shell environment variable exists. Otherwise we can default back to Local Lightning’s default. Be sure to change <db-port>
to the port number listed within Local Lightning.
$wsl_host_ip = getenv('WSL_HOST_IP');
if ( ! empty( $wsl_host_ip ) ) {
define( 'DB_HOST', "$wsl_host_ip:<db-port>" );
} else {
define( 'DB_HOST', 'localhost' );
}
This still won’t work for the following reasons:
- Windows’ firewall will block connections from WSL.
- Local Lightning isn’t serving the database over the virtual network IP address, it’s using
127.0.0.1
.
Both of these roadblocks can be overcome using PowerShell. I adapted my script from this user contribution. You may need to extend the ports section to cover your Local Lightning database ports. Create this as wsl-startup.ps1
and store somewhere like C:\tools\startup\
.
# Ports to forward. Includes common Local Lightning ports.
$ports=@(10000,10001,10002,10003,10004,10005,10006,10007,10008,10009,10010);
$ports_all = $ports -join ",";
# Fetch WSL IP address
$windows_address = netsh interface ip show addresses "vEthernet (WSL)" | findstr "IP Address"
$windows_address = $windows_address -replace " *IP Address: *", ""
# Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";
# Add Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_all -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_all -Action Allow -Protocol TCP";
# Remove all existing proxy rules
netsh interface portproxy reset
# Add new proxy rules
for( $i = 0; $i -lt $ports.length; $i++ ){
$port = $ports[$i];
iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$windows_address connectport=$port connectaddress=127.0.0.1";
}
This can be manually run within PowerShell like this.
Powershell.exe -executionpolicy bypass -File C:\tools\startup\wsl-startup.ps1
This script will allow connections from WSL to proxy through using the WSL IP address to the Window’s localhost. Running netsh interface portproxy show all
within PowerShell will reveal the mappings. Here is an example of what that looks like.
PS C:\tools\startup> netsh interface portproxy show all
Listen on ipv4: Connect to ipv4:
Address Port Address Port
--------------- ---------- --------------- ----------
172.29.16.1 10000 127.0.0.1 10000
172.29.16.1 10001 127.0.0.1 10001
172.29.16.1 10002 127.0.0.1 10002
172.29.16.1 10003 127.0.0.1 10003
172.29.16.1 10004 127.0.0.1 10004
172.29.16.1 10005 127.0.0.1 10005
172.29.16.1 10006 127.0.0.1 10006
172.29.16.1 10007 127.0.0.1 10007
172.29.16.1 10008 127.0.0.1 10008
172.29.16.1 10009 127.0.0.1 10009
172.29.16.1 10010 127.0.0.1 10010
Last, schedule this script to run each time the computer is restarted. This will recreate firewall and network proxy rules. Enjoy!
References: