Configuring Windows Subsystem for Linux (WSL2)

Posted

Here is how I configure Windows Subsystem for Linux (WSL). I do things a bit strangely: by changing the mounts, by running a GUI Desktop, and by running Docker in WSL2 and not on the Host...

Pre-requisites

To get WSL on Windows 10 Pro, head over to Turn Windows features on or off and enable Windows Subsystem for Linux (WSL), which in turn needs Windows Hypervisor Platform (Hyper-V).

Turn windows features on for WSL and Hyper-V

Set WSL to version 2:

wsl --set-default-version 2

Once that is done, open the Microsoft Store and download a WSL distro of your choice. I downloaded Ubuntu:

Searching for WSL2 distros on the Windows Store

A new Start Menu entry for Ubuntu will be created, and on running it, a Ubuntu will be installed and prompt for a root username and password - I use usr as the username.

After that, you will be dropped into a Linux prompt (bash), in what is essentially a terminal window.

After my initial install, I did wsl -l -v only to discover I was on WSL version 1. I probably ran Ubuntu once before --set-default-version... if you have issues, follow Microsoft’s documentation, Step 4 - Download the Linux kernel update package and Step 5 - Set WSL 2 as your default version.

To upgrade the previously Downloaded distro:

wsl --set-version Ubuntu 2

Aside: I had a post sometime back, where I discovered VirtualBox running very slow after Hyper-V was enabled - the workaround is a boot menu to turn off Hyper-V in order to use VirtualBox. The two virtualization technologies do not play well together!

Cloning the instance

WSL2 creates a virtual hard disk (.vhdx) for Ubuntu somewhere. So first thing I did was to copy the distro to a location of my choosing. This way I can muck about and then still fallback to the distro if anything goes wrong without having to delete (unregister) and download it again.

wsl --export Ubuntu - | wsl --import UbuntuDesktop [D:\VMs\UbuntuDesktop] -
wsl -l

If you want to keep a clean copy of the distro, use wsl --export Ubuntu ubuntu.tar instead. Using - as above pipes STDOUT to STDIN, so that I bypass saving to a file.

You can set the new copy as the default distro:

wsl -s UbuntuDesktop

Or, start the distro explicitly (remember to specify the username, or else WSL defaults to root):

wsl -d UbuntuDesktop -u [usr]

Changing default mount behaviour

The default configuration mounts all Windows volumes as read-write. You can check with mount which will list the Linux mount points mapping all Windows partitions, e.g.:

rootfs on / type wslfs (rw,noatime)
...
C:\ on /mnt/c type 9p (rw,noatime,dirsync,aname=drvfs;path=C:\;uid=0;gid=0;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8)
D:\ on /mnt/d type 9p (rw,noatime,dirsync,aname=drvfs;path=D:\;uid=0;gid=0;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8)

I prefer to explicitly allow / deny mounts, to limit read / write access, so I configure WSL2 not to mount any folders by editing /etc/wsl.conf:

[automount]
enabled=false

Now I can mount the specific folders I desire (the example below is as read-write, but you can add -r to make it read-only). To automatically do this, I edit ~/.bashrc:

sudo mount -r -t drvfs -o 'noatime,dirsync,aname=drvfs;uid=0;gid=0;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8' 'D:\VMs\UbuntuDesktop' /mnt/d

Finally, to allow mounting and unmounting without superuser password, add this to /etc/sudoers:

usr ALL=NOPASSWD: /bin/mount
usr ALL=NOPASSWD: /bin/umount

In limiting mount points and allowing only read-only access files (principle of least common mechanism?), I am breaking the principle of least privilege by allowing passwordless mount - so maybe do not do this! There may be a better way, I just have not figured it out...

I have a Linux drive in my system too, so I’ll update this when the next version of WSL allows --mount-ing of physical ext4 partitions.

GUI apps via Remote Desktop

To run GUI apps without requiring to install an X Windows Server on Windows, I figured a Remote Desktop connection would suffice.

So, in the Ubuntu instance:

sudo apt update
sudo apt upgrade
sudo apt install -y xrdp xfce4

The installation of the XFCE Desktop and xrdp server took "864 MB of additional disk space" for me.

Just starting xrdp and connecting via Remote Desktop Connection client failed with the error below. I think it’s because the client is hard-coded to reject connections to localhost on the default port, despite the fact that the local Remote Desktop Services service is not running...

Remote Desktop error connecting to localhost on port 3389

To get things working, the minimum changes I needed to make before starting xrdp were:

sudo sed -i 's/port=3389/port=3388/' /etc/xrdp/xrdp.ini
echo xfce4-session > ~/.xsession
sudo service xrdp start

Now I can connect to localhost:3388 via Remote Desktop:

Remote Desktop Connection

In WSL1, a lot of things did not work including any browser I tried, e.g. Firefox would crash with Channel error: cannot send/recv.

But WSL2 is great! I installed Firefox with sudo apt install -y firefox and it ran just fine!

If I recall, at some point I also tried installing Google Chrome, and that worked too:

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb

Installing Docker Engine

One could install Docker Desktop for WSL2, but alternatively just directly Install Docker Engine on Ubuntu - the less installed in Windows, the better!

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo service docker start
sudo docker run --rm hello-world

That worked for me and I got “Hello from Docker!”

Again this would never have worked in WSL1, I’d get errors like failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N and so on...

Terminating WSL2

At this point you will have many, many Linux processes running in the background, even after you close Remote Desktop or logout from the console.

You could of course stop the running processes, e.g. sudo service docker stop, etc. but otherwise, just remember stop the instance when you are done with it:

wsl --terminate UbuntuDesktop

Or terminate all running intances and the WSL2 VM itself with:

wsl --shutdown

Conclusion

So that’s how my WSL2 Linux environment is setup.

Since I’ve stuffed my image with a desktop, UbuntuDesktop ext4.vhdx image is almost 4.5 GB. That is nearing the size of my full Ubuntu Desktop 20.04 Hyper-V VM, which is 6 GB. I guess that makes sense, seeing that WSL2 uses Hyper-V...

But I still don’t understand why I can’t just manage WSL2 via the Hyper-V Manager. Wish I could just take a simple snapshot to rollback, instead of resorting to cloning the distro as previously mentioned.

On a final note, if like me, you mainly only run Docker... then perhaps Alpine Linux would suffice - remember that some commands do change slightly including apk instead of apt, sudo is not installed, service is not used etc.:

su - 
umount /mnt/c
apk add --no-cache docker
dockerd

You can go full inception with nested virtualization. I have tried Windows > WSL2 with Docker Engine > Ubuntu container with xrdp. Since everything is run in a container, then the WSL2 host volumes need not be exposed, so one could ignore the mount setup above. Maybe.

Good luck!