Version control for your WSL distribution with a Dockerfile
Disclaimer: I do not work on the WSL team at Microsoft, and this article represents my experience using WSL in the past months and creating custom distributions.
I’ve been using WSL2 as my primary development environment ever since its launch, together with VS Code Remote WSL and the new Windows terminal. My particular workflows allow me to exclusively use the WSL filesystem and tools, so almost my entire work happens to be a combination of the terminal and VS Code. This means I rely on the configuration of my WSL distribution quite a bit, and I want to be able to quickly re-create it in case of a re-install, or replicate my environment for testing.
Up to this point I didn’t have major issues with Windows or WSL, so I didn’t need to re-install my system yet. I also don’t claim that the experience is perfect, and since WSL2 requires Windows Insiders, proceed with care and at your own risk. This article is not trying to convince you to switch to WSL2, nor it is a step-by-step tutorial on how to configure it. Rather, it is me documenting a declarative approach to creating distributions already configured with the tools for your workflows, and how to version control them.
Back-up your WSL distribution
The wsl.exe
tool comes with an --export
command that allows you to export
the file system of your distribution to an archive. This is the easiest way to
back-up your distribution if you plan on re-installing your system.
Additionally, if you manually configured your instance, you can back-up the
entire virtual hard disk.
In both cases, you are creating a snapshot of your file system that contains all files, including temporary files - meaning that your back-up could be tens of GB in size. You could start cleaning up temporary files, downloads, and other unnecessary files, but if you only want a clean environment that contains all of your tools, there could be an easier solution.
Import a distribution from a filesystem
You can import a distribution from an archived filesystem. That filesystem can
be generated using the --export
command, but this is not a requirement - any
valid Linux filesystem should be able to be imported as a WSL distribution (I
haven’t found documentation yet on things required in the filesystem - I’ve been
able to import distributions based on both Debian and Arch - but check this
issue if your distribution has symlinks for tools in /bin
or /sbin
).
A very neat way of defining a Linux filesystem is to create a container (which
is made up of a filesystem, among others), then export its filesystem as a
.tar
file - which is exactly what you can import in WSL. This would allow you
to:
- create a Dockerfile containing the desired state of the distribution
- build a container image based on the Dockerfile
- run a container based on that image and export its filesystem as an archive
- import the archive as a WSL distribution
Put the Dockerfile and configuration files in a git repository, and you built yourself version control for your distribution.
Writing a Dockerfile for a WSL distribution
For a complete example have a look at this repo - but you can start from a base distribution - Ubuntu 18.04 in this case, and install all the tools you need.
The example below is based on my Dockerfile for building distributions - because
I don’t want to always run as root
, this creates another user, adds it to the
sudo
group, and gives the new user ownership over /home/radu
. I still get
sudo
access, but the password is required. (Setting up the additional user is
optional, and not a required step for a working WSL distribution.)
The user password is provided as a build argument - using the
--build-arg pwd=<password>
flag. While this is better than hard-coding the
value in the Dockerfile, keep in mind that the value is present in the layers of
the built image - so be careful if you are publishing the image to a container
registry. You can always put a generic password and change it after booting up
the distribution.
FROM ubuntu:18.04
ENV HOME /home/radu
RUN apt-get update
RUN apt-get install -y \
sudo \
<instal all tools you need>
RUN useradd -ms /bin/bash radu
RUN usermod -aG sudo radu
RUN sudo chown -R radu /home/radu
ARG pwd
RUN echo radu:${pwd} | chpasswd
USER radu
Finally, one thing this Dockerfile does not handle is configuring private keys and passwords. If you are absolutely sure the image will never leave your machine, there is nothing stopping you from copying signing keys, or tokens, or passwords and configuring them.
Importing the distribution into WSL
First, build the image based on your Dockerfile:
$ docker build -t my-custom-fs .
$ docker run --name fs -it -d my-custom-fs
$ docker export --output my-custom-fs.tar fs
The size of the filesystem depends on what tools you configured. A blank Ubuntu 18.04 filesystem starts at around 60MB, and for example, my configuration is around 4 GB (but it contains multiple SDKs and frameworks, a container runtime, and a full browser, among others).
At this point, you can import the distribution:
$ wsl --import my-custom-distro .\my-custom-distro-location\ .\my-custom-fs.tar
$ wsl --list --verbose
NAME STATE VERSION
my-custom-distro Stopped 2
If the version of your distribution is 1, you can convert it using wsl
, or
directly set the default version to be 2. Check the WSL
documentation for more on this topic. If you are using the new
Windows terminal, the distribution should be automatically added to your
configuration list after restarting the terminal. Otherwise, you can manually
open a shell to your distribution using wsl
:
$ wsl --distribution my-custom-distro
root@surface:~#
If you configured a user, you can pass it to the wsl
command using the
--user
flag, or you can configure the Windows terminal to always login as that
user:
"commandline": "wsl.exe --distribution my-custom-distro --user radu",
Conclusion
In this article, we explored a way of pre-configuring a WSL distribution using a Dockerfile. You can use this method to configure all the tools you use regularly, and version your environment using git.
Thanks for reading!