WSL Localhost Fix
A short one for the fellas in the back that use WSL, like me.
I love WSL. I’ve been using it since WSL v1 got announced a few years ago, and I haven’t looked back since. WSL is perfect for me because it allows me to use Windows as my main operating system for browsing, gaming, and C#, while also having all the standard Unix tools I’ve come to learn and love right at my fingertips. No longer are the days of dual booting or using VirtualBox for Unix tools! Long live WSL!
That said, it isn’t perfect. As of WSL 2 (19043), there are some issues with file polling that makes automatic updates wonky at times, and networking can be really fucky at times. Localhost domain name resolution only works half of the time and has been a real pain in the ass for me lately. It’s hard working on something like, say, a blog without being able to connect to a dev version on localhost. I mean, I could install Ruby on my Windows host, but then I also have to install MSys2 to emulate the Unix environment ruby needs to work properly, and that beats the entire purpose of having WSL in the first place. So instead of bogging down Windows with a bunch of crap, let’s fix WSL.
First off, let’s establish how the two operating systems communicate with each other. As of WSL 2, the Linux kernel is virtualized and uses a custom network adapter to communicate with the host and the internet. This adapter works out of the box, but for some reason localhost breaks after putting windows to sleep. When broken, WSL’s localhost resolves to 127.0.0.1
, but doesn’t communicate with Windows’ 127.0.0.1
for some reason. Reinstalling Windows’ network adapters via Windows Network Reset seems to fix the issues temporarily, but as soon as you put the machine to sleep again, networking breaks again. (Some GitHub issues on the WSL repo report that turning off Windows Fast Startup in power options can fix this, but this didn’t work for me.)
I believe the issue here boils down to how localhost is implemented in WSL and Windows. In WSL, localhost resolves to 127.0.0.1
, but in Windows the default localhost resolution is ::1
. Even if I change WSL’s /etc/hosts
file to point localhost to ::1
, communication fails, and I think it’s because WSL’s network adapter only supports IPV4. I have no confirmation on this though, I could be wrong.
Either way, I found that communication between the VM and Host is still possible! You have to use the IP that HyperV assigns to WSL every time it starts up, and this IP changes every time you initiate WSL. (i.e. Start a new session while no other sessions exist in the background.) I also saw that people online designed a bash script for WSL that modifies Windows’s /etc/hosts
upon startup to add WSL’s IP to that file, but I figured that messing with perms for critical files in System32 just to make a minor convenience work probably isn’t the best idea. So I came up with this script instead:
#!/bin/bash
# WSL disclaimer
# colors: Cyan=\e[36m Yellow=\e[33m Green=\e[32m Reset=\e[0m
echo -e "\e[36mStarting Jekyll Server with WSL IP workaround...\e[0m\n"
echo -e "\e[33mWARNING: Because WSL localhost networking breaks for some on the latest WSL build,"
echo -e "we have to host the server using the dynamic WSL IP that changes every time WSL"
echo -e "is reinitiated. Absolutely stupid. You may have to double ^C to SIGINT btw.\e[0m\n"
# get current wsl IP
WSLIP=`ifconfig | grep eth0 -A1 | tail -n1 | cut -d ' ' -f10 `
echo -e "\e[32mCurrent WSL IP: $WSLIP\e[0m\n"
# start server
bundle exec jekyll serve -Dl --host=$WSLIP --force_polling --future
It works for now.
Ignore the fact that the above screenshot says that ^C
is SIGTERM
. I know it’s SIGINT
. I had a brain fart, ok?
Supposedly this has been fixed already and is in a later release of WSL 2 that will never reach Win 10 now that Win 11 exists. Here’s to hoping Windows 11 actually has this fix, and also eventually becomes stable enough that upgrading doesn’t become a huge regret. Till then, I guess I’ll stick with my current jalopy of a toolchain.
Next post soon™.