Using WSL-2 as a dev environment

15 Jan 2021

After several trials with native Windows, WSL-2 and docker containers, I finally decided to base my development environment inside WSL-2. WSL stands for Windows Subsystem for Linux, it runs a native Linux distribution inside Windows.

I have one WSL distribution as my primary shell for day to day tasks and one distribution per big project I'm working on. Here's how I usually setup a new project.

Introduction

I noticed that without special care to the programs and dependencies I install on my machine, the more projects I work on, the messier my OS gets. Particularly when working on two projects that require different versions of the same tools or programs. Luckily, most languages mitigate this issue using virtual environments (pip, conda, npx, etc.)... But there are often cases when I need something that can't be managed by a language's virtual environment.

I tried to find a uniform approach where every project would be isolated from the OS in a uniform manner. A theoretically perfect approach would be to spin up a docker container pre-configured with the development environment for each project. That way, no need to pollute the host with dependencies and languages versions. And this would also allow to run / restart the project several years later without worrying about everything being broken due to different versions of everything — which could be a real issue: as of today I can't run my first C/C++ programs due to non-existent dynamic libraries.

The operating system: Windows 10

I'm a big fan of Linux and if it weren't for missing drivers, I'd be running one of its distributions right now. But some components on my PC are not well handled on Linux so I'm stuck on Windows 10. Switching from MacOS & Linux to Windows a few years ago was extremely painful, but I finally getting used to it and I even started learning PowerShell (which is pretty great).

The major drawback with Windows for developers is that most installation or configuration instructions are written for Linux. A readme on GitHub is usually not written for chocolatey and powershell but rather for apt-get and bash.

The trick: WSL-2

But there is WSL-2, the Windows Subsystem for Linux, a lightweight virtual machine running a Linux Kernel created by Microsoft (WSL2-linux-kernel) to embed Linux inside Windows. It's not perfect (yet) but it works great. According to here and here using WSL-2 to compile java/maven projets could even be faster than using java on Windows (!!). These pages benchmark the performance of WSL-2 vs Windows and native Ubuntu. I'm very surprised to see that in some benchmarks, WSL-2 perform better than Windows or native Ubuntu. This article on Ulsoy's blog is an interesting read about his experience using WSL.

Let's setup a new development environment in WSL-2.

Requirements

First, install WSL-2.

Then you need a clean distribution to import into WSL. Here's how to create one easily: Download your distribution of choice from the App Store (I chose Ubuntu 20.04 LTS). Open the newly installed distribution once, and immediately close it to register the distribution inside WSL.

Open a new PowerShell terminal, list the distributions available using the following command and find the one you just installed:

powershell
wsl --list

Then, export it to create an untouched copy of the distribution (In my case, it's called Ubuntu):

powershell
mkdir ~\Documents\wsl-distributions
wsl --export Ubuntu ~\Documents\wsl-distributions\ubuntu-20.04LTS.tar

Now, we're ready to create a new project.

Setting up a new project

When working on a new project big enough to justify having its own environment, I create a new WSL distribution for the project. To do so, I'll simply import a copy of the pristine Ubuntu distribution created in the previous section:

powershell
wsl --import ProjectName ~\AppData\Local\Packages\WSL.ProjectName ~\Documents\wsl-distributions\ubuntu-20.04LTS.tar

To start the distribution, use this command:

powershell
wsl -d ProjectName

This will open a new root terminal inside the distribution. Let's create a non-root user and configure it as the default user for this distribution. To do so, run these command inside the root WSL terminal:

bash
useradd -s /bin/bash -d /home/user/ -m -G sudo user
passwd user
printf "[user]\ndefault=user\n" >> /etc/wsl.conf

Then exit the terminal, and inside powershell, restart the distribution:

powershell
wsl --terminate ProjectName

When reopening the distribution, you should be logged in as the newly created user. As we saw earlier, type this line into PowerShell to open the distribution:

powershell
wsl -d ProjectName

And that's it, you now have a new distribution that you can setup like any other.

Additional configuration

Shell prompt

To avoid mixing up my terminals, I like to edit my shell's prompt to display my distribution's name. To do so, edit the PS1 variable in your .bashrc:

~/.bashrc
if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@$WSL_DISTRO_NAME\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@$WSL_DISTRO_NAME:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@disro-name:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@$WSL_DISTRO_NAME: \w\a\]$PS1"
    ;;
*)
    ;;
esac

To ease access to the files inside Windows (for instance, to copy downloads into WSL), I like to create a shortcut as follows (change julien by your windows username):

ln -s /mnt/c/Users/julien ~/winhome

Graphical applications

It is possible to run graphical applications inside WSL. To do so, you need to install an X-Server on windows and configure the WSL distribution to connect to this server.

I use X410. Here's how to set it up for WSL-2. Once the X-Server is ready, edit your WSL environment to connect to it:

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

Custom keyboard layout

When running GUI apps through the X-Server, the keyboard layout needs to be installed in the distribution. Here's how I setup my Colemak layout:

sudo apt install x11-xkb-utils x11-xserver-utils
wget https://colemak.com/pub/unix/colemak-1.0.tar.g
tar -xvf colemak-1.0.tar.g
cd colemak-1.0/
setxkbmap us; xmodmap xmodmap/xmodmap.colemak && xset r 66

Configure ssh keys

Generate a new SSH key:

ssh-keygen -t ed25519 -C "$USER@$WSL_DISTRO_NAME"

To copy the newly created public key to the clipboard, use this command:

cat ~/.ssh/id_ed25519.pub | clip.exe

Then, follow these instructions to add the newly created key to your GitHub account.

Instead of the traditional ssh-agent, it is more convenient to use keychain inside WSL: (copied from this source)

sudo apt-get install keychain

Then, edit your ~/.bashrc file to load the keys automatically:

~/.bashrc
# For Loading the SSH key
/usr/bin/keychain --nogui $HOME/.ssh/id_ed25519
source $HOME/.keychain/$HOSTNAME-sh

Tips

Access Windows clipboard

To access Windows clipboard, use the clip.exe executable. For instance:

echo "test" | clip.exe