Chris Bailey

Setting up a development environment on Windows 10

Typically, when I write code I target and use a UNIX-like environment; but when I'm not actively writing any code I want to use Windows to get the most out of my machine for leisure (games, non-kneecapped Amazon Prime / Netflix).

In other cases, my computing device when I'm not using my workstation has a sub-par experience on Linux. I frequently use various models of Microsoft Surface Pro because the 2-in-one functionality is great for consuming media on the go; but the Linux experience simply has too many caveats to be worth it in my opinion.

None of this is Linux's fault of course—but the problem is that it still means using Linux as a desktop OS in 2019 is too troublesome.

Approaches I have tried

I'll briefly enumerate all of the approaches I've tried to minimize the friction such a setup entails:

Of all the tried approaches, the Windows Subsystem for Linux (WSL) one was definitely the best. If you don't know what that is, you should probably search for it, but in short: it was similar to Cygwin in that it gives you a UNIX-like CLI on Windows, but it worked by intercepting and translating Linux syscalls to NT syscalls in real time, providing a real Linux userspace!

This was pretty frictionless, but performance wasn't the best (primarily because of Windows Defender and NTFS filesystem performance apparently), but it was good enough for most things. There were some issues because of the lack of a real Linux kernel though (no Docker, no rootfs, appimages didn't work correctly) but you could typically work around them.

While Windows Subsystem for Linux is pretty decent on its own, Microsoft decided to up their game by providing an alternative implementation of WSL: instead of translating syscalls, they would do the following:

  1. Run a real Linux kernel alongside Windows in Hyper-V
  2. Perform some NT-layer black magic to create super lightweight VMs with some Windows-interoperability related extensions
  3. Name it Windows Subsystem for Linux 2 and profit 💰💰💰

Before carrying on, there are two main things to note however:

  1. Windows Subsystem for Linux 2 (hereon WSL2) requires Hyper-V which means your Windows 10 version will need to support it, and at the time of writing this makes a machine using WSL2 incompatible with Virtualbox or VMware software.

Setting up WSL2

  1. Go get Windows 10 build 18917 or higher. Follow these instructions to get the insider builds..
  2. In an elevated Powershell prompt (Win+X), run the following and reboot:
    Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
  3. Head to the Microsoft store and download a distribution of your choice. I typically go for Ubuntu, but others are available.
  4. Once your distro of choice is installed, run the following in an elevated Powershell prompt:
     wsl --set-version <DISTRO> 2 

Well done! Now you should have WSL2 running, just open up your start menu and run your distro of choice, though I also run wsl --set-default-version 2 because I have no reason to drop back into the original WSL implementation.

You can install your distro of choice via the Microsoft Store. I usually go for the latest version of Ubuntu. If you're not happy with the selection of distributions on the store you can actually use your own rather painlessly with WSL2 (though I wouldn't know how because Ubuntu serves me well enough).

You'll notice that when you run your distribution, it'll open in the old Windows command prompt. This actually has amazingly low input latency, but there are a few quirks you can attribute to it's age. I'll outline some alternative options I'd look into below:

Terminal Emulators

There isn't too much choice when it comes to Terminal Emulators on Windows unfortunately, every single one of them has some tradeoffs to consider:

Foregoing any of the choices above, you can actually run an XServer on your WSL instance and have access to your favourite Linux native terminal emulator. I'll outline how to do that in the next section—I used to do this with Terminator but I recently just decided to make the switch to Windows Terminal and it's working great!

Setting up X11

It's my personal opinion that you should always run an XServer when using WSL2 because it fixes a really annoying issue you might not face immediately, but also it lets you leverage the entire Linux application ecosystem.

I personally use VcXsrv as my X11 implementation of choice on Windows because its the most performant implementation for high resolution displays (1440p+) and it's free. I've also paid for and use X410 which is pretty good too (but noticeably slower for full screen applications). I just have them set to auto-start alongside Windows and to boot up in multiwindow mode so all the applications I launch look just like any other Windows application.

Before firing up any applications, you'll want to add the following snippet to your .bashrc (or equivalent) since unlike the original WSL implementation, WSL2's localhost is not equal to your host OS's localhost:

WSL_HOST=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}')
export WSL_HOST=$WSL_HOST
export DISPLAY=$WSL_HOST:0.0

Now you can run konsole, gnome-terminal, urxvt or even vscode as though you were living in Linux entirely if you want 😄

If you end up doing this and you notice that even using VcXsrv the performance isn't great, make sure you have the following line added to your .bashrc (or equivalent) since by default, the actual rendering of GUI will occur inside WSL and will all be done in software. You can make Windows draw the GUI instead (and leverage your GPU and skip on network latency) with:

export LIBGL_ALWAYS_INDIRECT=1

You can set applications to automatically start on Windows by adding them to the following directory: %appdata%/Roaming/Microsoft/Start\ Menu/Programs/Startup/. You can make shortcuts to execute WSL applications with the following .vbs script:

Set oShell = CreateObject ("Wscript.Shell") 
Dim strArgs
strArgs = "wsl bash -c 'DISPLAY=WSLHOST:0 <APPLICATION>'"
oShell.Run strArgs, 0, false

Docker

When I was using the original WSL, I was forced to run Docker on Windows and connect to it from my WSL instance. Occasionally things just broke and this took time to debug and troubleshoot.

Even with WSL2 and the improved integration with Docker for Windows, occasionally if I didn't restart/shutdown my workstation for a few days I'd get strange networking issues between containers, I wouldn't be able to access my Postgres contains etc. I made due by simply force-restarting my WSL2 instance alongside Docker for Windows but this was a major grievance as occurences of it accumulated.

I decided that since WSL2 includes a real Linux kernel, to try running the native implementation of Docker... and it worked amazingly and hasn't broken once!

As a result, I won't bother detailing how to set up Docker for Windows, just make sure that when you sudo apt-get install docker-ce you remove any traces of Docker from your system which may have been used alongside Docker for Windows since that caused me a few hard to debug issues too.

This should work very smoothly assuming you simply sudo service docker start when your WSL2 instance boots up.

Misc Improvements

Conclusion

I can now game, watch Netflix at whatever bitrate/resolution I want, host servers/projects and develop in one place!

I hope that helps you set up and configure a WSL2 based development environment on Windows 10! There were definitely a few bumps along the way but I think it works pretty damn well when you consider the kafkaesque configuration of everything!


Return to Posts →