Docker buildx – the best thing since sliced bread?

I’m still giddy; giddy about Docker buildx; giddy about creating multi-arch Docker images on my Mac and having them uploaded and properly stored on Docker Hub with a single, simple command.

Why I needed Docker buildx

Docker makes my life so much easier – period.

Unless there is really good reason to not Dockerize a piece of software it should be.

I run a small water-proof Raspberry Pi on the roof of my house. It has been serving as a LoRaWAN gateway for TheThingsNetwork for the past three years. Hence, it’s one of the devices here that’s on 24/7. Needless to say that the gateway software runs in a Docker container I helped create.

Consequently, I sometimes build new images for that RPi and since it runs on ARM architecture, rather than e.g. x86, creating such images is a little more involved (than it should). You can’t just build such images on Docker Hub. In order to work around that you can build on a CI platform like Travis CI using QEMU or build them on the target platform. The latter isn’t exactly convenient if you want to support multiple architectures.

Of course, if you make an image public it should ideally support multiple architectures in order to be useful for as many users as possible.

To cut a long story short: I want to write a single Dockerfile whose images run on ARM for e.g. RPi and x86 for everyone else.

Build multi-arch images with Docker buildx

My inspiration & guidance for the use of Docker buildx have been a Docker engineering post from April and a post from Collabnix.

Enable experimental Docker features

buildx comes bundled with Docker CE starting with 19.03. However, at the time of this writing it still requires experimental mode to be enabled. To do so you can either

  • activate it globally by going to Docker preferences > Deamon > enable checkbox
  • set the DOCKER_CLI_EXPERIMENTAL=enabled environment variable whenever needed e.g. in a build script
  • permanently add "experimental": "enabled" to the CLI configuration file ~/.docker/config.json

Create a buildx builder

Start by listing all architectures your Docker installation supports.

~/Data/my-image > docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
default docker
default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

Then create a new buildx builder and use it.

~/Data/my-image > docker buildx create --name my-builder
my-builder
~/Data/my-image > docker buildx use my-builder

To get insight into what it was created you can inspect the new builder

~/Data/my-image > docker buildx inspect --bootstrap
Name: my-builder
Driver: docker-container

Nodes:
Name: my-builder0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

Build and push the multi-arch image

With the preparation done building & pushing multi-arch images is now down to a single command. Most noteworthy is how similar this is to what you might already be familiar with.

~/Data/my-image > docker buildx build --platform \
linux/amd64,linux/arm64,linux/arm/v7 \
-t marcelstoer/my-image --push .
Docker buildx output showing how three images for three different architectures are built in parallel
Docker buildx building one image per selected architecture in parallel

Once done you’ll find a couple of freshly pressed images on Docker Hub – one for every architecture you selected. Yay!

Oh, and in case you were wondering…Yes, when you do a docker pull for this image Docker automatically selects the one for the matching architecture.

Leave a Reply