WSL performance tweaks that work for me

I enjoy using Windows Subsystem for Linux (WSL). As compared to, say, running a Linux virtual machine in VirtualBox, or running Cygwin, WSL provides high operability between a Linux development environment with its Windows host environment without taking too much system resource or sacrificing too much performance (read: good enough). It’s almost like the best of both worlds to me.

While I don’t expect WSL to perform as fast as bare metal Linux, I was surprised when I first read the article published on Phoronix on a benchmark test done by Michael Larabel. WSL (more specifically, WSL2) even surpasses bare metal Ubuntu Linux in some areas such as Python and Nginx performance.

But that is just a benchmark test. In a real development workflow, there are other factors that can slow things down.

If you tried to experiment with WSL by running random stuffs that you were able to run natively on bare metal Linux, it’s quick to realise that some of the hacky customisations that you’ve done have made your WSL run slower than it used to.

The quickest way to work around it, is to deregister your slow WSL distribution and create a fresh WSL distribution.

But if you would prefer not to start afresh, here are a few tips to improve the performance of your WSL distribution. Both are based on my experience and experimentation when using WSL.

In fact, even if you decide to start afresh by resetting your WSL distribution, these tips can still be useful to make your WSL experience even smoother.

Tip #1: Disable passing of the $PATH environment variable values from Windows

If your shell prompt doesn’t show up instantly after you enter a command each time, it could be due to the some custom output in your prompt requires it to run some other commands before showing the prompt.

Having a long list of paths in your $PATH environment variable makes it harder for your shell prompt to locate the commands it needs to get ready to be run. To check if that is the case for you, simply check your $PATH environment variable in your WSL with the following command:

echo $PATH

By default, WSL automatically adds the items in Path environment variables (of both system and user) in Windows into the $PATH environment variable in the WSL. This can be easily verified by checking Windows’ Path environment variable through the System Properties UI in Windows (Win + Pause, or search for “View advanced system properties from Windows start menu).

My WSL $PATH environment variable shows a surprising lot of paths that I don’t use by default

As you can see in the examples in Path system environment variable in my Windows, it’s apparent that many developer tools that I installed on my Windows added themselves into the the system Path environment variable.

Having your WSL $PATH environment variable configured the same way as the Path environment variable in your Windows system allows you to run the Windows executable files (e.g. explorer, regedit etc.) that you are able to run in Windows command prompt without the need to specify the full paths. However, in most cases, you probably don’t need all of them.

To override WSL’s default behaviour to automatically add the Windows paths into a WSL distro’s $PATH environment variable, we can specify the overriding behaviour in the wsl.conf file.

WARNING: If your workflow requires you to frequently run some Windows commands from WSL, this will cause them to stop working on your WSL environment. One common example is the code command, which opens up Visual Studio Code in the WSL directory you run the command from. To fix that, you will need to add the command’s path manually into WSL $PATH environment variable. See step #3 below for more details.

  1. In your WSL distro, open /etc/wsl.conf with your preferred text editor as root. I’ll use nano here. If the file doesn’t exist, go ahead and create it.

    sudo nano /etc/wsl.conf

    NOTE: If you prefer to configure the setting globally for all your WSL distros, you can edit the .wslconfig file in your Windows %UserProfile% folder instead. Refer to the documentation for more details.

  2. Look for the [interop] section. Change the appendWindowsPath setting under the section to false. If the section or the setting doesn’t exist, go ahead and add it into the file. Once you’ve added it, it should look like the following. Save the file to proceed.

    appendWindowsPath = false
  3. Optionally, if you use Visual Studio Code with WSL, you will need to add the path of VS Code’s code command into your WSL $PATH environment variable for it to work. In your WSL, replace [Windows user name] in the following command accordingly and add it into your rc file e.g. the .bashrc file for Bash shell

    export PATH="$PATH:/mnt/c/Users/[Windows user name]/AppData/Local/Programs/Microsoft VS Code/bin"
  4. For the setting to take effect, you’ll need to relaunch the WSL distro. To terminate your WSL distro, open the Windows command prompt or PowerShell prompt as the administrator, and then replace [WSL distro name] in the following command accordingly and the run it:

    wsl -t [WSL distro name]

    NOTE: To find out about your WSL distro name, run the command wsl -l -v. Once done, wait for 8 seconds, then open your WSL terminal as usual, your WSL distro should now be running with the new setting. To verify that the $PATH environment variable no longer includes the paths from Windows, run echo $PATH again. To test out if VS Code with Remote WSL extension is working correctly, simply run code . in any WSL directory to see if it opens VS Code in the correct WSL folder with Remote WSL extension.

Tip #2: Exclude WSL directories with executables from being scanned by Windows Defender/Security

I found this idea from multiple sources. Please be warned that this could make your system less secure. You should assess your risk for doing so based on your use case. Proceed at your own risk.

Windows Defender’s real-time protection on WSL bin directories can sometimes cause high CPU usage. By excluding Windows Defender from proactively scanning WSL bin directories, we can reduce the occasional slow down of WSL. For this, you’ll need to identify the true Windows folder paths of the bin directories in your WSL distro, and add them into Windows Defender’s exclusion list.

I’m only using one WSL distro, and since Windows Defender allows us to use wildcard to specify the exclusions, there are only a handful of paths and processes to be configured in my case. I use the following steps to configure the exclusions:

  1. Open Windows Security program by searching for it from the start menu.

    Search for Windows Security program with the Windows 11 start menu
  2. Choose the ‘Virus & threat protection’ menu on the left. You should see the ‘Virus & threat protection’ page.

    The ‘Virus & threat protection’ page on Windows Security program
  3. Then, under the ‘Virus & threat protection settings’ section, click on the ‘Manage settings’ link. You’ll be on the ‘Virus & threat protection settings’ page.

    This is the settings page for virus & threat protection in Windows Security
  4. Look for the ‘Exclusions’ section at the bottom. Click on the ‘Add or remove exclusions’ link. You’ll be asked to give the permission for Windows Security to proceed further. Go ahead and click the Yes button.

    Scroll to the bottom to see the Exclusions section for virus & threat protection settings in Windows Security
  5. You can now add the exclusions. Below are some of the common paths to be excluded based on what I’ve added in my case. Remember to change the folder path according to your user name and the distro you installed.NOTE: The exclusions for ‘process’ are basically directories where executable files are stored in WSL. You may add other directories based on your usage of WSL. For example, if you use Golang, you should probably add /usr/local/go/bin/*; if you work on Flutter and FVM, you should probably add $HOME/fvm/default/bin/* and $HOME/fvm/versions/stable/bin/* etc.

    • ‘Folder’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\bin\*
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\sbin\*
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\usr\bin\*
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\usr\local\bin\*
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\usr\sbin\*
    • ‘Process’ exclusion: C:\Users\[Kaiden]\AppData\Local\Packages\[CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc]\LocalState\rootfs\home\[kaiden]\bin\*
      The exclusion lists that I’ve configured for my machine

That’s it! Those are the two tips I have for now. Feel free to share with me yours too!

An Ode to Windows Subsystem for Linux (WSL)

I’ve always been enjoying running my Linux-based development workflows on a Windows machine with Windows Subsystem for Linux (WSL). Even on a day when I don’t do any development work, I would still open up my Windows Terminal to run the sudo apt update and sudo apt upgrade -y command. Keeping my Linux system up-to-date manually somehow gives me a sense of blissfulness.

I’ve always been more comfortable working with UNIX-based environments. I got in touch with Linux when I was attending my pre-U programme. I installed it on my desktop PC and used it as my daily driver. Then I started to meddle with Hackintosh to try out macOS. When I started working, the first personal laptop that I got myself with was a MacBook Pro. Throughout my days with Linux, Hackintosh and my first MacBook Pro, I just had to make sure that the machines are configure with dual boot (or triple boot when I started to use Hackintosh) for Windows. I always had to reboot the machine to switch between the OSes for apps or tools exclusive on each of them.

When Microsoft announced WSL a few years ago, I was so excited about it. Back then, my MacBook Pro was aging and I was looking to get a new laptop. But the then-current generation of MacBook Pro with the touch bar was really deterring me from getting one due to various issues other buyers were experiencing. I was considering to get a laptop that comes natively with Linux, but pretty much all major laptop brands here in Malaysia shipped their products bundled with Windows. Having dealt with hardware drivers on Linux on desktop machines in the past, I was concerned about the same issue on a laptop, given how limited after-market hardware customisation can be done on a laptop machine. So the release of WSL bridged my needs for using my UNIX-based development workflows on a Windows laptop and saved me from the difficulty in selecting the right laptop to purchase.

The version 2 of WSL was released not long after the first version of WSL was released. WSL2 has also been an improvement for my workflow. I find it noticeably faster then its predecessor. Microsoft’s partnership and collaboration with the Docker team to port Docker for Windows to be powered by WSL2 instead of Hyper-V or VirtualBox also gave Docker for Windows a performance boost. The seamless integration of Visual Studio Code within WSL also makes the development flow much smoother.

Building on top of WSL, Microsoft also recently released Windows Subsystem for Android (WSA). Although it is not officially available in Malaysia, a fan of WSL like me would figure out a way to get their hands on it. Given that Android is based on Linux, the possibility of having Android running in the Windows environment, just like how Linux can through WSL has long been speculated. WSA could make mobile apps development much easier for developers. At the time of writing, I’m still tinkering with my WSL and WSA to establish a smooth Flutter development workflow involving the two. The possibility to have these workflows running harmoniously and seamlessly simply excites me.

I really appreciate and applaud Microsoft’s effort in embracing Linux and FOSS in general, as opposed to strictly opposing them years before. As a result of this change of their stance, we get good stuffs like WSL, Docker for Windows, Visual Studio Code, and many of the enhancements on GitHub etc. It makes the experience as a developer much more pleasant for me.