Windows Subsystem for Linux(WSL) has been on the frontpage for sometime because of its bold claim to bridge the gap between linux and windows experience in a completely new way.

In this post, I will go over my experience of WSL(s) from a perspective of heavy Linux user and Java developer. The next post will include my take on making the environment reproducable.

Setting the expectations

I started using Unix around 1996 with Solaris 2.4 and Slackware 3.0 (was running Linux 1.2) Since then, Linux is part of my both personal and professional life every day,both for desktop and server purposes.

Therefore I consider myself a very difficult person to satisfy when it comes to my daily working environment. I am quite picky about my editors, settings, fonts, terminal, shells, habits, desktops (or lack of a desktop, I work on maximized windows)

So, how did I end up using WSL ?

But what is WSL ?

TL;DR

The Windows Subsystem for Linux lets developers run a GNU/Linux environment – including most command-line tools, utilities, and applications – directly on Windows, unmodified, without the overhead of a virtual machine. https://docs.microsoft.com/en-us/windows/wsl/about

WSL holds this promise almost 100%. How far you can go, depends on the version of WSL you pick.

Currently there are 2 versions of it. Even though you might think that you should just prefer WSL 2, I see some cases where WSL 1 can still be preferred by some audience.

WSL 2 toolchain (Windows 10 buils 19041 or later) allows users to have both versions their system, so you are free to experiment with both.

Main difference of WSL 1 vs WSL 2: Kernel

WSL 1 relies on LXSS Manager Service for the emulation of Linux system calls, therefore there is no virtualization. Given historical support for POSIX subsystem, I suppose extending that to cover all system calls was the first choice.

WSL 2 on the other hand runs a Microsoft maintained, real Linux kernel inside a heavily optimized, lightweight Hyper-V subset.

You might wonder how is that without overhead of a virtual machine, if it still involves a Hyper-V and then become tempted to use WSL 1 to avoid virtualization.

Overhead of a virtual machine is not just about the resources consumed. (I am much less worried about resources)

Overhead I see is more about management of it. Ultimately, user has to make some manual configurations, manage partitions, think about resizing, updating, backing up, reinstalling in case of a problem. I have always maintained my vm environment via Vagrant and it paid off, but If I can simplify further, I would do it. I do not want to manage virtual machines.

Contents of /dev /proc

WSL2 > WSL1

Since WSL 1 does not involve a kernel, there is no possibility to do anything with kernel modules and much of the /dev tree will be absent. Following is all you will have in WSL 1:

block fd kmsg lxss null ptmx pts random shm stderr stdin stdout urandom zero

Since /proc is populated by the kernel, it will be much less populated compared to a real environment. Therefore, if you a have code or use case that requires certain devices, proc files, you will hit a blocker. It happened to me once with an npm module (please don’t ask) This is not a problem for everybody though. It’s just, there is no workaround, you have to switch to WSL 2 or use a virtual machine.

WSL 2 on the other hand, uses a real kernel so there is no such problem. WSL 2 is clear choice if you require certain /dev, /proc files.

I/O performance

WSL2 > WSL1

This is one of the biggest complains about WSL 1. I/O is horribly slow. Even your oh-my-zsh prompts will suffer when you traverse Git directories. The sluggish I/O makes WSL 1 useless for non-trivial use cases (eg. building software)

I can recommend two tips to sooth the pain, slightly:

Exclude WSL 1 disk path from Windows indexing service and antivirus Overwrite $PATH in you shell because Windows automatically appends Windows paths which causes your shell to freeze while crawling through all if you mistype a command

On the other hand, WSL 2 I/O is quite satisfactory, even shows signals for being faster than Windows.

Although this can not be considered as a verdict or a comprehensive test, I compiled Apache HTTPComponents library using Maven 3.6 and OpenJDK 14 on WSL1, WSL2 and Windows (failed there)

I ran mvn clean package twice for each, first one to make sure dependencies are found locally in the second run.

Windows

[INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for Apache HttpComponents Client Parent 5.0-beta7-SNAPSHOT: [INFO] [INFO] Apache HttpComponents Client Parent ................ SUCCESS [ 7.282 s] [INFO] Apache HttpClient .................................. SUCCESS [01:29 min] [INFO] Apache HttpClient Fluent ........................... SUCCESS [ 14.984 s] [INFO] Apache HttpClient Cache ............................ SUCCESS [ 34.302 s] [INFO] Apache HttpClient Windows features ................. SUCCESS [ 10.357 s] [INFO] Apache HttpClient Integration Tests ................ FAILURE [01:03 min] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 03:40 min

WSL 1

[INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for Apache HttpComponents Client Parent 5.0-beta7-SNAPSHOT: [INFO] [INFO] Apache HttpComponents Client Parent ................ SUCCESS [ 5.888 s] [INFO] Apache HttpClient .................................. SUCCESS [01:19 min] [INFO] Apache HttpClient Fluent ........................... SUCCESS [ 14.239 s] [INFO] Apache HttpClient Cache ............................ SUCCESS [ 41.748 s] [INFO] Apache HttpClient Windows features ................. SUCCESS [ 10.096 s] [INFO] Apache HttpClient Integration Tests ................ SUCCESS [01:31 min] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 04:03 min

WSL 2

[INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for Apache HttpComponents Client Parent 5.0-beta7-SNAPSHOT: [INFO] [INFO] Apache HttpComponents Client Parent ................ SUCCESS [ 5.386 s] [INFO] Apache HttpClient .................................. SUCCESS [ 33.122 s] [INFO] Apache HttpClient Fluent ........................... SUCCESS [ 7.625 s] [INFO] Apache HttpClient Cache ............................ SUCCESS [ 15.095 s] [INFO] Apache HttpClient Windows features ................. SUCCESS [ 6.322 s] [INFO] Apache HttpClient Integration Tests ................ SUCCESS [01:04 min] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:12 min

I had also tried buildings some larger projects (120K LOC, tens of thousands of dependencies, couple hundred maven modules etc) on WSL 1 and Windows (did not try with WSL2) It turned out that building inside a VM in Windows is magnitudes faster than both.

I know how much things can differ when it comes to building a software, so again, this is not a conclusion. It’s just, I/O of WSL 2 is pretty good and definitely worths a try.

Accessing file systems from each sides

WSL1 > WSL2

Both WSL 1 and 2 lets you not just access files from both sides, but also allows executing binaries

Inside WSL, executing a Windows binary

berk@DESKTOP lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.3 LTS Release: 18.04 Codename: bionic berk@DESKTOP /mnt/c/Python38/python.exe --version Python 3.8.0

Inside Windows, executing a Linux binary

wsl -u berk -- lsb_release -a '&&' python --version No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.3 LTS Release: 18.04 Codename: bionic Python 2.7.15+

In both WSL versions, Windows drives are automatically mounted to Linux without any effort. (You can modify the mount point via /etc/wslconfig.ini)

This is a huge convenience compared to traditional ways of running Linux inside Windows (via hypervisors or docker). wsl command in Linux is able to convert paths between two sides, making scripts even easier to write.

However, accessing Linux file system from Windows using explorer or plain scripts works different between WSL 1 and 2.

Since WSL 1 is stores all files on your NTFS partition, you can directly access all of them without an extra efforts. You can just open PowerShell or explorer and browse to it. However, accessing files directly from Windows is not recommended. I occasionally did that to make brief editings and avoided writing from both sides. If you need frequent write access, using the \\wsl$ network share is the safer option.

However, WSL 2 content is in a disk image owned by the kernel and access is done over the network via 9P Protocol

This leaves only one option for now, accessing the files via hidden network share \\wsl$ on Windows. This share is available for WSL 1 too, as mentioned above, but I learned about its existence only after WSL 2. This is definitely inconvenient compared to WSL 1 experince.

As pointed out by @VirtualScooley, VSCode users can avoid this by using remote-wsl that allows running VSCode on Windows and seamlessly executing development tasks and accessing files in WSL.

Graphics

WSL1 > WSL2

WSL (both 1 and 2) does not have a framebuffer or a dri device. Therefore you always have to run an X server on Windows (e.g. Xming, X410, Vcxsrv This works pretty well. exporting the DISPLAY is enough to access them.

User can run IDE, or other native tools on Windows, directly accessing files on NTFS or run a good, GPU accelerated terminal like kitty or alacritty directly.

But why do I say WSL 1 is better in this ? Just because of convenience. See the next topic:

Networking

WSL1 > WSL2

WSL 1 has access to the same network interface with Windows, which also includes loopback. This is extremely handy because you can run Docker on Windows side and use it from Linux simply by letting Docker expose itself on the loopback interface via its settings. Sharing the loopback is great convenience.

Since WSL 2 runs under own kernel in a lightweight vm, it has its own network interface, including loopback. Therefore, you can access network ports on Windows side, only if they bind on an external interface (I do not know if we can create an internal network for that purpose, but putting effort for that as a user, defeats the purpose of WSL)

After that, you need to let Windows firewall allow access to that port (It will pop up and ask when you try to access from Linux)

Also, if you have a corporate laptop that has very strict policies, you may not even have a possibility to play with firewall rules.

Following this, user can still run X on Windows and export DISPLAY in WSL to access that.

Alternative is, running an OpenSSH server inside WSL 2 and tunneling X11 and other service ports to WSL 2.

However this is not the end of the problems. When your Windows goes to sleep, or network interface change address, your socket connections will die and X applications will crash.

Therefore, I find this very cumbersome, difficult to work, even prone to security issues. (I don’t want my X11 or Docker service to bind to a physical interface, even if there is a firewall)

I just want to have an easy network shortcut between Windows and Linux via a loopback interface.

UPDATE Feb 27, 2020 Apparently Docker Desktop has an experimental support for WSL 2. Enabling it will install a proxy inside WSL that will allow user to run docker commands without messing with network connections. DOCKER_HOST environment variable always wins over everything, so you should unset it, if you are going to use this feature.

Conclusion

I had immediately jumped in to WSL 1. Even though I/O performance makes it impossible for me to use it for development purposes (still, depends on what you do for development), I kept using it every day because having a seamlessly integrated, working native Linux shell without an effort is very valuable. (Bye bye cygwin)

WSL 2 fixed this problem but brought another one due to seperation of network interfaces. However, if you are a heavy Vim/Emacs/screen/tmux user, you will be probably very happy with it. (I am living inside Intellij IDEA and sometimes VS Code)

The only reason I use X is to run kitty/alacritty and actually terminal does not have to run inside Linux. Microsoft is actively developing a brand new terminal here. I find this very promising and I am pretty sure it will eventually replace cmder for me, as Windows terminal emulator. Therefore I do not see the lack of framebuffer or still having to use X in 2020 as a complete blocker, constant flow of improvements make me trust that those will also be solved.

I would definitely recommend giving WSL a try. It is an amazing work from awesome, dedicated people and I am pretty sure that, even if you find it is not fully ready for your heavy lifting, you will keep bunch of WSL terminals open around.

The convenience of WSL is addictive.

Edit 1: Thanks to @VirtualScooley for letting me hear about remote-wsl extension VSCode, Jan 17, 2020

Great write up :). If you’re a VSCode user and haven’t tried remote-wsl extension you should. It does all of the work to make vs code on windows target wsl for all dev stuff including extensions and debug tools. Works with wsl 1 and 2https://t.co/Aago3S3V8e — scooley (@VirtualScooley) January 17, 2020

Edit 2: Thanks to @nelson for heads up about emulation, and official warning of Microsoft for accessing WSL files from Windows , Jan 24, 2020