How to build a Docker image on a specific architecture with Docker Hub?

I solved my own issue after a bit of research... First, I was making a stupid mistake and second, I was forgetting a very important thing. Here's how I fixed my issues:

The Stupid Mistake

Although I specified different Dockerfiles for each automated build, I also had a build hook which was overwriting the docker build command and it was defaulting to Dockerfile for all builds instead of picking the right file.

Fixed build hook file:

#!/bin/bash

docker build \
    --file "${DOCKERFILE_PATH}" \
    --build-arg BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
    --build-arg VCS_REF="$(git rev-parse --short HEAD)" \
    --tag "$IMAGE_NAME" \
    .

The Important Thing

Like @JanGaraj mentioned on his answer, Docker Hub runs on amd64 so it can't run binaries for other architectures. How does one build multi-arch images with Docker Hub Automated Builds? With the help of qemu-user-static and more hooks. I found the answer on this GitHub issue but I'll post here the complete answer to my specific use case:

My sample project tree:

.
├── Dockerfile
├── Dockerfile.aarch64
├── Dockerfile.armhf
└── hooks
    ├── build
    ├── post_checkout
    └── pre_build

The post_checkout hook file:

#!/bin/bash

BUILD_ARCH=$(echo "${DOCKERFILE_PATH}" | cut -d '.' -f 2)

[ "${BUILD_ARCH}" == "Dockerfile" ] && \
    { echo 'qemu-user-static: Download not required for current arch'; exit 0; }

QEMU_USER_STATIC_ARCH=$([ "${BUILD_ARCH}" == "armhf" ] && echo "${BUILD_ARCH::-2}" || echo "${BUILD_ARCH}")
QEMU_USER_STATIC_DOWNLOAD_URL="https://github.com/multiarch/qemu-user-static/releases/download"
QEMU_USER_STATIC_LATEST_TAG=$(curl -s https://api.github.com/repos/multiarch/qemu-user-static/tags \
    | grep 'name.*v[0-9]' \
    | head -n 1 \
    | cut -d '"' -f 4)

curl -SL "${QEMU_USER_STATIC_DOWNLOAD_URL}/${QEMU_USER_STATIC_LATEST_TAG}/x86_64_qemu-${QEMU_USER_STATIC_ARCH}-static.tar.gz" \
    | tar xzv

The pre_build hook file:

#!/bin/bash

BUILD_ARCH=$(echo "${DOCKERFILE_PATH}" | cut -d '.' -f 2)

[ "${BUILD_ARCH}" == "Dockerfile" ] && \
    { echo 'qemu-user-static: Registration not required for current arch'; exit 0; }

docker run --rm --privileged multiarch/qemu-user-static:register --reset

The Dockerfile file:

FROM amd64/alpine:3.8
(...)

The Dockerfile.aarch64 file:

FROM arm64v8/alpine:3.8
COPY qemu-aarch64-static /usr/bin/
(...)

The Dockerfile.armhf file:

FROM arm32v6/alpine:3.8
COPY qemu-arm-static /usr/bin/
(...)

That's it!


Nowadays you can also use docker buildx to build for different architectures without having to maintain a separate Dockerfile for each. Building Multi-Architecture Docker Images With Buildx has a description of how that works. Basically, you

  1. register your QEMU simulator in binfmt_misc on the host with the fix-binary flag so that it can run in the container without having to copy it into the container file system
  2. use docker buildx build --platform linux/arm/v7 ... to instruct the builder to build for an architecture different from your build host's