Using Docker for Multiarch Images on Travis

TCI-Graphics for AdsBlogs (3)

When a client, e.g. Docker client, tries to pull an image, it must negotiate the details about what exactly to pull with the registry given the conditionals. Let’s dive into multiarch builds using Docker in Travis.

Getting started

You can run a program compiled for Arm on 1amd64 on a Linux machine (if specified in your .travis.yml), if it has binfmt_misc support. In simple terms, binfmt allows you to run a program without worrying about which architecture it was originally built for. A crucial thing you must remember is binfmt_misc is not a de facto dependency on Ubuntu, but don’t fret, it’s really simple to install it via Docker.

Spin up the container

First, open up your .travis.yml set your dist to focal or xenial, there’s some distros that binfmt_misc mount error out on. In your .travis.yml file let’s add this line:

  - sudo docker run --privileged linuxkit/binfmt:v0.6

Next, we need to fetch the buildkit, so first we’ll start the buildkit server as a container process, then we’ll copy the buildctl binary which is the command line frontend for buildkit to our /usr/bin directory and then we’ll set the BUILDKIT_HOST environment variable. Now head back to your .travis.yml, let’s look at your before_install section again, and add the following:

  - sudo docker run --privileged linuxkit/binfmt:v0.6
  - sudo docker run -d --privileged -p 1234:1234 --name buildkit moby/buildkit:latest --addr tcp:// --oci-worker-platform linux/amd64 --oci-worker-platform linux/armhf
  - sudo docker cp buildkit:/usr/bin/buildctl /usr/bin/
  - export BUILDKIT_HOST=tcp://

Building the Docker Image

Let’s write the script to start building the images, in this example I’ll be using Arm:


buildctl build --frontend dockerfile.v0 \
        --frontend-opt platform=linux/${PLATFORM} \
        --frontend-opt filename=./${DOCKERFILE_LOCATION} \
        --exporter image \
        --exporter-opt${DOCKER_USER}/${IMAGE}:${TAG}-${PLATFORM} \
        --exporter-opt push=true \
        --local dockerfile=. \
        --local context=.

After writing the script (you can copy and paste the foundation I’ve provided) you have successfully automated the creation and push of multiarch docker images.


We now need to create a manifest, creating manifest is an experimental Docker CLI feature and you should update Docker. You’ll need to add the following lines to your .travis.yml:

      - docker-ce

With your Docker client open, also run:



What we want to do first is, we’ll create the manifest, then we annotate the manifest and finally then do a push using git push.

More information on manifest

I wrote a beautiful guide on manifest that can be found on my GitHub here. I highly recommend referring to this if you have any questions.


Final steps

Under the hood, the container runs a shell script from QEMU, that registers the emulator as an executor of binaries from the external platforms. So remember buildctl is stating that it must use the specified Dockerfile; (the one you wrote, or grabbed), and the conditional of it must build the images for the defined platforms (I specified linux/amd64,linux/arm64,linux/arm/v7), create a manifests list that’s tagged as the desired image, and push all the images to the registry. There you have it you just successfully used Docker for a multiarch build using Travis.


Support for multi-platform is getting easier and things that were hard say a year ago maybe even less are just mildly complex now, and with more and more features in the Travis CI pipeline, this will be only getting easier and easier.

If you have any questions please email me at

Happy building!