As you might have already figured out, Docker Desktop is priced very steeply for large companies. A huge part of Docker is the qemu emulator here, that allows you to emulate a couple or architectures on Windows itself.
How do we roll?
First thing first.
Basic setup
- You’ll need latest WSL2. You can get one here. Grab a Debian Bookworm while you’re at it.
- Launch bash, as a command. You should see a Linux prompt.
- Follow the Docker install guidelines on official Docker website (remember that you might not have sudo installed, so either skip it as a command or just flat out install it.
Replace chosen config files
Replace /etc/wsl.conf with
[network]
generateResolvConf = false
nameserver 8.8.8.8
[boot]
systemd=true
We’l need that systemd=true later. Replace /etc/resolv.conf with
nameserver 8.8.8.8
Or just use your own favourite DNS.
Installing qemu
To support multiple processor architectures, you’ll need a couple of extra packages. Just run these (again you might wish to prefix these with sudo:
apt install -y qemu-system qemu-system-x86 \
qemu-system-common qemu-system-arm \
qemu-user-binfmt \
libvirt-daemon-driver-qemu \
libvirt-clients libvirt-daemon
Qemu support
For a Linux kernel to actually have a clue that it’s running a ARM64 binary, it needs to have the formats registered. Once you’ve installed all the qemu components, we’ll just do that. Sadly, it needs to be ran after each mahine start. But before you do so (scripts for Docker come for free in /lib/systemd/system/docker.service), I’d like you to attach the following file to /lib/systemd/system/binfmt.service:
[Unit]
Description=Binfmt register service
After=docker.service
[Service]
Exec=/usr/bin/docker run --rm --privileged docker/binfmt:820fdd95a9972a5308930a2bdfb8573dd4447ad3
[Install]
WantedBy=multi-user.target
Now register it using systemctl enable binfmt.service. Also please try systemctl enable docker.service.
Now let’s really install systemd
I’m running Windows 11 or I have latest build of Windows 10.
Then see this article.
I’m running earlier Windows 10
Then I’m afraid you’ll have to examine a third-party script. Following snippet seriously helped me, since I was in no position to bump Windows, but I found the necessary care packages on the Internets:
cd /tmp
wget http://ftp.us.debian.org/debian/pool/main/i/icu/libicu67_67.1-7_amd64.deb
apt remove libicu75
dpkg -i libicu67_67.1-7_amd64.deb
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.23_amd64.deb
ddpkg -i libssl1.1_1.1.1-1ubuntu2.1~18.04.23_amd64.deb
wget --content-disposition \
"https://gist.githubusercontent.com/djfdyuruiry/6720faa3f9fc59bfdf6284ee1f41f950/raw/952347f805045ba0e6ef7868b18f4a9a8dd2e47a/install-sg.sh"
chmod +x /tmp/install-sg.sh
apt --fix-broken install
apt install -y policykit-1 gawk
/tmp/install-sg.sh
rm -f /tmp/install-sg.sh
How to launch my brand new Docker with Systemd
Now, you’d be tempted to just use bash. This is wrong. To start Docker with systemctl you need to call the following:
wsl --shutdown # when you're done with WSL
wsl genie -s # when you want to start WSL with a shell
Try to memorize the last command, wsl genie -s. Adapt to using it instead of running bash. Of course, if this session you’ve already started wsl genie -s, then there’s no obstacle for using bash. Genie is basically a tool to emulate a systemd environment for WSL setups.
Now reboot your machine and check if everything works.
How do I use it?
The way I roll by default is to spawn a WSL bash and run docker commands where needed. Since WSL has all my working files and can send things to the Docker Daemon we’re safe. Remember, that the docker and docker-compose must actually run under WSL, since that way they have access to the socket.
Will it work directly under Windows?
It’s quite possible, but so far, I haven’t given this much thought,.
The way I would do it is forward a TCP port (configured either in /lib/systemd/system/docker.service or /etc/docker/daemon.json, but never both – you’ll get startup errors), and then systemctl daemon-reload and systemctl restart docker.service and then set a global environment variable called DOCKER_HOST pointing at this. Also a proper configuration looks like this:
{"bip": "172.17.0.1/28",
"hosts": ["fd://", "tcp://0.0.0.0:2375"],
"experimental": true
}
I suppose forwarding a TCP port (remember that WSL defines it’s own bridge interface) and then defining it globally as a DOCKER_HOST environment variable would do the trick, but I’ll leave that as an exercise to the reader.