When the Windows Subsystem for Linux (WSL) – or, as most people even at Microsoft often refer to it – Bash on Ubuntu on Windows – was announced on Microsoft’s Build conference 2016, a world of new tools opened up to us Windows devs. Personally, I love being able to choose between PowerShell, Bash or plain old cmd when I want to script something. And it’s always bugged me that I couldn’t get Docker working from Bash on Windows – until now.
The original title of this post was “Running Docker from Bash on Windows”, but that would have been a slight overstatement. Docker requires access to quite a of lot system calls which aren’t necessarily all implemented on Windows, so getting the engine running under the WSL is probably not so easy. Instead, we’ll run the Docker Engine on Windows, and connect to it from Bash. This also has the advantage that you can start a container from PowerShell and interact with it from Bash, or the other way around – in other words, your computer will still feel just like one machine.
Here’s how to do it:
1. Install Docker on Windows
To install the Docker engine on Windows, just go to docker.com and download the appropriate distribution. Also, make sure hardware virtualization is enabled and Hyper-V is installed, lest the engine won’t start.
Shortcut: Install Windows 10 Creators Update
With Windows 10 Creators Update, accomplishing all of this has become a lot simpler, since it allows you to run Windows executables from Bash. Just add these two lines to your
.bashrc (and reload your environment) and you’re done!
export PATH="$HOME/bin:$HOME/.local/bin:$PATH" export PATH="$PATH:/mnt/c/Program\ Files/Docker/Docker/resources/bin" alias docker=docker.exe alias docker-compose=docker-compose.exe
You can now run
docker --version from Bash, and you don’t even have to read the rest of this blog post :)
Making it work on Windows 10 Anniversary Edition
To install Docker on the WSL, you’ll need to jump through a few more hoops. There’s a description for Ubuntu in general here, which works for the WSL as well, with the exceptions of some of the optional steps. Here’s what I did:
# Install packages to allow apt to use a repository over HTTPS $ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common # Add Docker's official GPG key $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # Set up the repository sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # Update source lists sudo apt-get update # Install Docker sudo apt-get install docker-ce
Of course, there’s also the option of downloading and extracting the binaries we’ll need, and put them somewhere in your
PATH. There are instructions here for how to get the latest version.
Where did that get us?
We now actually have the Docker engine installed on both Windows and the WSL, but it isn’t started on either. The Windows installer helpfully created a Docker shortcut on the desktop and/or in the Start menu – use that to start the Docker engine. Then, you can try running e.g.
docker images from PowerShell and from Bash:
PS C:\> docker images REPOSITORY TAG IMAGE ID CREATED SIZE
We haven’t created any images yet, so that’s fine.
$ docker images Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Clearly unsatisfying. But with one or two extra steps, we’ll get it all working.
2. Connect Docker on WSL to Docker on Windows
docker against an engine on a different machine is actually quite easy, as Docker exposes a TCP endpoint which the CLI can attach to. The endpoint uses TLS, which means we have to set up the client on the WSL side to recognize the certificates that the Docker machine on the Windows side uses (thanks Alan for the tip!):
export DOCKER_HOST=tcp://192.168.99.100:2376 // your Docker IP export DOCKER_CERT_PATH=/mnt/c/Users/YOUR_USERNAME/.docker/machine/certs export DOCKER_TLS_VERIFY=1
With that done, all we need to do is instruct the CLI under Bash to connect to the engine running under Windows instead of to the non-existing engine running under Bash, like this:
$ docker -H tcp://0.0.0.0:2376 images REPOSITORY TAG IMAGE ID CREATED SIZE
Having to specify that flag all the time is annoying, though. Luckily, there’s a better way (thanks Dave!) – export an environment variable which instructs Docker where to find the host engine:
Of course, we’d want these changes in .bashrc to make them sticky:
$ echo >> ~/.bashrc <<EOF # Connect to Docker on Windows export DOCKER_CERT_PATH=/mnt/c/Users/YOUR_USERNAME/.docker/machine/certs export DOCKER_TLS_VERIFY=1 export DOCKER_HOST='tcp://0.0.0.0:2375' EOF $ source ~/.bashrc
Now, running docker commands from Bash works just like they’re supposed to.
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE
The versions used in this post
Of course, things like these might always have dependencies of the versions of the tools you’re using. In this blog post, I’ve been using the following versions:
- Windows 10 Pro Anniversary Edition (Version 1607, OS Build 14393.1066)
- Windows 10 Pro Creators Update (Version 1703, OS Build 15063.138)
- WSL Ubuntu
- 14.04.5 LTS
- Docker on Windows
- 17.03.1-ce, build c6d412e
- Docker on Ubuntu
- 17.03.1-ce, build c6d412e