Multi-Architecture Images for Docker Hub

Docker Manifest

Multi-architecture Docker images can be created using Docker Manifest and pushed to Docker Hub. I’ll give an example on how to do this.

Normally when you build a Docker image and push it to Docker Hub it will work on the architecture on which you build it. For a recent project I needed to implement some Python code to run on a Raspberry Pi. For convenience and access to tools I did the development on my laptop. After the development was done I created a Docker image and uploaded it to Docker Hub. On the Raspberry Pi I then tried to pull and run the image from Docker Hub – but it wouldn’t start. That sparked my curiosity! Why was I able to pull e.g. the same Python image on different types of machines and not my own?

Enable Manifest Command

After some searching I found out what I needed to use was Docker Manifest. The Docker Manifest command will allow you to make a Manifest List that points to versions of an image for different architectures. However, this command is still experimental in the Docker CLI, so this needs to be enabled in the Docker CLI configuration file. On Linux and Mac look for this folder and file in your home dir:

~/.docker/config.json

Edit the file and add this line:

"experimental": "enabled"

Preparing Images

Before going ahead with the manifest command I needed to build the platform variant images I want to support. In my case I want support for the image on Linux/Mac and Raspberry Pi. My dockerfile is based on Python so the same dockerfile would be able to build on both systems. The Docker Build command does not (easily, at least) offer ‘cross-compiling’ so the image had to be build on each platform.

On my Mac I took the code and build the Docker Image with a tag indicating version and platform. This I pushed to Docker Hub.

docker build -t kstobbe/esp-update-server:1.0.3-amd64 .
docker push kstobbe/esp-update-server:1.0.3-amd64

Then I downloaded the same code to my Raspberry Pi and build the exact same dockerfile but with the Raspberry Pi Docker CLI. This image I tagged with the same version as previous but with a different platform. This was also pushed to Docker Hub.

docker build -t kstobbe/esp-update-server:1.0.3-arm32v6 .
docker push kstobbe/esp-update-server:1.0.3-arm32v6

Combining Images

At this point I had two images on Docker Hub under the same repository tagged with the same version number, 1.0.3 but with different platforms, -amd64 and -arm32v6. My goal is that the user should not have to worry about platforms, but only be concerned about the version and have Docker deliver the correct image for any platform.

This is where Docker Manifest comes into the picture. With Docker Manifest I can create a third ‘image’, which is really a manifest list, that is tagged with only the version, 1.0.3, which points to the two platform specific images. Docker will then make sure to get the correct one.

A bit of annotation of some images it needed to help Docker determine which image to use on which platforms. The manifest list can be pushed to Docker Hub like an ordinary image and will show up as such. In the commands below I both create and push version tags 1.0.3 and latest. Notice the --purge option on the push commands – I’ll get back to those.

docker manifest create kstobbe/esp-update-server:1.0.3 kstobbe/esp-update-server:1.0.3-amd64 kstobbe/esp-update-server:1.0.3-arm32v6
docker manifest annotate kstobbe/esp-update-server:1.0.3 kstobbe/esp-update-server:1.0.3-arm32v6 --os linux --arch arm
docker manifest push kstobbe/esp-update-server:1.0.3 --purge
docker manifest create kstobbe/esp-update-server:latest kstobbe/esp-update-server:1.0.3-amd64 kstobbe/esp-update-server:1.0.3-arm32v6
docker manifest annotate kstobbe/esp-update-server:latest kstobbe/esp-update-server:1.0.3-arm32v6 --os linux --arch arm
docker manifest push kstobbe/esp-update-server:latest --purge

My original problem was now solved. I was able to start the image on both Mac and Raspberry Pi without worrying about which platform it was build for. You’re welcome to try my ESP Update Server out.

docker run -v $PWD/bin:/esp-update-server/bin -p 5000:5000 kstobbe/esp-update-server:1.0.3

If you want to get into cross-platform building have a look at Building docker multiarch images.

No Remove Manifest?

In my exploring of the manifest command I ended up doing a first try. I got the images pushed to Docker Hub and the manifest created and pushed. I wanted to re-do it with a few modifications so I deleted all the uploaded images on Docker Hub. Then I re-did everything and ended up doing a docker manifest create for a manifest list with the same name and tag as in my first attempt. GRRRRR!! ERROR!! There was already a local manifest!

But how to get rid of the local manifest? There is no docker manifest rm command (or docker manifest list for that matter). But docker manifest push has the --purge option which will remove the local manifest after a successful push. But I would have to undo my second attempt and reconstruct the initial images for this to work.

I finally found out that on the Mac the manifest files were stored in the directory below.

/Users/kstobbe/.docker/manifests

Manually deleting the files did the trick. To avoid this headache I recommend always pushing manifests with option --purge.