DEV Community

Felipe Santos
Felipe Santos

Posted on • Updated on

Running Docker on WSL2 without Docker Desktop (the right way)

So, now that Docker Desktop is paid under certain scenarios, you may want to switch to something else.

This is a straight to the point guide on how to make Docker CE run fully on WSL2.

PS: the title says right way, but it is just my personal opinion. I am not claiming that any other guide does it in a wrong way.

What you will get

  • A full-fledged Docker installation on WSL2
  • Docker Daemon automatic start without any crazy hacks

What you will not get

  • Docker Daemon sharing between Windows and WSL (i.e. you cannot run docker from Windows PowerShell)
  • Docker Daemon sharing between WSL distributions

Requisites

I will consider that you already have WSL2 working, and you are using Ubuntu as your distribution.

Guide

  1. Install Docker CE on Ubuntu by following the official guide:

    # Ensures not older packages are installed
    $ sudo apt-get remove docker docker-engine docker.io containerd runc
    
    # Ensure pre-requisites are installed
    $ sudo apt-get update
    $ sudo apt-get install \
        ca-certificates \
        curl \
        gnupg \
        lsb-release
    
    # Adds docker apt key
    $ sudo mkdir -p /etc/apt/keyrings
    $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    
    # Adds docker apt repository
    $ echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    # Refreshes apt repos
    $ sudo apt-get update
    
    # Installs Docker CE
    $ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
  2. Perform the post-installation steps:

    # Ensures docker group exists
    $ sudo groupadd docker
    
    # Ensures you are part of it
    $ sudo usermod -aG docker $USER
    
    # Now, close your shell and open another for taking the group changes into account
    
  3. Make Docker Daemon start on WSL initialization:

    If you are running Windows 11, make sure you are using the Windows Subsystem for Linux installed from the Microsoft Store, since it supports Systemd starting from version 0.67.6 onwards (you can check with wsl.exe --version). In this case, you only need to add:

    [boot]
    systemd=true
    

    To your /etc/wsl.conf within your WSL distribution. Then, restart it with wsl.exe --shutdown. To verify that it works, you can run docker version. If you do not receive any permission denied error, you are good.

    But if you are not running Windows 11, you can achieve a similar result with the following approach:

    Open the ~/.profile (or ~/.zprofile if you are using ZSH rather than Bash) in your WSL distribution, and add a section like so to it:

    if service docker status 2>&1 | grep -q "is not running"; then
        wsl.exe -d "${WSL_DISTRO_NAME}" -u root -e /usr/sbin/service docker start >/dev/null 2>&1
    fi
    

    This piece of code will run every time you open a new shell on your WSL distribution. It checks whether the Docker Daemon is running, and if not, starts it without prompting for credentials. Without any noticeable delay.

    To verify, after making the changes, open a new shell and run the docker version command. If you do not receive any permission denied error, you are good.

    If you are unsure about which method to choose, you can choose both. It's harmless, I do it on mine.

If you want, you can also verify that Docker Compose got installed by running docker compose version.

Bonus

  1. Installing Docker Compose Switch (to use the legacy docker-compose command instead of docker compose):

    # Finds the latest version
    $ switch_version=$(curl -fsSL -o /dev/null -w "%{url_effective}" https://github.com/docker/compose-switch/releases/latest | xargs basename)
    
    # Downloads the binary
    $ sudo curl -fL -o /usr/local/bin/docker-compose \
        "https://github.com/docker/compose-switch/releases/download/${switch_version}/docker-compose-linux-$(dpkg --print-architecture)"
    
    # Assigns execution permission to it
    $ sudo chmod +x /usr/local/bin/docker-compose
    

    PS: I suggest to install the docker-compose binary to /usr/local/bin/ because otherwise, the VS Code - Remote Containers extension will not find it.

    To verify it works, you can run docker-compose version.

  2. Install the Docker Credential Helper:

    You will need this if you want Docker to store your credentials securely when you perform docker login. Thanks to the WSL interoperability between Windows, you can install the Windows version of the Docker Credential Helper inside of WSL itself.

    # Finds the latest version
    $ wincred_version=$(curl -fsSL -o /dev/null -w "%{url_effective}" https://github.com/docker/docker-credential-helpers/releases/latest | xargs basename)
    
    # Downloads and extracts the .exe
    $ sudo curl -fL -o /usr/local/bin/docker-credential-wincred.exe \
        "https://github.com/docker/docker-credential-helpers/releases/download/${wincred_version}/docker-credential-wincred-${wincred_version}.windows-$(dpkg --print-architecture).exe"
    
    # Assigns execution permission to it
    $ sudo chmod +x /usr/local/bin/docker-credential-wincred.exe
    

    Then, configure your Docker CLI to use it by assuring that the following is present in your ~/.docker/config.json:

    {
        "credsStore": "wincred.exe"
    }
    

    To verify that it works, you can try to docker login and if not, Docker will complain about storing credentials in plain text.

  3. Enabling Docker BuildKit:

    As it came enabled by Docker Desktop before. It's simple, ensure that the following is present in your /etc/docker/daemon.json:

    {
        "features": {
            "buildkit": true
        }
    }
    

    You will need to edit this file as root, so make sure to use sudo before running your editor (sudo nano /etc/docker/daemon.json).

Final considerations

The entire setup process may take some time, but you will have achieved almost everything that Docker Desktop used to provide to you (by the way, I use k3d as an alternative to Docker Desktop's built-in K8s provisioner).

However, you can achieve a similar (and even higher/better) level of easiness that Docker Desktop provided to you by wrapping all the steps above in your dotfiles installation steps.

For example, in my dotfiles, all these steps are automated, including configuring /etc/docker/daemon.json, changing ~/.profile, and even providing a way to automatically update your extra binaries (docker-compose, or the wincred.exe) every time you update your dotfiles (by using a feature of chezmoi - a dotfiles manager which I totally recommend).

Oldest comments (36)

Collapse
 
zachary profile image
zachary

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Collapse
 
felipecrs profile image
Felipe Santos

What shows up if you type sudo service docker start?

Collapse
 
felipecrs profile image
Felipe Santos

Chances are that your WSL version isn't new enough to support boot.command in wsl.conf. Did you try the .profile trick as mentioned in the guide?

Collapse
 
nmtoken profile image
James Passmore • Edited

Going just with adding the boot command to wsl.conf, I don't get a permission denied error, but 'docker version' gives version information and the Cannot connect to the Docker daemon at unix:///var/run/docker.sock.... error. using command = "sudo service docker start" has the same effect. Editing the .profile does work though

Collapse
 
felipecrs profile image
Felipe Santos

Most likely your WSL version isn't new enough to support this feature.

Collapse
 
jledesma84 profile image
jledesma84

I have the same issue. I'm using WSL2. Any solution?

Collapse
 
felipecrs profile image
Felipe Santos • Edited

This specific feature is only available in Windows 11. See the link below for reference.

I also updated the guide to make this clearer.

docs.microsoft.com/en-us/windows/w...

But you can, of course, just use the alternative approach as the guide mentions.

Collapse
 
lemosluan profile image
Luan Lemos Almeida

I've the same issue, and using wsl2 and ubuntu 22.04;

I found the possible reason.
If you are using debian or some ubuntu version that has iptables-nft as default:
Install Docker on Windows (WSL) without Docker Desktop.

Try it

sudo update-alternatives --config iptables

and change it to iptable-legacy

It works for me. ;)

Thread Thread
 
felipecrs profile image
Felipe Santos

That's interesting to know about. I haven't tried yet with Ubuntu 22.04, but I will try and update the guide in case something like this is missing. Thank you!

Thread Thread
 
felipecrs profile image
Felipe Santos • Edited

It turns out that it's indeed necessary for Ubuntu 22.04 on WSL. So, I added instructions for it in the guide. Thanks again for pointing out!

Thread Thread
 
felipecrs profile image
Felipe Santos

Just to update you, later versions of docker seems to have fixed the issue without requiring this step so I have removed it from the guide.

Collapse
 
eramosce profile image
eramos-ce

Felipe, this worked great! I am trying to identify what to migrate to now that Docker for Windows changed their licensing model.

Thank you for sharing!

Collapse
 
felipecrs profile image
Felipe Santos

Awesome! I'm very glad it helped you!

Collapse
 
trevmlt profile image
trevmlt

Signed up just to say thanks for this! Was driving me crazy that docker would not start automatically in Ubuntu. Was able to start manually with service docker start but with the addition to ~/.profile it starts automatically!

Collapse
 
felipecrs profile image
Felipe Santos

You are very welcome!

Collapse
 
darwinjs profile image
Darwin Sanoy

Thanks for this!

Which guide to docker networking would make a port I expose when running docker in wsl available to the Windows host?

So if in this docker setup I run docker run -p 3000:3000 -it ruby bash - I would like to be able to get to that port from the browser in Windows.

Collapse
 
felipecrs profile image
Felipe Santos

You get that by default on every ports exposed in WSL, accessing through localhost. Unless you explicitly disabled. See this and this.

Collapse
 
terminatedprocess profile image
Mark Ryan

Hi Felipe! Thank you for this excellent guide! I was able to install using your dotfile link. All seems to work, however, I can't use docker login. It just times out. I do have WIndows 11 fully updated, and WSL 2. I even tried unregistering Ubuntu and loading a fresh copy. Repeated things and it won't let me log in. I'm using the username on the dockerhub upper right corner and the password I created when I created my account. What could I be doing wrong?

Collapse
 
terminatedprocess profile image
Mark Ryan

I repeated the whole process installing the wincred properly. This time it worked. Thanks!

Collapse
 
felipecrs profile image
Felipe Santos

I'm not sure if there was something wrong with the dotfiles installation (which could be fixed by running chezmoi apply --force if so), or maybe some bug with the WSL interoperability with .exe - which I have more frequently than I liked.

Anyway, I'm glad it's working now. :-)

Collapse
 
mattschlosser profile image
Matt Schlosser

If you get the Cannot connect to the Docker daemon at unix:///var/run/docker.sock.... error. and have already tried starting the docker daemon, it may be that your docker daemon is crashing. This will happen if your WSL is still on version 1.

To update, open PowerShell and type

wsl.exe --list --verbose
Enter fullscreen mode Exit fullscreen mode

If your distro is labeled as version 1, you can update it with

 wsl.exe --set-version Ubuntu 2
Enter fullscreen mode Exit fullscreen mode

Where "Ubuntu" is the name of your distro.

Collapse
 
felipecrs profile image
Felipe Santos

That's absolutely true. Thanks for sharing the tip!

Collapse
 
mariomeyrelles profile image
Mario Meyrelles

Thanks Felipe. Works like a charm. Lovely :)

Collapse
 
david24622147 profile image
David

This is an excellent replacement except I haven't been able to get K8s clusters from the WSL side to connect on the devcontainer side. Docker Desktop offers its built-in cluster API at a special address that resolves correctly back out of the devcontainer. Any other cluster: kind, k3d, minikube, created on the WSL side will not resolve inside the devcontainer.

This is the devcontainer feature I'm using to enable cluster access on the devcontainer side using its kubeconfig copy script to copy my local kubeconfig in.

Have you tried this? Is Docker Desktop somehow blessed in its operation?

Collapse
 
felipecrs profile image
Felipe Santos

In all my devcontainers, I add --network=host to the runArgs of it, so that it does not suffer of this issue.

However, have you tried Rancher Desktop? It may work for you.

Collapse
 
douglewis22 profile image
douglewis22 • Edited

Felipe,

First let me say thank you for sharing this with us! It is a very good help.

I do have one small issue. Today when I attempted to get the credential helper setup I get the following error:

curl: (22) The requested URL returned error: 404

I can see the .exe file for v0.7.0 but not seeing a .zip file though. So, I downloaded the .exe file and copied it to the /usr/local/bin location, issued the chmod command and tried the docker login and it was successful.

Collapse
 
felipecrs profile image
Felipe Santos

Hey, thanks for reporting it.

You're right about copying the file to /usr/local/bin, but don't forget to give it execution permission as well.

I updated the guide to point the new changes.

Collapse
 
mhangaard profile image
Martin Hangaard Hansen

Thank you for this tutorial @felipecrs . Will this work together with an Azure Client installed on windows?

For example:
$ which az
/mnt/c/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin/az

with az acr login?

Thanks
Martin

Collapse
 
felipecrs profile image
Felipe Santos

If it delegates to the docker cli I see no reason for it not to work. Let me know if you ended up testing it, though.

Collapse
 
mhangaard profile image
Martin Hangaard Hansen

I couldn't get it to do it, but worked around by taking the token from az and passing it to docker login

docker login ${MY_ACR}.azurecr.io -u 00000000-0000-0000-0000-000000000000 -p $(az acr login -n ${MY_ACR} --expose-token | jq .'accessToken' | tr -d \")
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ac76 profile image
Adam Carr • Edited

You can run the commands in Powershell. You need to add proxy functions to your $PROFILE.

Replace the {} tokens with the relevant distribution and the username you use in that distribution.

function docker {
    wsl -d "{DISTRO_NAME}" -u {DISTRO_USER} -e docker $args
}

function docker-compose {
    wsl -d "{DISTRO_NAME}" -u {DISTRO_USER} -e docker-compose $args
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
felipecrs profile image
Felipe Santos

Thanks for the tip, I would also recommend using PowerShell-WSL-Interop to achieve this.

Collapse
 
sstranks profile image
Simon Stranks

Any idea how to get rootless working on WSL2? I can get the rootless daemon running successfully, but it won't connect to the docker registry when trying to pull images - 'no such host'.

I managed to get it working on WSL2 Ubuntu 20.04 then stupidly upgraded it (and WSL2). Something somewhere obviously broke and now I can't get it working again.

Collapse
 
sstranks profile image
Simon Stranks

OK just found the solution: github.com/microsoft/WSL/issues/74...

I'm using WSL2 on Windows 10, Ubuntu 22.04 LTS.

  • Install Docker Engine using the official instructions, using apt-repository method docs.docker.com
  • Install Docker Rootless using the official instructions docs.docker.com - add the '--skip-iptables' flag to the install command.
  • As per the solution linked above, create/amend the '/etc/wsl.conf' file to enable 'systemd' (works on WSL2 now, both Windows 10 and 11) and disable WSL from automatically creating '/etc/resolv.conf', so we can create our own manually and set the nameserver to 1.1.1.1.

You don't need to change iptables to legacy anymore either, you can leave them on the newer nft setting.

Collapse
 
bhabermann profile image
Bruno Habermann

Excelente conteúdo! Me ajudou demais.

Collapse
 
abirb profile image
abirax • Edited

Hi Felipe,

Thanks for the article, I am on ubuntu 22 I am able to start docker but unable to pull images from docker.io I get an error
When I run docker pull hello-world
i get the error:
Using default tag: latest
Error response from daemon: unknown:

408 Request Time-out


Your browser didn't send a complete request in time.

any idea? My proxies are fine since I can run curl docker.io -v
I also tried docker login and it didnt work
I get errors like
docker: Error response from daemon: Get "registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io on [::1]:53: read udp [::1]:56718->[::1]:53: read: connection refused.