man using a laptop

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.

    [interop]
    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!

Leave a Reply

Your email address will not be published.