DEV Community

Cover image for Install Docker on Windows (WSL) without Docker Desktop
Jonathan Bowman
Jonathan Bowman

Posted on • Updated on

Install Docker on Windows (WSL) without Docker Desktop

Updated April 10, 2022, with current Alpine instructions, Debian/Ubuntu package signing tweaks (no more apt-key), and better guidance for handling iptables in Debian. A little more suggestion about TCP access, as well. And further emphasis on the optional nature of the /mnt/wsl/shared-docker socket directory.

Windows Subsystem for Linux 2 sports an actual Linux kernel, supporting real Linux containers and Docker. Docker works on WSL 2, and without requiring the robust but heavy Docker Desktop if that is undesirable. However, due to both WSL and Docker complexities, a little tender loving care is required to get Docker up and running. This article attempts to explore such a process and options along the way.

Contrary to what the length of this article might suggest, getting Docker working on WSL is fairly simple. In a nutshell:

  • Instead of using an init system such as systemd to launch the Docker daemon, launch it by calling dockerd manually.
  • If sharing the Docker daemon between WSL instances is desired, configure it to use a socket stored in the shared /mnt/wsl directory.
  • If sharing and privileged access without sudo are desired, configure the docker group to have the same group ID across all WSL instances
  • For simplicity, rather than launch a Windows-based Docker client, launch docker inside WSL.

Plenty more nuance and decisions below, of course. See details regarding the companion Github repo by scrolling to the bottom.

Are you sure you don't want Docker Desktop?

Before proceeding, let's note that Docker Desktop is amazing. If you want Docker to work on Windows and WSL 2, installing Docker Desktop is most likely the way to go. With Docker Desktop's WSL 2 backend, Docker integrates with Windows in a fairly elegant way, and the docker client can be launched from either Powershell or Linux. Reading about what goes on under the hood is an entertaining and informative endeavor, as well. Very clever.

If you came here looking how to get Docker running easily, or if you want Windows containers (still a rarity) out of the box, then Docker Desktop is your friend, and you can go install it now.

But if you, like me, feel that all the added complexity of Docker Desktop is unnecessary, you don't need Windows containers, or you are simply tired of that whale in the system tray taking... so... long... then perhaps you want to run the docker daemon (dockerd) in the WSL distro of your choice and be happy. You are at the right place.

Note that Docker Desktop is only free individuals or for small companies. If you are using it for work, and your company exceeds a certain size or revenue, then consider paying for a subscription. Of course, if you use Docker without Docker Desktop, as detailed in this article, then this does not apply. See more details about the Docker subscription model here.

Have you tried Podman?

Podman

Before we mosey along, though: are you aware of Podman? Podman is daemonless (no background service needed), modern (cgroups v2 out of the box), supports rootless, and serves as a drop-in replacement for Docker. Without needing to worry about sockets and ports, a lot of headaches go away.

I have written about getting Podman to work on WSL 2. Feel free to try it out. You may never look back. And, yes, VSCode can work with podman.

And yet... Sometimes, one just needs Docker to work. Maybe some tooling you use can't handle Podman, or you just want to put WSL through its paces. If so, read on.

Make sure that WSL is version 2

Please note that these steps require WSL 2 (not version 1). WSL 2 uses an actual Linux kernel that allows Linux containers. WSL 1 was genius with running Linux on the Windows kernel, but of course lacked some of the features, such as containers. Microsoft offers a more detailed comparison in the docs.

You will most certainly need WSL 2 to run the Docker service.

Does the command wsl --set-default-version 2 work? This will set the default version to WSL 2, or fail if you are still on the first version.

Microsoft's has step-by-step instructions on how to upgrade to WSL 2.

To run WSL 2, Windows version 1903 or higher is needed, with Build 18362 or higher. To tell what version you are running, run winver in Powershell or CMD, or just type Win key and R (⊞-r) to open the Run dialog and then enter winver. Hopefully you will see something like "Version 21H2. OS Build 19044.1586"

Install a Linux distro

If you do not yet have a running WSL instance with a distro of your choice, the next step is to pick one from the Microsoft Store. If you dislike the Windows Store, there are other options.

Custom installations are also a great option with WSL 2. For instance, install and configure Fedora, or any other distro for which you can obtain a rootfs in tar format and then wsl --import rootfs.tar.

This guide includes instructions for launching dockerd in Debian, Ubuntu, Alpine, and Fedora. If you think there is another obvious WSL distro that should be considered, feel free to let me know in the comments.

Configure a non-root user

Once you have installed the distro of your choice, launch it and set up a non-root user if you have not already. Debian and Ubuntu will configure this automatically at first launch, as should Alpine if you installed it from the Store. If the whoami command returnes "root", then you will want to add a non-root user. For Alpine or Fedora, use adduser myusername to create a new user. On Alpine, this should prompt for the new password. On Fedora, you will additionally need to passwd myusername and enter the password you want to use. (If your Fedora does not have passwd, then you will need to first dnf install passwd cracklib-dicts).

On later versions of Alpine from the Microsoft Store, while a non-root user is created as part of setup, this user is initially password-less. You can double check on any distro with:

cat /etc/shadow | grep myusername | cut -d: -f2
Enter fullscreen mode Exit fullscreen mode

(If you are not root, you may need to su first).

If the result is "!" then that user has no password set. If the result is a random hash string, then you are good. If you need to set a password, you can use passwd myusername (of course, in all of the above, use your username in place of "myusername."

Configure admin (sudo) access for the non-root user

If you used Debian or Ubuntu from the Windows store and set up the default user on first launch, then sudo should already be configured on behalf of the default user. You can skip this step, and proceed to updating packages and testing network connectivity, below.

When signed in as the user you set up (try su myusername if you are still root), can you sudo -v without an error?

If not, first make sure that sudo is installed. On Alpine, that's apk add sudo and on Fedora, dnf install sudo. If this fails due to network connectivity, see below. You can follow the directions there in order to correct DNS, but of course eliminate any occurrence of sudo in those commands, as you do not have it yet, and you should still be root anyway.

Is your user a "sudoer"? Try the following to see if they are part of the sudo or wheel group:

grep -E 'sudo|wheel' /etc/group
Enter fullscreen mode Exit fullscreen mode

On distros that have a sudo group, such as Ubuntu and Debian, you should see something like sudo:x:27:myusername and on distros that have a wheel group, such as Fedora and Alpine, you should see something like wheel:27:myusername.

If your username is missing from the group, take note of the group name (sudo or wheel) and add the user in question to that group:

  • Alpine: addgroup myusername wheel
  • Fedora: usermod -aG wheel myusername
  • Probably not necessary, but on Ubuntu/Debian: usermod -aG sudo myusername

Finally, as root, make sure that the admin group (whether sudo or wheel) is enabled for sudo:

grep -E '%sudo|%wheel' /etc/sudoers
Enter fullscreen mode Exit fullscreen mode

If the line is there, but commented out with a #, then run visudo then make sure the line reads thus (use wheel or sudo as determined earlier):

%wheel ALL=(ALL) ALL
Enter fullscreen mode Exit fullscreen mode

Then save.

Once these steps are complete, test again with:

su myusername
sudo -v
Enter fullscreen mode Exit fullscreen mode

If you are prompted for the password, then all is well. If you instead received an error containing something like "Sorry, user myusername may not run sudo" then you may need to follow the steps again, from the beginning.

Set default user

If you obtained your Linux distro from the Store, you can likely skip this step, as the default user is already set up.

If, however, when you launch WSL, you are still root, then set your new user as the default.

Assuming you have Windows build 18980 or later: simply add a user section to /etc/wsl.conf.

Something like this will work well if you do not already have that file, or a [user] section in it:

printf "\n[user]\ndefault = myusername\n" | sudo tee -a /etc/wsl.conf
Enter fullscreen mode Exit fullscreen mode

However, if on a version of Windows before build 18980, then you will instead need to edit the registry to set a default user. Before doing this, we will need two bits of information: the user id, and the name of the WSL distro. Chances are, you already know these. If not, you can obtain the user id with id -u myusername and check your list of WSL distros with (in Powershell) wsl -l

Then, use the following command in Powershell, but use your WSL distro name in place of "Alpine" and use your user id in place of "1000":

Get-ItemProperty Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\*\ DistributionName | Where-Object -Property DistributionName -eq Alpine  | Set-ItemProperty -Name DefaultUid -Value 1000
Enter fullscreen mode Exit fullscreen mode

Whichever method you use, test by logging out of WSL, and then log back in. Confirm that whoami yields the correct username. Success.

Update/upgrade packages and test network connectivity

Let's make everything new and shiny with one of the following:

  • Debian/Ubuntu: sudo apt update && sudo apt upgrade
  • Fedora: sudo dnf upgrade
  • Alpine: sudo apk upgrade -U

Network issues?

Upgrading the packages also serves as a network test. For a variety of reasons, network connectivity issues can happen with WSL 2, and tweaking the DNS settings often resolves these problems in my experience. If the upgrade command succeeded, you can skip this section. But if the above commands fail to access the package servers, it may be something unique to your network, or your firewall or anti-malware software. I recommend the following:

echo -e "[network]\ngenerateResolvConf = false" | sudo tee -a /etc/wsl.conf
sudo unlink /etc/resolv.conf
echo nameserver 1.1.1.1 | sudo tee /etc/resolv.conf
Enter fullscreen mode Exit fullscreen mode

The first line tells WSL to cease auto-configuring the /etc/resolv.conf file. Then we remove/unlink the old file, and create a new one.

With this newly-configured DNS resolver (in this case, pointing directly to Cloudflare's DNS server) you can try upgrading packages again. Success? Excellent.

Prepare for Docker installation

Thankfully, there are official guides for installing Docker on various Linux distributions. I have based these instructions on those, with some tweaks learned from real world testing.

Remove residue

If this is not a fresh install, and you may have experimented with docker before, then first clear out any residual docker installs:

  • Fedora: sudo dnf remove moby-engine docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine
  • Debian/Ubuntu: sudo apt remove docker docker-engine docker.io containerd runc
  • Alpine (probably not necessary, but just in case): sudo apk del docker-cli docker-ce docker-openrc docker-compose docker

Install dependencies

Then, install pre-requisites:

  • Debian/Ubuntu: sudo apt install --no-install-recommends apt-transport-https ca-certificates curl gnupg2
  • Fedora: sudo dnf install dnf-plugins-core
  • Alpine: Nothing needed. Dependencies will be installed later, automatically.

Debian: switch to legacy iptables

Docker utilizes iptables to implement network isolation. For good reason, Debian uses the more modern nftables, but this means that Docker cannot automatically tweak the Linux firewall. Given this, you probably want to configure Debian to use the legacy iptables by default:

update-alternatives --config iptables
Enter fullscreen mode Exit fullscreen mode

And select iptables-legacy.

If you are comfortable, instead, with nftables and want to configure nftables manually for Docker, then go for it. I suspect that most, however, will want to switch to iptables legacy.

Debian/Ubuntu package repository configuration

On Debian or Ubuntu, first temporarily set some OS-specific variables:

. /etc/os-release
Enter fullscreen mode Exit fullscreen mode

Then, make sure that apt will trust the repo:

curl -fsSL https://download.docker.com/linux/${ID}/gpg | sudo tee /etc/apt/trusted.gpg.d/docker.asc
Enter fullscreen mode Exit fullscreen mode

ID will be either "ubuntu" or "debian", as appropriate, depending on what is in /etc/os-release.

Then add and update the repo information so that apt will use it in the future:

echo "deb [arch=amd64] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
Enter fullscreen mode Exit fullscreen mode

Fedora package repository configuration

On Fedora, first add Docker's repo:

sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
Enter fullscreen mode Exit fullscreen mode

Install Docker

Now we can install the official Docker Engine and client tools:

  • Debian/Ubuntu: sudo apt install docker-ce docker-ce-cli containerd.io
  • Fedora: sudo dnf install docker-ce docker-ce-cli containerd.io
  • Alpine (install the latest from edge): sudo apk add docker --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community

Add user to docker group

The Docker daemon is a service that Docker requires to be running in the background. The service (dockerd) and client (docker) communicate over a socket and/or a network port. For communication over the socket, privileged access is required. Two ways to obtain this access:

  • Run docker as root (i.e. sudo docker)
  • Through group membership, grant specific users privileged access to the Docker socket

In other words, unless you want to utilize sudo or root access every time, add your user to the Docker group, named docker:

  • Fedora/Ubuntu/Debian: sudo usermod -aG docker $USER
  • Alpine: sudo addgroup $USER docker

Then close that WSL window, and launch WSL again. You should see docker when you run the command groups to list group memberships.

Sharing dockerd: choose a common ID for the docker group

If you only plan on using one WSL distro, this next step isn't strictly necessary. However, if you would like to have the option of sharing the Docker socket system-wide, across WSL distributions, then all will need to share a common group ID for the group docker. By default, they each may have a different ID, so a new one is in order.

First, let's pick one. It can be any group ID that is not in use. Choose a number greater than 1000 and less than 65534. To see what group IDs are already assigned that are 1000 or above:

getent group | cut -d: -f3 | grep -E '^[0-9]{4}' | sort -g
Enter fullscreen mode Exit fullscreen mode

Can't decide what number to use? May I suggest 36257. (Just dial DOCKR on your telephone keypad...) Not likely to be already in use, but check anyway:

getent group | grep 36257 || echo "Yes, that ID is free"
Enter fullscreen mode Exit fullscreen mode

If the above command returns a line from /etc/group (that does not include docker), then pick another number and try again. If it returns "Yes, that ID is free" then you are good to go, with the following:

sudo sed -i -e 's/^\(docker:x\):[^:]\+/\1:36257/' /etc/group
Enter fullscreen mode Exit fullscreen mode

Or, if groupmod is available (which it is on Fedora, Ubuntu, and Debian, but not Alpine unless you sudo apk add shadow), this is safer:

sudo groupmod -g 36257 docker
Enter fullscreen mode Exit fullscreen mode

Once the group id has been changed, close the terminal window and re-launch your WSL distro.

Note that the above steps involving the docker group will need to be run on any WSL distribution you currently have or install in the future, if you want to give it access to the shared Docker socket.

(Optionally) prepare a shared directory

As with the last step, if you only plan on using one WSL distro, this next step isn't strictly necessary. However, if you would like to have the option of sharing the Docker socket system-wide, across WSL distributions, then a shared directory accessible to all is needed.

Let's first make a shared directory for the docker socket, and set permissions so that the docker group can write to it.

DOCKER_DIR=/mnt/wsl/shared-docker
mkdir -pm o=,ug=rwx "$DOCKER_DIR"
chgrp docker "$DOCKER_DIR"
Enter fullscreen mode Exit fullscreen mode

Configure dockerd to use the shared directory

Again, this step can be skipped if you opt against using a shared directory for the docker socket. However, you may have other settings you wish to put in daemon.json, so you may appreciate some familiarity with this topic.

I suggest using the configuration file /etc/docker/daemon.json to set dockerd launch parameters. If the /etc/docker directory does not exist yet, create it with sudo mkdir /etc/docker/ so it can contain the config file. Then the following, when placed in /etc/docker/daemon.json, will set the docker host to the shared socket:

{
  "hosts": ["unix:///mnt/wsl/shared-docker/docker.sock"]
}
Enter fullscreen mode Exit fullscreen mode

Launch dockerd

Most Linux distributions use systemd or other init system, but WSL has its own init system. Rather than twist things to use the existing init system, we just launch dockerd directly:

sudo dockerd
Enter fullscreen mode Exit fullscreen mode

There should be several lines of info, warnings related to cgroup blkio, and the like, with something like API listen on /mnt/wsl/shared-docker/docker.sock at the end. If so, you have success.

Open another wsl terminal.

If and only if you opted to use the shared docker socket in /mnt/wsl/shared-docker as detailed above, first set the DOCKER_HOST environment variable:

export DOCKER_HOST="unix:///mnt/wsl/shared-docker/docker.sock"
Enter fullscreen mode Exit fullscreen mode

Then, try out the docker cli:

docker run --rm hello-world
Enter fullscreen mode Exit fullscreen mode

You should see the "Hello from Docker!" message.

Launch script for dockerd

The following lines can be placed in .bashrc or .profile if autolaunching is desired, or in a separate shell script. For instance, you may want to create a script ~/bin/docker-service so that you can run docker-service only when you want, manually.

DOCKER_DISTRO="Debian"
DOCKER_DIR=/mnt/wsl/shared-docker
DOCKER_SOCK="$DOCKER_DIR/docker.sock"
export DOCKER_HOST="unix://$DOCKER_SOCK"
if [ ! -S "$DOCKER_SOCK" ]; then
    mkdir -pm o=,ug=rwx "$DOCKER_DIR"
    chgrp docker "$DOCKER_DIR"
    /mnt/c/Windows/System32/wsl.exe -d $DOCKER_DISTRO sh -c "nohup sudo -b dockerd < /dev/null > $DOCKER_DIR/dockerd.log 2>&1"
fi
Enter fullscreen mode Exit fullscreen mode

If you went with the default docker socket location of /var/run/docker.sock instead of the shared socket directory of /mnt/wsl/shared-docker as detailed above, then the script can be something like this:

DOCKER_DISTRO="Debian"
DOCKER_LOG_DIR=$HOME/docker_logs
mkdir -pm o=,ug=rwx "$DOCKER_LOG_DIR"
/mnt/c/Windows/System32/wsl.exe -d $DOCKER_DISTRO sh -c "nohup sudo -b dockerd < /dev/null > $DOCKER_LOG_DIR/dockerd.log 2>&1"
Enter fullscreen mode Exit fullscreen mode

You may choose whatever location you would like for your docker logs, of course. It just needs to be in a place that has permissions so that your user can write to it.

Note that DOCKER_DISTRO should be set to the distro you want to have running dockerd. If unsure of the name, simply run wsl -l -q from Powershell to see your list of WSL distributions. Pick the right one and set it to DOCKER_DISTRO.

The script above does the following:

  • Sets the environment variable $DOCKER_HOST to point to the shared socket. This isn't necessary for dockerd but it will allow running docker without needing to specify -H unix:///mnt/wsl/shared-docker/docker.sock each time.
  • Checks if the docker.sock file already exists and is a socket. If it is present, do nothing. If not present then the script does the remaining steps, as follows.
  • Creates the shared docker directory for the socket and dockerd logs, setting permissions appropriately
  • Runs dockerd from the specified distro. This is important, because it allows any WSL distro to launch dockerd if it isn't already running.
  • When dockerd is launched, pipe its output and errors to a shared log file.
  • When dockerd is launched, it runs in the background so you don't have to devote a terminal window to the process as we did earlier. The sudo -b flag gives us this, and we run with nohup so that it runs independent of the terminal, with an explicit null input to nohup to avoid extra warnings. Both standard output and errors are written to the logfile, hence the 2>&1 redirect.

Passwordless launch of dockerd

If the above script is placed in .bashrc (most Linux distros) or .profile (distros like Alpine that have Ash/Dash as the default shell), or other shell init script, then it has an unfortunate side effect: you will likely be prompted for a password most every time a new terminal window is launched.

To work around this, you can, if you choose, tell sudo to grant passwordless access to dockerd, as long as the user is a member of the docker group. To do so, enter sudo visudo and add the following line (if your visudo uses vi or vim, then be sure to press "i" to begin editing, and hit ESC when done editing):

%docker ALL=(ALL)  NOPASSWD: /usr/bin/dockerd
Enter fullscreen mode Exit fullscreen mode

Save and exit (":wq" if the editor is vi, or Ctrl-x if it is nano), and then you can test if sudo dockerd prompts for a password or not. For peace of mind, you can double-check: something like sudo -k ls -a /root should still require a password, unless the password has been entered recently.

Make sure $DOCKER_HOST is always set

If using the script earlier to launch dockerd, then $DOCKER_HOST will be set, and future invocations of docker will not need an unwieldy -H unix:///mnt/wsl/shared-docker/docker.sock

If that script is already in your .bashrc or .profile, then the following is unnecessary. If, however, you manually invoke dockerd in some way, then the following may be desirable in your .bashrc or .profile, if you opted for the shared docker socket directory:

DOCKER_SOCK="/mnt/wsl/shared-docker/docker.sock"
test -S "$DOCKER_SOCK" && export DOCKER_HOST="unix://$DOCKER_SOCK"
Enter fullscreen mode Exit fullscreen mode

The above checks for the docker socket in /mnt/wsl/shared-docker/docker.sock and, if present, sets the $DOCKER_HOST environment variable accordingly. If you want a more generalized "if this is wsl, then set the socket pro-actively" then you may prefer the following, which simply check for the existence of a /mnt/wsl directory and sets the docker socket if so:

[ -d /mnt/wsl ] && export DOCKER_HOST="unix:///mnt/wsl/shared-docker/docker.sock"
Enter fullscreen mode Exit fullscreen mode

Running docker from Windows

If configured as above, I recommend always running docker from wsl. Do you want to run a container? Do so from a WSL window. (See my article on using Windows Terminal for a convenient way to use WSL and Powershell.)

This doesn't just apply to the terminal, either. For instance, VSCode supports docker in WSL 2.

But if you want the convenience and utility of running docker in a Powershell window, I have a couple suggestions.

One is to expose dockerd over a TCP Port, or, better yet, set up an SSH server in WSL and connect that way. docker context will likely be your friend. Such methods will be explored in a later article, but I encourage you, reader, to explore. A hint: ever tried scoop.sh? After setting it up, scoop install docker docker-compose will get you some familiar tools, then an SSH server such as Dropbear or OpenSSH on the WSL side...

A simplified method I recommend: a Powershell function that calls the WSL docker, passing along any arguments. This function can be placed in your Powershell profile, usually located at ~\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

$DOCKER_DISTRO = "fedora"
function docker {
    wsl -d $DOCKER_DISTRO docker -H unix:///mnt/wsl/shared-docker/docker.sock @Args
}
Enter fullscreen mode Exit fullscreen mode

Again, try wsl -l -q to see a list of your WSL distributions if you are unsure which one to use.

Make sure the Docker daemon is running, then launch a new Powershell window, and try the hello-world container again. Success?

You could also make a batch file with the appropriate command in it. For instance, name it docker.bat and place in C:\Windows\system32 or other location included in %PATH%. The following contents will work in such a script:

@echo off
set DOCKER_DISTRO=fedora
wsl -d %DOCKER_DISTRO% docker -H unix:///mnt/wsl/shared-docker/docker.sock %*
Enter fullscreen mode Exit fullscreen mode

You could go a step further and ensure that dockerd is running whenever you start Powershell. Assuming that the dockerd start script detailed above is saved in a file in WSL as $HOME/bin/docker-service and is executable (try chmod a+x $HOME/bin/docker-service), then the following line in your Powershell profile will launch dockerd automatically:

wsl -d $distro ~/bin/docker-service
Enter fullscreen mode Exit fullscreen mode

Not sure where your Powershell profile is located? Try entering $profile in a powershell window.

If you don't want to rely on a particular WSL shell script, you could implement a Powershell function to launch dockerd, such as this:

function Docker-Service {
  Param ([string]$distro)
  $DOCKER_DIR = "/mnt/wsl/shared-docker"
  $DOCKER_SOCK = "$DOCKER_DIR/docker.sock"
  wsl -d "$distro" sh -c "[ -S '$DOCKER_SOCK' ]"
  if ($LASTEXITCODE) {
    wsl -d "$distro" sh -c "mkdir -pm o=,ug=rwx $DOCKER_DIR ; chgrp docker $DOCKER_DIR"
    wsl -d "$distro" sh -c "nohup sudo -b dockerd < /dev/null > $DOCKER_DIR/dockerd.log 2>&1"
  }
}
Enter fullscreen mode Exit fullscreen mode

This function takes one parameter: the distro name.

In all of the above, the principle is the same: you are launching Linux executables, using WSL interoperability.

A note on bind mounts: stay on the Linux filesystem

With docker, it is possible to mount a host system's directory or files in the container. The following often works, but is not advisable when launching WSL docker from Windows:

echo "<h1>Hello, World</h1>" > index.html
docker run -p "8080:80" -v "$PWD:/usr/share/nginx/html:ro" nginx
Enter fullscreen mode Exit fullscreen mode

Instead of doing the above haphazardly, when launching WSL docker from Powershell, two recommendations:

  1. For performance reasons, only bind mount from within the Linux filesystem. To get to a Linux directory while in Powershell, try something like cd (wsl wslpath -m ~/path/to/my/dir)
  2. Instead of $PWD to get the current directory, try '$(wslpath -a .)' and, yeah, the single quotes are helpful for escaping.

An example:

cd (wsl wslpath -m ~)
mkdir html
cd html
echo "<h1>Hello, World</h1>" > index.html
docker run -p "8080:80" -v '$(wslpath -a .):/usr/share/nginx/html:ro' nginx
Enter fullscreen mode Exit fullscreen mode

Then point your browser to http://localhost:8080, and happiness will result. (Depending on your network configuration, you may instead need to access this through http://[WSL IP Address]:8080 which should be obtainable with ifconfig or ip addr)

Try wsl wslpath from Powershell, or just wslpath from Linux, to see the options. This is a very useful tool, to say the least.

Example scripts

After walking through the steps in this article, you should now have a working and potentially auto-launched dockerd, shared Docker socket, and conveniently configured docker command.

Some of the code examples above have been placed in scripts in a companion Github repo. I summarize the files available here:

  • docker-service.sh is a Unix shell script to launch dockerd
  • docker-service.ps1 contains a Powershell function to launch dockerd in WSL
  • docker.bat is a Windows batch file for launching WSL docker from CMD
  • docker.ps1 contains a Powershell function for launching WSL docker from Powershell, if placed in your profile

No doubt there are ways these can be tweaked to be more useful and reliable; feel free to post in the comments.

Interested in further tinkering with WSL 2?

Other articles I have written on WSL:

Discussion (93)

Collapse
tempcke profile image
Todd Empcke

I'll never understand why developers who write code to run in linux fight with windows. Just run linux native. This isn't the 90's anymore, it is really super easy to run linux on your local dev machine and every program you would want for dev that is worth running already runs on linux.

Collapse
patarapolw profile image
Pacharapol Withayasakpunt • Edited on

Usually because I need

  • Proprietary software, not limited to MS Word and PowerPoint. I still need to work and discuss with non-dev people, you know.
  • Best possible hardware drivers by default
  • I love POSIX as well, but I don't have a choice. macOS is expensive to buy (yet mainstream), as well as forced obsolescence (via OS updates + requirement, and repair / replacement prevention); not to mention keyboard layout confusion (which is "cost to change").

But yes, I used WSL2 enough that moved to a second PC with native Linux.

Collapse
bowmanjd profile image
Jonathan Bowman Author

I am familiar with those circumstances.

For me, using WSL isn't a choice against Linux, but a choice to use Linux everywhere. A Linux dev machine is quite desirable. Even with that, I will still run WSL on any Windows machine I can. Because I do a lot from the command line, and I often want that command line to be Linux, no matter the location or network connectivity.

Collapse
arakwar profile image
Sebastien Lemieux

Corporate.

We tried. But I have other things to do than spend my time trying to argue with people that we should be allowed to get Linux machines on our corporate network.

The only option that we had is to run a corporate-managed VM on Azure, with their own "linux" which is a special build from oracle that I never heared of before they mentionned it, and where no open source tools seems to offer any kind of support.

WSL is the only option that I have. HyperV is not stable enough on Linux, and VirtualBox is blocked by corporate rules.

Collapse
billybraga profile image
billybraga

Here are the problems I had on Ubuntu (note that I really wanted to work on linux since our servers run on linux) :

  • lack of proper bluetooth drivers (mSBC not supported, so headset sound was crap)
  • software support
    • Teams would not switch to headset mode automatically, nor detect when BT headset was connected after app was opened)
    • no edit and continue on .net core (and no, I won't change the language we write at work)
  • watching 4K videos on my 4K screen had tearing (and yes, watching videos on my break is a requirement)
  • regular system freeze
Collapse
chx profile image
Karoly Negyesi of House AstraZeneca

I will readily admit being a Linux newbie despite I installed Slackware with Linux 0.99pl15 for the first time from a stack of floppies early 1994. I am still running Linux on servers to this day. I ran Linux dual boot from 2000-2004 and then as a daily driver 2004-2017. It was a miserable experience. The choices are running Ubuntu where upgrading every six months shatters your OS so badly you can't work for days or Arch where upgrades often break one of your printer/scanner/Bluetooth. Connecting to any sort of enterprise-y VPN or WiFi just doesn't work. I don't care whether it's the fault of F5 or the community for not working -- if I can't VPN in, I can't work.

So the reason I use Windows is because that's where the driver support is. Plain and simple. And I use WSL2 because Linux excels at CLI and daemons.

Collapse
h8f1z profile image
Ahmed Hafiz

I have tried with multiple laptops (and multiple distros) and even with so many customisations, laptops keep heating up on idle.
Not so ideal for development with that heat on my hand . I do wish it'd change some day. Been waiting for years now.

Collapse
bhayes profile image
b-hayes • Edited on

That sounds odd. Ive been running WSL on potato laptops and now I high end one with no heat issues at all.

Collapse
sqlrob profile image
Robert Myers

I work on client/server software. The client is Windows; the server is not.

Collapse
sarvasana profile image
Kris Kater

^^ This.

Stop running Windows unless you really have to.

Collapse
marzelin profile image
Marc Ziel

Fight? There's no fight between Windows and Linux since wsl2. It's a peaceful symbiosis.

Collapse
kushakjafry profile image
Kushak Zohaad Jafry

Hi, followed everything but on doing sudo dockered getting this error.

WARN[2021-10-24T16:24:00.993150800+05:30] grpc: addrConn.createTransport failed to connect to {unix:///var/run/docker/containerd/containerd.sock 0 }. Err :connection error: desc = "transport: Error while dialing dial unix:///var/run/docker/containerd/containerd.sock: timeout". Reconnecting... module=grpc
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER: iptables v1.8.4 (legacy): can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgrade

Collapse
badsamaritan profile image
Michał Drzymała

What helped me in similar situation:

  • Add iptables false (as mentioned in the article),
  • Restart WSL engine (restart Lxssmanager service on Windows host),
  • Run WSL prompt as Admin (elevated) and there only run dockerd
Collapse
ajburley profile image
ajburley

I got this so I just added "iptables": false to my daemon.json and this error was averted. In the original post it says you only need to do this for Debian but not Ubuntu, and I'm using Ubuntu so I skipped that step originally. But in the end, turned out it was required.

Collapse
kushakjafry profile image
Kushak Zohaad Jafry • Edited on

I did that but it did not work for me. Now I have started using docker desktop again.

Collapse
sw1tch73ch profile image
sw1tch73ch

So I wonder if Windows 10 wsl Debian changed - I can't use the update-alternatives --config iptables. I know I did before, I'm not sure what I left out - but the iptables-legacy isn't set-able now.

Collapse
bowmanjd profile image
Jonathan Bowman Author

Interesting; I just did this successfully last weekend. I wonder what is different. Do you have iptables installed?

Collapse
sw1tch73ch profile image
sw1tch73ch

I did. I even uninstalled and installed it back. Still had no "update-alternatives" for iptables which I believe is part of the problem I was having with Docker trying to run the "Computer Language Drag Racing" suite.

I removed the Debian WSL for now. Ubuntu works correctly, I think because they still use iptables and not the nftables in Debian that Docker apparently doesn't really understand unless you configure nftables just right.

Thread Thread
sw1tch73ch profile image
sw1tch73ch

I reinstalled the Debian WSL. Unless I missed a step above, when I got to "update-alternatives --config iptables" it's still broke on my system. Here is what I get:

$ update-alternatives --config iptables
update-alternatives: error: no alternatives for iptables

I'm pretty sure using the nftable subsystem is eventually what is making things not work - if I could get iptables-legacy it might be different.

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

Just double-checking: are you sure you have iptables installed?

Thread Thread
sw1tch73ch profile image
sw1tch73ch

Yes. I did "sudo apt-get install iptables" to be sure. It seems like there is another package that adds the iptables-legacy links. I even removed and installed fresh wsl. Either Windows is remembering somewhere that it doesn't add the iptables-legacy rules, or I'm missing a package (or more than one) somewhere.

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

Well, let's check. On your Debian install, what is the result of dpkg -S /usr/sbin/iptables-legacy?

Thread Thread
sw1tch73ch profile image
sw1tch73ch

dpkg shows:

$ dpkg -S /usr/sbin/iptables-legacy
dpkg-query: no path found matching pattern /usr/sbin/iptables-legacy

iptables is installed:
$ iptables --version
iptables v1.6.0

Thread Thread
sw1tch73ch profile image
sw1tch73ch

I'm not sure what happened to the previous reply:

$ dpkg -S /usr/sbin/iptables-legacy
dpkg-query: no path found matching pattern /usr/sbin/iptables-legacy
$ iptables --version
iptables v1.6.0

I think iptables installs when Debian itself is installed. It just isn't setting up the legacy rules. Searching around google, the answer that keeps popping up is to use the update-alternatives, which is the whole problem

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

I probably sound like I am quite fixated on the iptables package, but would you try reinstalling it? Using apt install --reinstall iptables

Thread Thread
sw1tch73ch profile image
sw1tch73ch

So I looked in /usr/sbin... I only have one entry if I look for iptables:

$ ls /usr/sbin/iptable*
/usr/sbin/iptables-apply

I believe there should be nearly a dozen links to other objects there.

Thread Thread
sw1tch73ch profile image
sw1tch73ch

$ sudo apt install --reinstall iptables
Reading package lists... Done
Building dependency tree
Reading state information... Done
0 upgraded, 0 newly installed, 1 reinstalled, 0 to remove and 0 not upgraded.
Need to get 288 kB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 deb.debian.org/debian stretch/main amd64 iptables amd64 1.6.0+snapshot20161117-6 [288 kB]
Fetched 288 kB in 0s (2,349 kB/s)
(Reading database ... 36399 files and directories currently installed.)
Preparing to unpack .../iptables_1.6.0+snapshot20161117-6_amd64.deb ...
Unpacking iptables (1.6.0+snapshot20161117-6) over (1.6.0+snapshot20161117-6) ...
Setting up iptables (1.6.0+snapshot20161117-6) ...

$ update-alternatives --config iptables
update-alternatives: error: no alternatives for iptables

I agree it must be something in iptables too. It just doesn't set the default links in the install process to be able to switch to the legacy rules.

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

Debian 9, I see. I honestly haven't tried this with older versions of Debian. Did 9 even use nftables? Pretty sure there is no legacy version because iptables wasn't legacy then. Does dockerd work?

Thread Thread
sw1tch73ch profile image
sw1tch73ch

I didn't notice the 9. It is the latest from Microsoft - or so I thought. Dockerd does work. Maybe the project I'm trying to compile doesn't like Debian 9! Here I thought it was because the iptables didn't follow the instructions. Strange my Debian is so far behind.

Collapse
raspberrypinews profile image
RPI News

For some reason I can't get internet connection inside the container.

Collapse
salvatorepiccione profile image
Salvatore Piccione

Hi, I have exactly the same issue... @bowmanjd can you share any hint about how to get Internet connection working on docker containers running on WSL2?

Collapse
bowmanjd profile image
Jonathan Bowman Author

Interesting... What sort of errors are you seeing? Is it all internet connectivity, or just DNS?

Thread Thread
salvatorepiccione profile image
Salvatore Piccione

It is all internet connectivity: I cannot ping 1.1.1.1 but I can ping the docker host from a container.

Collapse
salvatorepiccione profile image
Salvatore Piccione

BTW I solved this issue switching from Debian to Ubuntu as WSL2 distro.

Thread Thread
timblaktu profile image
Tim Black

I'm having same issue, using Debian 11 on WSL2. With a Dockerfile containing only:

FROM centos:7
RUN yum -y install httpd

I was getting yum errors not resolving the name of the mirror server:

Determining fastest mirrors
Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=container error was
14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"

Since I could resolve the name of the server from Debian WSL2 with no issue, I knew my DNS was working there. So I added some sleuthing to the Dockerfile:

FROM centos:7 RUN cat /etc/resolv.conf && ping -v -c2 host.docker.internal && ping -v -c2 1.1.1.1 && ping -v google.com && ping -v mirrorlist.centos.org RUN echo "timeout=30" >> /etc/yum.conf && cat /etc/yum.conf && yum -y install httpd

and run docker build with --add-host=host.docker.internal:host-gateway, I can see that I can ping the host from the container, but the container cannot seem to ping any external ip, even the cloudflare dns 1.1.1.1 or google's 8.8.8.8.

I've played around with setting DNS in the container explicitly using the /etc/docker/daemon.json with things like "dns": ["1.1.1.1", "8.8.8.8"], but if the container can't even get connectivity to these ips that's not going to work..

My Debian environment does not have any iptables configured. I'm flummoxed.

Thread Thread
timblaktu profile image
Tim Black • Edited on

I found my debian environment is configured to use iptables-nft:

$> sudo update-alternatives --config iptables
There are 2 choices for the alternative iptables (providing /usr/sbin/iptables).

  Selection    Path                       Priority   Status
* 0            /usr/sbin/iptables-nft      20        auto mode
  1            /usr/sbin/iptables-legacy   10        manual mode
  2            /usr/sbin/iptables-nft      20        manual mode
Enter fullscreen mode Exit fullscreen mode

But I was getting no rules generated by iptables-nft-save, and several rules generated by iptables-legacy-save, so I explicitly update-alternatives to iptables-legacy and rebooted (host and wsl2/debian). (Will report back with results..)

Thread Thread
timblaktu profile image
Tim Black • Edited on

Still same error after switching explicitly to iptables-legacy in debian 11. FWIW, I'm also passing the following dns servers to my containers via docker daemon.json:

# This one is the WSL net interface as seen by windows. It cannot be pinged from container.
nameserver 172.29.224.1
nameserver 1.1.1.1
nameserver 8.8.8.8
Enter fullscreen mode Exit fullscreen mode

I've tried putting the google and cloudflare dns first in this order, to no avail.

Thread Thread
timblaktu profile image
Tim Black

The issue is more easily reproduced on my system by just running ping commands inside the latest alpine image:

$> home/tim/src > docker run --rm -it alpine ping -c4 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
Enter fullscreen mode Exit fullscreen mode
Thread Thread
timblaktu profile image
Tim Black

The problem was that even though I had reverted to iptables-legacy in Debian, I still had iptables: "false" in my docker daemon.json. On removing that, docker can use its default iptables impl and work with Debian Bullseye. Now, my containers can access "the internet".

I realize that your post indicated to use iptables: false as a way to get debian wsl2 instances to work with docker. But that never worked for me for some reason.

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

Yeah, I have actually changed the instructions, removing the iptables:false, as using iptables-legacy seems like the right way to do it.

Collapse
medinam profile image
Luis Medina

Awesome post! Thanks!

Collapse
dirtysoc profile image
Derek

What an excellent write-up. Thank you!

I do have one question though. My understanding of the inner-workings of WSL is still rudimentary. Why do we place the docker socket in the \mnt\wsl folder? What is the significance of \mnt\wsl?

Collapse
fonata profile image
Fonata

Hey Derek, I believe the \mnt\wsl location is chosen so multiple Linux installations can share the same docker daemon. If you only run one it doesn't hurt, but you could use Docker's default location, /var/run/docker/containerd/containerd.sock

Collapse
bowmanjd profile image
Jonathan Bowman Author

Exactly!

Collapse
jrtitus profile image
Jeff • Edited on

Jonathan, thank you for the incredibly detailed description of setting up Docker for use in WSL2 without Desktop. I'm sure a lot more people will be visiting this page now that Docker has changed their license terms.

For anyone struggling with using this behind a proxy, I found the only configuration file that dockerd looks at is /etc/environment, so set the likes of HTTP_PROXY, HTTPS_PROXY, and NO_PROXY in there before starting Docker.

Additionally, I found this to be helpful for configuring dockerd to start when opening a new terminal (if it hasn't already been started).

Lastly, if you are working behind a proxy and need access to a private container registry, and get an x.509 certificate error with docker login, grab the root certificate of the proxy from your browser (export as base-64) and drop it into the docker certs directory related to your private registry/etc/docker/certs.d/{private_reg_name}:{private_reg_port}/ca.crt (private_reg_port is optional if you're using a standard port). The next time you do docker login, the auth section of ~/.docker/config.json will be updated.

Collapse
kcelestinomaria profile image
Celestine Kariuki

Thanks so much for this @jonathan Bowman, was really helpful, don't forget to do another article on installing docker-compose on a WSL Distro without passing through Docker Desktop, might be minimal but it would be a decent supplement to this awesome article of yours

Collapse
bowmanjd profile image
Jonathan Bowman Author

Thank you for the encouragement!

Collapse
ajburley profile image
ajburley • Edited on

I got this error, I solved it by running WSL itself with admin privileges when opening the WSL window to run sudo dockerd.

EDIT: It turned out that the eventual root cause of my issue was that my distribution was still on WSL1. Even after upgrading WSL to 2 and running wsl --set-default-version 2, my distribution was still WSL1 as it was created before the upgrade. So I had to run wsl --set-version Ubuntu 2 (where my distribution was called "Ubuntu") and this converted the distro to WSL2. Then this issue just went away, regardless of whether I ran WSL as admin.

Collapse
nparkes profile image
nparkes

I love you. WSL2 Ubuntu+Windows10.

Goodbye Docker Desktop.

Collapse
dove6 profile image
Dawid Sygocki

After setting up Docker on Debian WSL2, I couldn't connect to the Internet from inside containers.
Switching back to legacy iptables (and removing "iptables": false from daemon.json) solved the issue for me.
The commands are listed here: wiki.debian.org/nftables#Reverting...

Collapse
brycolem profile image
Bryan Patrick Coleman

I followed the instructions on your page, when I ran

# update-alternatives --set iptables /usr/sbin/iptables-legacy
# update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
# update-alternatives --set arptables /usr/sbin/arptables-legacy
# update-alternatives --set ebtables /usr/sbin/ebtables-legacy
Enter fullscreen mode Exit fullscreen mode

I got an error saying arp and eb were not registered and weren't going to be set.

I still don't have any communication from the container out to the internet which is an issue since I need them to run containers for vscode which installs stuff from special scripts during container creation.

Collapse
bowmanjd profile image
Jonathan Bowman Author

I don't know if arp works on wsl... github.com/Microsoft/WSL/issues/2279. Curious if you find any solutions, though

Thread Thread
brycolem profile image
Bryan Patrick Coleman

After I reboot Windows it started working. So no entry with iptables and setting the iptables and ip6tables to legacy worked. It just needed to restart everything.

Collapse
arm1 profile image
Armand Paghent

Thanks for the article. It really helps.
My need was to have a docker command recognized by the windows "system". Indeed, I run programs that run docker, etc... but sometimes they tell me something like "you don't have docker installed" because they don't manage to "find" and run your docker powershell "function".
So installing the windows docker-cli.exe is one of the solution but :
Is it possible to use the windows docker-cli (docker.exe) to connect to that dockerd using the socket things ?
I followed all your instructions but i just changed the path to the docker.sock to unix:///mnt/c/path/docker.sock. but dockerd doesn't start with this folder. Any thought ?

Collapse
bowmanjd profile image
Jonathan Bowman Author

Yes, it is indeed possible using TCP, but I have yet to work through that and add an article...

Collapse
arm1 profile image
Armand Paghent

I found after my question an article based on yours, on that topic at dev.to/_nicolas_louis_/how-to-run-.... And Indeed, it works (I also needed to disable my VPN, i don't know exactly why..).
Thanks !

Collapse
voronin profile image
Igor Voronin

Oh my god.. I now get why I should definitely try a tutorial from the beginning. I simply dismissed the warning about having to ensure WSL 2 is used... instead WSL 1 was used - and docker did not want to work with Ubuntu 20.04 installed in WSL 1 (various errors regarding not having permissions for "bridge" or missing some "iptables").

After converting to WSL 2 (yes - converting), it finally seems to work. Thank you for this brilliant article!

Collapse
dfseagate profile image
df-seagate • Edited on

Thanks for putting this together. After spending 20+ hours trying to get Docker Desktop to work with flakey results at best I thought I'd give this a try. The instructions are fantastic. However, when I execute, docker run I'm getting a toomanyrequests: error.
When I try this on a Linux machine, I have not problems.
I've tried both Docker Desktop and WSL2 Docker on another laptop and had no issues.
Time to reinstall windows I guess :)

Collapse
bowmanjd profile image
Jonathan Bowman Author

It sounds like you have a working docker setup; however, you have performed too many requests to the Docker Hub: docs.docker.com/docker-hub/downloa...

Collapse
haughki profile image
Hawkeye Parker

Incredibly detailed and helpful post: thank you!!

I stumbled on the same issue as df-seagate:
"$ docker -H unix:///mnt/wsl/shared-docker/docker.sock run hello-world
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: docker.com/increase-rate-limit."

I have rarely used docker, and this is a clean install on a machine on which I've never done anything with docker.

Shutting down my corporate VPN fixed the issue. I have no idea why. Also worth noting that I'm running wsl-vpnkit: github.com/sakai135/wsl-vpnkit

Collapse
medicmen profile image
MedicMen

I have done everything,

First It did not run, but when disabled VPN it started working but listen on something else.
Then I stopped dockerd and tried to restart but I failed, so after hour what is going on I simply run killall -9 docker and start dockerd again.
And it was working

BUT

When I started powershell and Ubunt in another cmd and run

docker -H unix:///mnt/wsl/shared-docker/docker.sock run --rm hello-world

I do have an error:

docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:722: waiting for init preliminary setup caused: EOF: unknown

???

Collapse
miguelsantos500 profile image
Miguel

In my case this was because my linux distro was still on wsl version 1. you have to

  • upgrade your system-wide wsl version from 1 to 2
  • upgrade your distro version ( wsl --set-version 2 )
Collapse
ajburley profile image
ajburley

Yes! Worked for me too. I had upgraded my system-wide WSL to 2 and set the default version to 2, but what I hadn't done was set the version for my specific installed distribution using --set-version.

Collapse
timblaktu profile image
Tim Black

What are some elegant ways to restart your WSL docker daemon (e.g. after changing your daemon.json)? I'm not interested in "reboot windows". I'm currently using a script to run dockerd as a shell command passed to wsl.exe, run on user login, as described in this (excellent) article. I'm pretty novice at powershell and managing windows processes, and have pulled out a lot of hair just trying to get the equivalent of ps aux | grep dockerd and kill <pid> in windows. The output of tasklist (and even things like sysinternals ProcessExplorer, amazingly) don't show the arguments passed to the command running in the process.

What I'm thinking now is, how about wrapping the dockerd wsl script in a windows service or scheduled task. Anyone doing this and have any recommendations?

Collapse
haosmos profile image
haosmos • Edited on

Thank you very much for the detailed instructions!
Let me ask you some questions.

  • Did I understand correctly that installing docker directly into wsl is not supposed to get any benefits or improvements, but just a way to avoid installing a heavy docker desktop application in windows that consumes a lot of RAM?

  • The Ubuntu development team recently announced the implementation of systemd support in Ubuntu 22.04 - bugs.launchpad.net/ubuntu/+source/....

Some enthusiasts have already tried this and it seems to work: github.com/microsoft/WSL/issues/51...

The developers of Fedora Remix for WSL also announced a new version of the distribution with systemd support a couple of weeks ago — whitewaterfoundry.com/blog/2022/4/...

Can you tell me, please, if activating this feature (wsl-systemd) - will it require changes to this tutorial of yours?

And in general, will it change anything in the way docker was used in wsl before? Would it be a game changer?

Collapse
bowmanjd profile image
Jonathan Bowman Author

You are correct. In my opinion, installing Docker this way is appealing for its lightweight and flexible (pick-your-distro) approach. It is also a good learning experience!

Thanks for the links about Ubuntu and systemd! What this will change: the startup script can be replaced with a simple systemd enable docker

Collapse
maksymrybak profile image
Maksym Rybak

Thanks a lot for this post, I made docker up and running on my windows laptop.
We also use a lot docker-compose, do you think it's possible to configure it like docker?
Are you planning to write an article how to use docker-compose on wsl?
Best, Max

Collapse
yuranick profile image
YuraNick

I didn't understand a little about docker-service service / file. My solution works for me:
sudo mkdir ~/bin
sudo nano ~/bin/docker-service.sh
insert the contents of this file: github.com/bowmanjd/docker-wsl/blo...
. ~/bin/docker-service.sh

But I haven't found a way to stop. I will be glad if you can tell me. And if it was useful for you.

Collapse
ramachandran09 profile image
Ramachandran

Hi,

How I can use --password-stdin along with wsl?. I mean, can able to login with docker using the below command

wsl -d Ubuntu-18.04 docker -H unix:///mnt/wsl/shared-docker/docker.sock login -u AWS -p "" .dkr.ecr..amazonaws.com

But, with the above example I required to set the token with the command line. And, it gets stored for future use. So, I need use the --password-stdin instead. But, I'm not sure how to do that. Any suggestion?

Collapse
galsi profile image
galsi

Hi , thanks for this great article !!
is there a way accessing the docker container from windows host ?
from the wsl distro i can ping the container
from windows i cannot ping or see the container
i can ping the wsl distro with no problem
i have tried the web app from docker but cannot access from windows host
thanks again

Collapse
dirtysoc profile image
Derek

After using this with my WSL for a few weeks, I would like to bring up that I found issues using the k3d tool to run k3s clusters in WSL. Unfortunately it looks like k3d does not support moving the docker socket to a custom location.

Just leaving this info here for anyone considering setting up a shared docker socket!

github.com/rancher/k3d/issues/762

Collapse
bhayes profile image
b-hayes

Thanks for this. I skipped all the shared mount stuff tho as I was trying to avoid the setup that docker desktop manages for us normally to see if there are any performance gains.
Without the MySQL data and project volume mounts operating in a seperate distro behind the scenes I saw almost my database heavy unit tests in a monolith sized project finish in half the time wich is now starting to reflect native linux performance.

I'll try podman next see if it gets any faster 😉.

Collapse
cartier profile image
Brian Cartier

Excellent how-to Jonathan.
You can expect a surge of traffic with the Docker Desktop license change yesterday.

Collapse
matez profile image
matez

Hi Jonathan, thanks for your article.

Did you manage to get VSCode + Remote Containers working?

I assigned the path of docker.bat to the remote extension but it it fails building the devcontainer with error message: "unable to prepare context: path "d:foobar.devcontainer" not found.
I guess there is something wrong with the translation of windows full path to wsl path.

If I execute the docker build command outside of VSCode with relative paths, the container is build successfully.

Collapse
matez profile image
matez

anwering to myself: yes it works, but the files, which should be opened inside the devcontainer must already lie in WSL space. Also see github.com/microsoft/vscode-remote...

Collapse
lubomirh profile image
LubomirH

@jonathan Bowman
I am lucky I have found your articles, but I still need a time to digest the knowledge you shared with us.
I am not sure if I am exactly tired of the whale in the system tray, but let's say I can imagine it might have been a perfect solution for me if only the systray part of Docker Desktop wasn't so deeply integrated.

In the post on docker forum, I am trying to describe what I would like to achieve.
forums.docker.com/t/docker-desktop...
I still haven't lost hope, that it is achievable to run both windows and wsl2 containers on one machine and sharing the resources.

It would be great if you find some time to look on it and post what do you think about it.
I pretty sure I am just missing some small thing to make it work :)
It might be a lack of knowledge is hiding some workaround for me.

thanks in advance

Lubomir

Collapse
bowmanjd profile image
Jonathan Bowman Author

Interesting use case. Can you run Docker Desktop for windows containers, then use the methods described in this article for linux containers?

Collapse
narimanab profile image
Nariman

@bowmanjd nice article! But what is the reason to use custom name for unix:///mnt/wsl/shared-docker/docker.sock instead of the default one - unix:///var/run/docker.sock?

for example testcontainers (and many other apps) expect it at the default location.

Collapse
bowmanjd profile image
Jonathan Bowman Author

Good question. As noted above, "If sharing the Docker daemon between WSL instances is desired, configure it to use a socket stored in the shared /mnt/wsl directory."

Of course, if you only have one WSL instance (such as Ubuntu alone, and no other distros), then sharing may not be needed, and you can leave the default socket alone.

I hope this helps!

Collapse
avagenzo profile image
Johann

Thank you for this awesome and detailed description. What i liked about Docker Desktop is the Kubernetes Integration. How can I get Kubernetes running without Docker Desktop on WSL2. Is it much more complicated?

Collapse
h8f1z profile image
Ahmed Hafiz

Thanks for the article. I had given up docker from the very beginning. May be I'll try it tonight... ** Off to find how to install alpine **

Collapse
jmcevod_jcplc profile image
Dennis McEvoy

Thanks for this article!

For our application, we want to include several docker containers as part of our Windows installation package. Since we are not including Docker Desktop, we'll include a script using WSL (as you have mapped out here.)

Are there any any other licensing issues that we should be concerned about?