Docker: How to Build and Push multi-arch Docker Images to Docker Hub

Life-is-short--so--enjoy-it
6 min readOct 30, 2023

--

In 2019, Docker released a preview of improved multi-architecture builds within Docker Desktop as ARM based Cloud Computing and Edge & IoT devices were emerging.

Docker: How to Build and Push multi-arch Docker Images to Docker Hub

UPDATE: 2023–10–31

I built Unbound DNS Docker images with different architectures like ARM, ARM64, and AMD64 on different machines like x86_64 ( MacBook Pro — Intel ) and ARM64 ( MackBook Pro M1 ).

  • slow: building ARM and ARM64 ( at the same time with buildx ) on MackBook Pro M1 — ARM64 ( it took around 30hr ) — ARM64 completed first and waited for ARM image.
  • Super slow: building ARM and ARM64 on x86_64 ( at the same time with buildx ) on MackBook Pro — Intel ( it took around 1hr ) — Both images’ buid happened in docker-container.
  • Fast: building x86_64 on MackBook Pro — Intel ( 6min )
  • Fast: building ARM64 on MackBook Pro M1 ( 6min )

Although it’s possible to build multi-arch Images on local, I don’t think it’s good idea to do it. Maybe, it should be like buliding the images on remote where the the machine’s arch matches to the image’s arch.

Intro

In the past, since the prod environment I used to work on was mainly x86_64 ( Intel CPU ), I didn’t have to worry about ( or even need to think about ) to support the different architectures.

Recently, I’ve had opportunities to work on using and building Docker Images in different architectures like arm64 ( or aarm64 ) and amd64 ( or x86_64 ).

Today, I took a look how to build Docker Image with different architectures and push to Docker Hub. ( Yep. At this time, it’s Docker Hub only )

This post is a documentation about what I tried and found in two different approach

  1. Without using Docker BuildX
  2. With using Docker BuildX

Approach 1: Without Using Docker BuildX

In short, this approach is “Do it by yourself”.

This approach requires to create separate repositories for each architecture. The architecture’s name is a part of the repository name. ( mostly suffix ) And also, one extra repository is required for the multi-arch Image repository.

This approach has five steps.

  1. Create repos for each target architecture with suffix
  2. Build Images on Local
  3. Push Images to Docker Hub
  4. Create Manifest
  5. Push Manifest

Pros / Cons

  • Pros: “Build” can be faster since each Image can be built and pushed from the machine that uses the same architecture. ( no need to use docker-container that uses QEMU — slower )
  • Cons: Many manual steps
  • Cons: Many repositories to host each architectures

1. Create repos for each target architecture with suffix

I tried with arm64 and amd64, so the list of repositories’ name I created were like below

  • multiarch-example-arm64
  • multiarch-example-amd64
  • multiarch-example ( multiarch )
Docker multiarch: Create repos for each target architecture with suffix

2. Build Images on Local

I built images on each architecture ( platform ) manually one by one with the commands below

# Dockerfile content
FROM python:latest
RUN python -c 'import os;print(f"Arch: {os.uname().machine}")' > /tmp/arch.txt
# ARM64 or AARM64
docker build \
--tag wowbro/multiarch-example-arm64:v1 \
--platform linux/arm64/v8 .

#x86_64 or amd64
docker build \
--tag wowbro/multiarch-example-amd64:v1 \
--platform linux/amd64 .
Docker multiarch: Created images on each architecture

3. Push Images to Docker Hub

I pushed the built Images to each repository one by one.

Docker multiarch: Push images to its own repository
Docker multiarch: Docker Hub Repositories have its own architecutre Image

4. Create Manifest

I created a OCI manifest with the Images I built.

Each Docker image is represented by a manifest. A manifest is a JSON file containing all the information about a Docker image. This includes references to each of its layers, their corresponding sizes, the hash of the image, its size and also the platform it’s supposed to work on. This manifest can then be referenced by a tag so that it’s easy to find.
ref: https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/

docker manifest create wowbro/multiarch-example:v1 \
wowbro/multiarch-example-arm64:v1 \
wowbro/multiarch-example-amd64:v1
Docker multiarch: Inspect manifest and check architure ( or platform )

5. Push Manifest

The created manifest was pushed to Docker Hub.

docker manifest push wowbro/multiarch-example:v1
Push Docker multiarch’s manifest to Docker Hub
Docker multiarch is created with the hash tags for each architecture

Approach 2: With Using Docker BuildX

Unlike the Approach 1, this approach uses the automation that is built in “buildx” command.

Pros / Cons

  • Pros: No need to have extra repo for each architecture. ( One repository is enough ).
  • Pros: No need to manage the OCI manifest. It will be managed automatically.
  • Cons: Not easy ( or extra steps ) to load the built image on local
  • Cons: Slower since Images are built in docker-container that use QEMU. ( maybe remote builder can make it faster? )

Here were the steps I used.

  1. Create a new builder
  2. Build and Push Images

1. Create a new builder, named container, that uses the Docker container driver

There are four builders that Docker support.

  • docker ( default )
  • docker-container ( need to create )
  • kubernetes
  • remote

Among these, the docker-container builder has to be created with the command below.

https://docs.docker.com/build/drivers/
# --use option can set as the builder as a default builder
docker buildx create \
--name container \
--driver=docker-container
docker multiarch with buildx

2. Build and Push Images

Once the docker-container build was created, I built and pushed the multiarch Image to Docker Hub by using the command below.

The --platform flag told buildx to generate Linux images for Intel 64-bit, Arm 32-bit, and Arm 64-bit architectures. The --push flag generates a multi-arch manifest and pushes all the images to Docker Hub.

docker buildx build \
--tag wowbro/multiarch-example:latest \
--platform linux/arm/v7,linux/arm64/v8,linux/amd64 \
--builder container \
--push .
Docker multiarch is created with the hash tags for each architecture

Troubleshooting

If the new builder with docker-container doesn’t exist, the error message below will show up.

If so, then create one.

ERROR: Multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use"
docker buildx ls
docker buildx create --name container --driver=docker-container

References

--

--

Life-is-short--so--enjoy-it

Gatsby Lee | Data Engineer | City Farmer | Philosopher | Lexus GX460 Owner | Overlander