Local Lightning and Upgrading to WSL2

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', "<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 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:

  1. Windows’ firewall will block connections from WSL.
  2. Local Lightning isn’t serving the database over the virtual network IP address, it’s using

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_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=";

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
--------------- ----------  --------------- ----------     10000       10000     10001       10001     10002       10002     10003       10003     10004       10004     10005       10005     10006       10006     10007       10007     10008       10008     10009       10009     10010       10010

Last, schedule this script to run each time the computer is restarted. This will recreate firewall and network proxy rules. Enjoy!