Just recently Microsoft released its latest update to Windows and with it comes an update to WSL. Previously it was using a translation layer that translated Linux system calls into NT system calls. This allowed running unmodified native ELF binaries directly under Windows albeit with some restrictions. It’s exactly those restrictions that made it impossible to run Docker natively on the subsystem. The reason for this is that for a good chunk of the API’s to make namespaces, cgroups, … work the implementation just wasn’t there and seemed non-trivial to implement. This all changed as of WSL2 as Windows now ships with a full-fledged Linux kernel, using virtualization technologies to improve file system performance and adding full system call compatibility.
To get Docker running natively under this recent version, you can just install the latest Docker for Windows. The installer will guide you through the process and offer the option to use docker under WSL 2. The process is straightforward and from my experience, it makes using docker under Windows even more reliable and giving it that native feeling. The way it works is that the installer will create a new WSL instance under which it installs the whole docker toolchain. You can have a peek at this by running
wsl -l -v which should print something similar like the following:
NAME STATE VERSION
* Ubuntu-20.04 Stopped 2
docker-desktop-data Running 2
docker-desktop Running 2
docker-desktop* instances that will host docker and any containers or images. There is a flag buried in the
Docker for Windows settings menu that allows you to expose this same docker environment to your other WSL instances.
Settings>Resources>WSL Integration has a toggle for each WSL instance. The way this works is that it will add a few binaries to that WSL instance and a proxy that forwards any requests.
$ ps aux
root 1 0.2 0.0 892 576 ? Sl 10:35 0:00 /init
root 6 0.0 0.0 892 80 ? Ss 10:35 0:00 /init
root 7 0.0 0.0 892 80 ? R 10:35 0:00 /init
username 8 1.0 0.0 10040 5072 pts/0 Ss 10:35 0:00 -bash
root 45 0.0 0.0 892 80 ? Ss 10:35 0:00 /init
root 46 0.0 0.0 892 80 ? S 10:35 0:00 /init
root 47 1.0 0.1 563128 20060 pts/1 Ssl+ 10:35 0:00 /mnt/wsl/docker-desktop/docker-desktop-proxy --distro-name Ubuntu-20.04 --docker-desktop-ro
username 54 0.0 0.0 10604 3296 pts/0 R+ 10:35 0:00 ps aux
But what if you want to run and manage your own setup without using
Docker on Windows. Well, that’s possible too! Remember we are running on a full Linux kernel now. So, for whatever distro you are using now, you can follow the standard docker installation instructions for that distribution. I’m using Ubuntu so let me walk you through the process of setting this up. If you are running another distro have a look at the docker documentation here: https://docs.docker.com/engine/install/
First let’s update our packages and install some dependencies that are required in order to get docker up and running.
sudo apt update
sudo apt install \
Next, we will need to grab the official docker PGP key.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Have a look at the official docker installation docs to help you in verifying that the key you just installed is the correct one. It just boils down to this:
sudo apt-key fingerprint 0EBFCD88
pub rsa4096 2017-02-22 [SCEA]
9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid [ unknown] Docker Release (CE deb) <[email protected]>
sub rsa4096 2017-02-22 [S]
Run the following command to get the docker repository set up:
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
Ok, that’s all the setup and preparation work we needed to get done. Now let’s get our hands dirty and install docker. If you prefer to install a specific version, then have a look at the official docs again. Those will explain what you need to change to the following command to install that specific version.
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
During the installation process it will ask to configure and setup grub. You can safely skip this procedure because the WSL Linux kernel doesn’t use grub.
At this point, we should have docker installed and available to use, we can verify this by running the following commands:
$ which docker
$ docker images
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
We can see that we have docker installed, but when running it we get an error message saying that we can’t connect to the Docker daemon. WSL doesn’t use systemd so docker won’t be able to start on its own. We will need to start the daemon manually by using the following command:
sudo service docker start
After this, the last thing you need to do is to add your current user to the docker group. This will allow you to use the Docker cli without typing
sudo usermod -aG docker $USER
For this to take into effect we will need to restart our instance
wsl --terminate name-of-wsl-distro or
wsl --shutdown (caution this last one will shut down all your WSL instances).
You will notice that when you restart your WSL instance the docker daemon is not automatically started again. At the moment there isn’t a recommended way to start the daemon on boot. The current recommendation https://github.com/microsoft/WSL2-Linux-Kernel/issues/30#issuecomment-558241868 is to launch the daemon in your
We will need to edit the
sudoers file with
sudo visudo and add the following line at the bottom of the file, replacing
username with the name of your current user.
username ALL=NOPASSWD:/usr/sbin/service docker start
Then in your
.bashrc file you can add to the following to start the service when it’s not running yet.
service docker status > /dev/null || sudo service docker start
Putting it all together
You should be all set up now, let’s try a few commands to check whether our setup is working. The default docker installation guides you into running the
hello-world image, so let’s give that a try:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
For more examples and ideas, visit:
If you get a similar result as the one above, then that means everything is working. But let’s take it one step further and see if we can build an image and get a webserver up and running. Paste the following into a
RUN dotnet new web -o /app/HelloWorld
CMD ["dotnet", "run", "--urls", "http://0.0.0.0:5000"]
Then in the same directory as where you created this
Dockerfile plug the following commands in your terminal
docker build -t hello-world-aspnet . and
docker run -it -p 5000:5000 hello-world-aspnet . You should now have an ASPNET Core web app running on port 5000:
$ curl -i http://localhost:5000
HTTP/1.1 200 OK
Date: Sun, 31 May 2020 07:02:08 GMT
Congratulations you now have Docker running natively under WSL 2 on a Linux kernel that’s shipped with Windows 🤯.
In this post, I talked about what changed in WSL 2 to make docker run on this subsystem. To then go through the Docker for Windows setup process briefly, how you can get it running on WSL and how this works internally. I also showed you how to get docker up and running from scratch onto a new WSL instance. How to overcome any issues you might face when installing it from scratch and having it automatically start up when your WSL instance boots. This process can be scripted to save you time on your next setup and with help of
wsl --export you can easily create an export that can be used as a backup.