DEV Community

Nicolas Louis
Nicolas Louis

Posted on

How to run docker on Windows without Docker Desktop

Since Docker announced a new subscription for Docker Desktop for personal use, educational institutions, non-commercial open-source projects and small businesses, other enterprises need to acquire licences for all installations of Docker Desktop.

So is there an alternative on Windows to continue to legally use containers with a docker command and a nice UI like VSCode without paying a licence : the answer is YES !

We are doing magic with Windows 10, Ubuntu on WSL2, docker builder cli for windows and a little elbow grease.

Big Thanks to Jonathan Bowman for his article.

I reused and I adapted it to make VisualCode working with dockerd under WSL2.

Installation on Windows

On your windows, you need to install a couple of things :

Installation of dockerd in WSL2/Ubuntu

(Inspired from the Jonathan Bowman's article)

Is your user a "sudoer" ?

Check if sudo is installed if not : *apt install sudo*`

grep -E 'sudo|wheel' /etc/group

You would see something like sudo: x:27:myusername

Otherwise, We use usermod to add an user to the sudoer group

usermod -aG sudo myusername

Finally you can check with this command :

sudo grep -E '%sudo|%wheel' /etc/sudoers

You'll have something like

%wheel ALL=(ALL) ALL

If you see a # at the first position, the line is commented, run sudo visudo, find the corresponding line and remove the #, save and check again.

Update your Ubuntu distro

sudo apt update && sudo apt upgrade

Remove Residue from previous docker installations

sudo apt remove docker docker-engine docker.io containerd runc

Debian/Ubuntu package repository configuration

source /etc/os-release

Trust the repo :

curl -fsSL https://download.docker.com/linux/${ID}/gpg | sudo apt-key add -

Update repo info :

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

Install official Docker release

sudo apt install docker-ce docker-ce-cli containerd.io

Add user to docker group

sudo usermod -aG docker $USER

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

Get IP address in WSL2

echo `ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d:`

You should have something like 172.20.5.64

Launch dockerd

In WSL, there is no systemd or other init system. So we need to launch manually docker with the automatic collect of the IP address

sudo dockerd -H `ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d:`

There should be several lines of info, warnings related to tls, and the like, with something like API listen on 172.20.5.64:2375 at the end. If so, you have success.

Test docker command

Get the IP address given with the line API listen and In another WSL terminal, you can test the following command :

docker -H 172.20.5.64 run --rm hello-world

You'll get something like this :

Hello from Docker!

...

Installing Docker.exe on Windows

Stefan Scherer is maintaining the project docker-cli-builder on GitHub where we can download the docker.exe command in standalone :

  • Download the exe
  • Put it in the directory like c:\bin
  • Add this directory in the path for executables : System Properties\Environement Variables\System Variables\Path

Add path to docker binary

Check if docker is working

Once done, logout from your session and log again
In a windows terminal (Windows Power Shell) , launch :

docker --version

You would get something like :

Docker version 20.10.5, build 55c4c8896

Launch dockerd

Open a terminal in Wsl2, you execute

sudo dockerd -H `ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 |awk '{ print $2 }' | cut -f2 -d:`

And you get the IP address, as described before

Test docker on Windows

In the Powershell windows of the terminal, you can run the following command
c:\bin\docker -H tcp://172.20.5.64 run --rm hello-world

And you would get :

Hello from Docker!

...

Great we have now docker in windows running with WSL2.
But let's continue magic !

Configure VSCode to access to WSL2 docker

If you launch Visual Code and you select the docker extension, you'll get error in the panel asking if docker is installed... Yes of course it's installed but not configured to access to WSL2

VSCode with docker extension errors

To do so, click on the icon (?) on the top right of the section "Containers" and select "Edit settings..."

VSCode edit docker settings

You'll get around 56 settings and you search for "Docker:Host" where you put the line "tcp://172.20.5.64:2375" where you can replace the highlighted ip address by the one you got before

VSCode set Docker:Host

Once done, you come back to the panel and you click on "refresh" icon (top right of each sections) and you would get information from your dockerd running in WSL2

VSCode it works

Making everything works without knowing IP

Now, how to run dockerd and docker without copy&paste IP address in command line nor VSCode.

In WSL2, it's not possible to assign IP address but, I can use the windows port forwarding to redirect a local port from the host to a specific one of my distribution. Hence I could put "tcp://localhost:2375" in VsCode and the calls will be redirected to dockerd running in WSL2-Ubuntu.

For this, I run the powershell script lines in windows terminal running as administrator :

$ip = (wsl sh -c "hostname -I").Split(" ")[0]

netsh interface portproxy add v4tov4 listenport=2375 connectport=2375 connectaddress=$ip

wsl sh -c "sudo dockerd -H tcp://$ip"

Script explanation :

  • First, I collect the IP address of my default distro with the wsl command.
  • Second, I set the port forwarding 2375 to my distro
  • Third, I launch in my distro dockerd with the IP

When executing these lines you'll be prompted to enter your distro password (sudo) and I'll see after the log of dockerd. Everything will work fine when I'll see the message "API listen on 172.18.75.23:2375".

In parallel, in a windows terminal opened in my distro, I can check with top or htop if dockerd processes are running.

In VSCode, I update my Docker:Host setting with tcp://localhost:2375 :

VSCode Docker host to localhost

And the magic is there :

VSCode Docker extension working with localhost

Now I can know create a dedicated powershell script with the previous line : start_docker.ps1

In a windows terminal running with administrator privileges, I set the Execution policy with :

Set-ExecutionPolicy RemoteSigned

And every time I want to run dockerd, I launch the start_docker.ps1 script:

Launch docked from Windows

And if you see API Listen on 172.18.75.23:2375

Everything works !

Logs 1

Now, I want to use docker without -H parameter, for this, I add a new system environment variable called DOCKER_HOST set to tcp://localhost:2375

Add DOCKER_HOST as system variable

Finally, in a windows terminal, I can simply run a command like this:

docker image ls

Simple use of docker command

In conclusion

This article shows how we can use docker in windows and WSL2 without Docker Workstation
To do so, we just need first to run a powershell script launching dockerd in WSL2 and once dockerd is listening we can simply use the command docker (maintained by Stefan Scherer).

Yes ! We can continue to develop with containers without Docker Workstation.

Enjoy !

Top comments (29)

Collapse
 
pawelgnatowski profile image
Pawel Gnatowski

Would you be interested in how to do same without so much trickery?
Ip stuff port forwarding etc. I mean?
My simple repo can have you up and running. I will write an article eventually, but it is there.

Collapse
 
_nicolas_louis_ profile image
Nicolas Louis

Hi Pawel, thank you for your feedback. I tried to made some simplifications from the initial article from Jonathan Bowman. I had in mind to make my existing toolchains still working (VSCode, Visual Studio).
I'm very interested if you have a simpler way to proceed :)

Collapse
 
pawelgnatowski profile image
Pawel Gnatowski

I run this stack using this. Start of the month i will write full article, for now this will have to do.
Essentially i run docker, vs code , gpu compute (inside containers too) all on ubuntu wsl2. With proper networking, which is the biggest blunder from M$ to date in regard to wsl..
github.com/pawelgnatowski/WSL2-Net...

Collapse
 
herbatnik profile image
Marek Kreśnicki

Exactly my thoughts, there's too much complexity here + there's more comprehensive guide on how to install docker in Linux on official docker website which takes half of this article.

Collapse
 
dlvoy profile image
Dominik Dzienia

For me launching dockerd failed since chain of commands with ifconfig returned some extra garbage.

I was able to fix it with adding | head -n 1 at the end, so final command would look like:

sudo dockerd -H `ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 |awk '{ print $2 }' | cut -f2 -d: | head -n 1`
Enter fullscreen mode Exit fullscreen mode
Collapse
 
patrickleboutillier profile image
Patrick LeBoutillier

You need to escape the dot (.) in the regexp as such:

"([0-9]{1,3}[.]){3}[0-9]{1,3}"

Then it will work as expected.

Collapse
 
jaipsharma profile image
Jai Prakash Sharma

I followed all the steps but unable to run docker on my WSL2 -

Any help in debuggin -

sudo dockerd -H ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.
0.0.1 |awk '{ print $2 }' | cut -f2 -d:

[sudo] password for jai:
INFO[2021-11-06T15:39:08.506977000+05:30] Starting up
WARN[2021-11-06T15:39:08.509171500+05:30] Binding to IP address without --tlsverify is insecure and gives root access on this machine to everyone who has access to your network. host="tcp://169.254.255.121:2375"
WARN[2021-11-06T15:39:08.509628200+05:30] Binding to an IP address, even on localhost, can also give access to scripts run in a browser. Be safe out there! host="tcp://169.254.255.121:2375"
WARN[2021-11-06T15:39:10.291048100+05:30] Binding to an IP address without --tlsverify is deprecated. Startup is intentionally being slowed down to show this message host="tcp://169.254.255.121:2375"
WARN[2021-11-06T15:39:10.292307700+05:30] Please consider generating tls certificates with client validation to prevent exposing unauthenticated root access to your network host="tcp://169.254.255.121:2375"
WARN[2021-11-06T15:39:10.292918800+05:30] You can override this by explicitly specifying '--tls=false' or '--tlsverify=false' host="tcp://169.254.255.121:2375"
WARN[2021-11-06T15:39:10.294801200+05:30] Support for listening on TCP without authentication or explicit intent to run without authentication will be removed in the next release host="tcp://169.254.255.121:2375"
failed to load listeners: listen tcp 169.254.255.121:2375: bind: cannot assign requested address

jai@FA057586:~$ wsl
WSL
Wsman Shell commandLine, version 0.2.1

Collapse
 
nelsonpena profile image
Nelson Peña

I had the same error, it seems it's because you are using WSL version 1. Just run wsl --set-default-version 2, and re install your linux distribution.

Collapse
 
byteskater profile image
Christopher Lutat

Thanks for the article, I was able to successfully implement most of it.

I am stuck here trying to start dockerd from the Windows PowerShell (in admin mode):
PS C:\Users\clutat> wsl sh -c "sudo dockerd -H tcp://$ip"
sudo: dockerd: command not found

Any ideas what's incorrect here?

Collapse
 
jvmlet profile image
Furer Alexander

Thanks Nicolas. Have you managed to mount volumes from windows to docker image running in WSL2 ? Looks like windows folder has to be mounted first to WLS2 and then from WSL2 to the docker image ....

Collapse
 
herbatnik profile image
Marek Kreśnicki

It's easy, by default (at least for me) wsl has mounted all drives in /mnt// for example /mnt/c/ for C: Drive and /mnt/d/ for D: drive
From there you can simply use these paths as youve mentioned

Collapse
 
jvmlet profile image
Furer Alexander

yes, you are right... but.
Previously with Docker Desktop we could run docker with -v %cd%/someFolder:/whatever or -v ./someFolder:/whatever, now we have to provide full path , like -v /mnt/c/full/local/path/to/someFolder:/whatever , which is user specific and will not run on team mate's computer... Any thoughts how to overcome this ?

Collapse
 
_nicolas_louis_ profile image
Nicolas Louis

When windows folders are mounted and you launch a terminal to your WSL2 distro, you can navigate on your windows folders with commands like : wsl cd MyWindowsDirectory... wsl ls etc... without to be connected to a bash, just direcly from windows :)
When Dockerd is simply launched in your distro, you can run also "wsl docker ps" etc...

Collapse
 
zalithka profile image
Andre Greeff

so.. my morning started out heading towards this rabbit-hole, but then fortunately I checked with our HR department, and discovered that my employer doesn't exceed the requirements for a commercial Docker Desktop license. big relief for me right there..

while this post does contain lots of super technical points (yeah, I saw those comments), this is a super technical topic.. which leads straight back to the "how" and "why" of Docker's decision on this matter.

anyways, with the deadline for this looming ever closer, I suspect there are going to be a sudden stupendous influx of "Docker alternative" and "Docker without Docker Desktop" articles, debates, and so on.. not unlike this one.

so before that gets out of control: I'd like to share one that I did discover just this morning: devopstales.github.io/home/docker-...

it has lots of helpful information presented in a clear way, and the alternatives it lists don't require any "special magic" to get working, which might be very appealing for some.


with all that said: I do sincerely hope that anyone able and/or required to pay for a license actually does so... it would be really sad for Docker to have come this far, having influenced so many aspects of "containerization", only to fade into the background because of "suddenly not being free to everybody". at the end of the day, everybody still has bills to pay.. 🤔

Collapse
 
altanmehmet profile image
Altan Mehmet Türkmen

I got this error when I tried to run "sudo dockerd -H ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d:" And I can't see my eth0 configs in ifconfig command
error:failed to load listeners: listen tcp 169.254.218.38:2375: bind: cannot assign requested address
Thanks for the help.

Collapse
 
petrysko profile image
Petrysko • Edited

Hi guys,

I am receiving error at launch docker with

sudo dockerd -H ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d:

The error is: failed to start daemon: pid file found, ensure docker is not running or delete /var/run/docker.pid

I tried deleting pid file but i dont have permission for it i tried using sudo systemctl stop docker and then running it but error is still the same.

Can anyone help me with it please ?

Thank you
`

Collapse
 
maihoang626 profile image
maihoang626

sudo dockerd -H ifconfig eth0 | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d:

above command in "Launch dockerd" section shows this error : "dockerd" accepts no argument(s)

Collapse
 
pvlerick profile image
Philippe Vlérick

Hello, there is a small error in regex provided to get the host's IP address; if the output of ifconfig eth0 returns this:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.109.90  netmask 255.255.240.0  broadcast 172.17.111.255
        inet6 fe80::215:5dff:fe3a:5592  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:3a:55:92  txqueuelen 1000  (Ethernet)
        RX packets 4570  bytes 2075002 (2.0 MB)
        RX errors 0  dropped 3  overruns 0  frame 0
        TX packets 238  bytes 26662 (26.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
Enter fullscreen mode Exit fullscreen mode

it will match the line starting with "TX packets too".

Here is the corrected version: ifconfig eth0 | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: