Cloud Native 16 min read

Building and Using Multi‑Architecture Docker Images with Buildx, CI/CD Integration, and Promotion

This article explains what multi‑architecture Docker images are, why they are useful, and provides step‑by‑step instructions for building them with traditional Docker commands or Docker buildx, integrating the builds into Jenkins and GitHub Actions pipelines, promoting images across environments, scanning for vulnerabilities, and important considerations to keep in mind.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Building and Using Multi‑Architecture Docker Images with Buildx, CI/CD Integration, and Promotion

What Is a Multi‑Architecture Container Image?

A multi‑architecture Docker image is a manifest list that references binaries and libraries compiled for several CPU architectures (e.g., ARM, x86, RISC‑V). It allows the same application to run on different hardware without maintaining separate images for each architecture.

Multi‑Architecture Container Use Cases

Performance and Cost Optimization : Build and deploy architecture‑specific images to achieve better performance and lower resource consumption.

Cross‑Platform Development : Use docker buildx to create images that run on both ARM and x86 for testing and release.

IoT/Edge Devices : Deploy images that run on ARM‑based edge devices as well as x86 servers.

Benefits of Using Multi‑Architecture Container Images

Run Docker images on multiple CPU architectures.

Select environmentally friendly CPU architectures.

Seamlessly migrate from one architecture to another.

Leverage arm64 for better performance and cost savings.

Utilize more CPU cores on arm64 platforms.

How to Build Multi‑Architecture Container Images?

There are several approaches; the article focuses on two widely used methods.

Traditional Docker build commands.

Docker buildx for multi‑arch builds.

Using Traditional Docker Build Commands

Manually build separate images for each architecture on machines that match the target CPU, push them to a registry, and create a manifest JSON that references both images.

FROM nginx
RUN echo "Hello multiarch" > /usr/share/nginx/html/index.html
# on amd64 machine
docker build -t username/custom-nginx:v1-amd64 .
docker push username/custom-nginx:v1-amd64

# on arm64 machine
docker build -t username/custom-nginx:v1-arm64 .
docker push username/custom-nginx:v1-arm64

# create manifest index file
docker manifest create \
    username/custom-nginx:v1 \
    username/custom-nginx:v1-amd64 \
    username/custom-nginx:v1-arm64

# push manifest
docker manifest push username/custom-nginx:v1

Using Docker Buildx

With buildx a single command can build and push images for multiple platforms.

docker buildx build \
  --push \
  --platform linux/arm64,linux/amd64 \
  -t username/custom-nginx:v1 .

Behind the scenes, docker buildx uses BuildKit and a container running moby/buildkitd with QEMU binaries to emulate the target CPU instruction sets. The appropriate QEMU binary (e.g., /usr/bin/buildkit-qemu-aarch64 ) is selected based on the --platform flag.

After building both images, the --push flag creates a manifest and pushes the images together to the registry.

Inspecting Multi‑Architecture Manifests

You can view the manifest with docker manifest inspect -v or with docker buildx imagetools inspect for a more readable format.

$ docker manifest inspect -v nginx
[ ... JSON output showing Ref and platform fields ... ]

Integrating Multi‑Architecture Builds into CI/CD

Embedding the build process into CI pipelines ensures consistent image creation across environments.

Jenkins Multi‑Architecture CI

Jenkins Docker plugin does not support multi‑arch directly, so buildx is used.

pipeline {
    agent { label 'worker1' }
    options { timestamps(); timeout(time: 30, unit: 'MINUTES'); buildDiscarder(logRotator(numToKeepStr: '10')) }
    environment {
        DOCKER_REGISTRY_PATH = "https://registry.example.com"
        DOCKER_TAG = "v1"
    }
    stages {
        stage('build-and-push') {
            steps {
                script {
                    docker.withRegistry(DOCKER_REGISTRY_PATH, ecrcred_dev) {
                        sh '''
                        export DOCKER_BUILDKIT=1
                        if [[ $(docker buildx inspect --bootstrap | head -n 2 | grep Name | awk -F" " '{print $NF}') != "multiarch" ]]; then
                          docker buildx rm multiarch || true
                          docker buildx create --name multiarch --use
                          docker buildx inspect --bootstrap
                        fi
                        docker buildx build --push --platform linux/arm64,linux/amd64 \
                          -t "$DOCKER_REGISTRY_PATH"/username/custom-nginx:"$DOCKER_TAG" .
                        '''
                    }
                }
            }
        }
    }
}

GitHub Actions CI for Multi‑Architecture Images

GitHub Actions also supports multi‑arch builds using QEMU and Buildx.

name: docker-multi-arch-push
on:
  push:
    branches: ['main']
jobs:
  docker-build-push:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64,linux/arm64
          push: true
          tags: username/custom-nginx:latest

Promoting Multi‑Architecture Images to Higher Environments

Because docker pull only fetches the image matching the host CPU, you must pull each architecture explicitly and then create a manifest for promotion. Tools like skopeo or crane can copy all architectures in a single command.

$ skopeo login --username $USER docker.io
$ skopeo copy -a docker://dev-account/custom-nginx:v1 docker://prod-account/custom-nginx:v1

If you prefer plain Docker commands, you can pull, tag, and push each architecture, then create and push a manifest:

# Pull DEV images
docker pull --platform=amd64 $DOCKER_IMAGE_NAME_DEV:$DOCKER_TAG
docker pull --platform=arm64 $DOCKER_IMAGE_NAME_DEV:$DOCKER_TAG

# Tag for STAGE
docker tag $DOCKER_IMAGE_NAME_DEV:$DOCKER_TAG $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-amd64
docker tag $DOCKER_IMAGE_NAME_DEV:$DOCKER_TAG $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-arm64

# Push each architecture
docker push $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-amd64
docker push $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-arm64

# Create and push manifest
docker manifest create \
    $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG \
    --amend $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-amd64 \
    --amend $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG-arm64

docker manifest push $DOCKER_IMAGE_NAME_STAGE:$DOCKER_TAG

Scanning Multi‑Architecture Images for Vulnerabilities

Tools such as Trivy, Gryp, or Docker Scan can be used, but each architecture must be pulled and scanned separately because the default docker pull only retrieves the image matching the host CPU.

# Scan amd64 image
docker pull --platform=amd64 nginx:latest
trivy image nginx:latest

# Scan arm64 image
docker pull --platform=arm64 nginx:latest
trivy image nginx:latest

Important Considerations When Using Multi‑Architecture Containers

Storing images for additional architectures consumes extra storage.

Building multi‑arch images takes more time; QEMU emulation for arm64 is especially resource‑intensive.

Emulated binaries run slower than native binaries.

Buildx may encounter issues such as missing base images for arm64 or the need for sudo privileges for certain cross‑compilation steps.

All images must be scanned individually.

Buildx multi‑arch builds are currently only supported on amd64 hosts.

Conclusion

This article introduced multi‑architecture container images, their use cases, and how to build them using traditional Docker commands or Docker buildx. It demonstrated integration with Jenkins and GitHub Actions pipelines, methods for promoting images across environments, scanning for vulnerabilities, and highlighted key considerations for successful adoption.

By leveraging multi‑arch images, developers can build once and run everywhere, seamlessly migrate between CPU architectures, and achieve better performance and cost efficiency by deploying architecture‑optimized images.

DockerCI/CDContainerqemubuildxmulti-arch
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.