- Multi-architecture images group AMD64 and ARM64 variants under the same label using manifest lists.
- Docker buildx and QEMU allow you to build multi-arch images from a single host for multiple platforms.
- Tools like Docker Manifest, ACR Tasks, or ECR make it easy to store and manage these images in cloud registries.
- Integrating multi-arch into CI/CD (GitHub Actions, CodeBuild, etc.) simplifies supporting mixed environments and migrations to ARM.

If you work with containers and need your applications to run on both classic x86 servers and ARM-based devices, sooner or later you're going to have to grapple with the multi-architecture Docker imagesThe good news is that today the ecosystem has matured considerably, and creating, publishing, and using these images is much simpler than it first appears.
Throughout this article we will see, calmly and in detail, how Create multi-arch images for AMD64 and ARM64, generate and publish manifests And what tools are available to you (Docker buildx, docker manifest, GitHub Actions, ACR Tasks, AWS CodeBuild, etc.). You'll see practical examples from Linux, macOS with an ARM chip, Raspberry Pi, and even CI/CD scenarios in the cloud.
Context: Why you need multi-architecture images
The first key point is understanding what problem we're solving. With Docker, we package applications and dependencies into portable and reproducible containersThat works great as long as your entire infrastructure uses the same CPU architecture, normally AMD64 (x86-64)But today the picture is much more varied.
On one hand we have architecture AMD64 (or x86-64), introduced by AMD as an extension of 32-bit x86 processors. It's the dominant architecture in desktop PCs, laptops, and many servers: it allows 64-bit addressing, improves performance, and remains compatible with older 32-bit x86 software. In short, the de facto standard in traditional computing.
On the other side is ARM (AArch64 / ARM64)It is very prevalent in mobile phones, tablets, embedded systems, and, increasingly aggressively, in data center servers. It is a RISC architecture designed to be very energy efficientWith smaller chips and better performance per watt, it is ideal for low-power devices and for reducing operating costs in the cloud.
In recent years, processors such as AWS Graviton/Graviton2 and Ampere solutions, along with the popularization of Raspberry Pi or Macs with Apple Silicon chips (ARM64). All of this leads us to want to run the same application on mixed environments: AMD64 development teams, ARM clusters in production, hybrid test environments, etc.
This change brings with it a challenge: if you only create Docker AMD64 images, your containers won't run natively on ARM (and vice versa). You could compile manually for each type of machine, but that's a maintenance nightmare. Multi-architecture images solve precisely this problem.
What is a multi-architecture Docker image?
A multi-architecture image is not a “magic image” that simply appears everywhere. It is actually a set of specific images by architecture (for example, one for AMD64 and another for ARM64) that are grouped under the same label by means of a list of manifests.
Each container image is described by a JSON manifestThis manifest specifies the image configuration, its layers, sizes, and the digest (an immutable SHA256 hash that identifies it). When you want to support multiple architectures, you create a list of manifests (also called the image index in the OCI world) that references several individual manifests, each associated with a specific operating system and architecture.
For example, a list of manifests can group an image Linux/amd64Another Linux/arm64 and even some Windows/amd64To the user, everything appears as a single image with a tag (for example, myimage:latest), but internally the registry maintains that index with the list of variants.
The funny thing is that, when you do docker pull or docker runThe Docker client negotiates with the registry and automatically selects the manifest that matches the architecture and operating system of the machine where it's running. You don't have to change the tag or remember which architecture you're using: the ecosystem itself chooses for you.
This same idea is supported by Docker Hub, Azure Container Registry, Amazon ECR and many registries compatible with the Docker Registry v2 specification and OCI. In other words, the storage aspect is more than mature; what is still evolving are the build tools.
Supported platforms and typical scenarios

Docker allows you to build images for a wide range of platforms. Among the most relevant for Linux containers are: linux/amd64, linux/arm64, linux/arm/v7 (typical in Raspberry Pi), in addition to less common architectures such as linux/riscv64, linux/ppc64le, linux/s390x o linux/386. There is also support for windows/amd64 and, to a lesser extent, in containers, darwin/amd64.
In practice, the most common combination today is Linux/amd64 and Linux/arm64It's the one that will allow you to deploy on conventional x86 servers and on next-generation ARM machines, both in the cloud and on-premises.
Examples of scenarios where multi-arch makes perfect sense:
- You develop on a Mac ARM64 But you're deploying on an EC2 AMD64 instance: you want to build an image locally that will then run on x86 without setting up an extra build server.
- CI/CD on ARM, x86 productionYou use cheap Graviton2 instances for compiling and testing, but your production environment is still on AMD or Intel.
- Hybrid Kubernetes clusters: an EKS with AMD64 and ARM64 node groups, where the same Deployment must work on both architectures without changing Kubernetes manifests.
- Raspberry Pi in the lab and x86 servers in production, sharing the same “meta-image” to ensure almost identical behavior.
In all these cases, the ideal is to have a single image reference (for example, myapp:1.0) that internally resolves the appropriate architecture.
First steps: preparing the environment in Linux (Ubuntu)
Let's see how to configure a Linux computer (for example, Ubuntu 24.04) to create images for both ARM64 and AMD64 using a single host. This type of setup is usually based on QEMU and binfmt-support, which allow emulating binaries from other architectures.
We assume you already have Docker installed and workingFirst of all, it's advisable to update the system:
sudo apt update
sudo apt upgrade
Next, we install the necessary components for buildx and multi-arch emulation. In many recent distributions, buildx is already integrated as a Docker plugin, but in others, the explicit package must be installed along with QEMU and binfmt:
sudo apt install docker-buildx qemu-system-arm qemu-user-static binfmt-support
With this, the kernel will be able to register different binary formats and use QEMU in user mode to run binaries from other architectures (e.g., ARM64) from an AMD64 host, without mounting complete virtual machines.
We can verify that buildx detects the new platforms with:
sudo docker buildx inspect –bootstrap
The result should show a list of platforms among which they will appear Linux/amd64 and Linux/arm64along with other possibilities. If you haven't yet created a custom builder, this command also initializes one based on the docker-container driver.
Setting up a multi-architecture builder with Docker buildx
Docker buildx It is an extension of the Docker CLI that provides access to the capabilities of BuildKit, the modern construction engine. Among its advantages are the integrated support of multiple platforms, the ability to parallelize builds and useful options such as –push o –output advanced.
To work comfortably with multiple architectures, we will create a new dedicated builder, using the docker-container driver (which is the one that supports cross-platform compatibility):
sudo docker buildx create –name multibuilder
sudo docker buildx use multibuilder
sudo docker buildx ls
If you want to check the configuration in more detail, you can run:
sudo docker buildx inspect –bootstrap
The output should indicate something similar to:
- driver: docker-container
- Supported platforms: linux/amd64, linux/arm64, linux/arm/v7, etc.
From here on, anyone docker buildx build The program you launch using this builder will be able to target multiple architectures simultaneously using the parameter –platform.
Building separate images for AMD64 and ARM64 on Ubuntu 24.04
Before we get into creating the joint manifest, it's very useful to do a basic test building a simple image for each architecture separately and verifying that it works on each type of machine.
Imagine you have a very simple Dockerfile for Ubuntu 24.04 that installs Apache and serves a "Hello World" page. That Dockerfile, along with some auxiliary HTML files, is in the directory /mnt/storage/dockerfiles/ubuntu-24.04-apache-basic/.
From the AMD64 host (for example, a Ryzen or Intel-based system), you could build and export the images using:
sudo /usr/bin/docker buildx build –platform linux/amd64 \
/mnt/storage/dockerfiles/ubuntu-24.04-apache-basic/ \
-t ubuntu-24.04-apache-basic:latest \
–output type=docker,dest=/mnt/storage/dockerimages/ubuntu-24.04-apache-basic/$(date+»%Y%m%d-%H%M»).AMD64.tar
sudo /usr/bin/docker buildx build –platform linux/arm64 \
/mnt/storage/dockerfiles/ubuntu-24.04-apache-basic/ \
-t ubuntu-24.04-apache-basic:latest \
–output type=docker,dest=/mnt/storage/dockerimages/ubuntu-24.04-apache-basic/$(date+»%Y%m%d-%H%M»).ARM64.tar
Here we are creating two independent tarballs, one per architecture, using –output type=docker to obtain a file that can then be uploaded with docker load -iIt has to be done in two steps because the Docker-type output does not support packaging multiple architectures in the same tar file at the same time.
Parameter –platform define the target platforms; and -t Set the image label. A useful convention is to include the date and time in the correct format. YYYYmmdd-HHMM to version the builds.
Verify the images on AMD64 and ARM64 machines

To verify that everything is working, you can use an AMD64 desktop computer and a Raspberry Pi 4 (ARM64) such as a test bench, or any other combination of machines.
On the AMD64 machine, you first confirm the architecture with:
hostnamectl
The output should show x86-64 as the kernel architecture. Then, you load the image:
sudo docker load -i /mnt/storage/dockerimages/ubuntu-24.04-apache-basic/20251013-0244.AMD64.tar
You check that it is present with:
docker images
And you execute it by mapping port 8080 to port 80 of the container:
sudo docker run -d -p 8080:80 –name ubuntu-24.04-apache-basic ubuntu-24.04-apache-basic
If you access http://127.0.0.1:8080 In the browser, you should see the "Hello World" message served by Apache from the AMD64 image.
On the ARM64 machine (for example, a Raspberry Pi 4 with IP 10.0.0.190), you perform a similar process. You check the kernel with hostnamectl, load the ARM64 image:
sudo docker load -i /mnt/storage/dockerimages/ubuntu-24.04-apache-basic/20251013-0244.ARM64.tar
You check with docker images and you lift the container:
sudo docker run -d -p 8080:80 –name ubuntu-24.04-apache-basic ubuntu-24.04-apache-basic
Next, you access via browser to http://10.0.0.190:8080 And you confirm that Apache responds correctly on ARM64. This proves that your Dockerfile is portable across architectures and that building with buildx/QEMU works.
Cleaning environments and good housekeeping practices
Once testing is complete, it's a good idea to clean up the environments to prevent the accumulation of unused containers and images. On both machines, you can stop and remove the container, and then delete the image.
sudo docker container stop ubuntu-24.04-apache-basic
sudo docker container rm ubuntu-24.04-apache-basic
sudo docker image rm ubuntu-24.04-apache-basic
Furthermore, if you work intensively with multi-arch, it is advisable to automate periodic cleanings of outdated images, temporary builders, and intermediate layers to save disk space, especially in CI/CD environments.
Create and publish a multi-architecture image with Docker Manifest
The most classic approach to multi-arch consists of build separate images for each architectureupload them to the registry (Docker Hub, ACR, ECR, etc.) and then Join them using Docker Manifest within a single multi-arch tag.
The general idea is:
- You build and publish myimage:amd64 y myimage:arm64 in your registration.
- You create a list of manifests that combines both under a new tag, for example myimage:multi o myimage:latest.
- You push that manifest to the registry.
- From there, you do docker pull myimage:multi and Docker downloads the variant that corresponds to the host architecture.
A simple example in Azure Container Registry would be:
docker tag myimage:arm64
myregistry.azurecr.io/multi-arch-samples/myimage:arm64
docker push myregistry.azurecr.io/multi-arch-samples/myimage:arm64
docker tag myimage:amd64
myregistry.azurecr.io/multi-arch-samples/myimage:amd64
docker push myregistry.azurecr.io/multi-arch-samples/myimage:amd64
Then you create the manifest list:
docker manifest create myregistry.azurecr.io/multi-arch-samples/myimage:multi \
myregistry.azurecr.io/multi-arch-samples/myimage:arm64
myregistry.azurecr.io/multi-arch-samples/myimage:amd64
And finally you publish it:
docker manifest push myregistry.azurecr.io/multi-arch-samples/myimage:multi
If you now inspect this tag with docker manifest inspectYou will see a JSON structure with several manifests internal, each with its own digest, size, architecture, and operating system. The same applies if you work with Docker hub o Amazon ECR.
All-in-one multi-arch build with Docker buildx
The previous method works well and helps to understand what a manifest is, but it's somewhat cumbersome. That's where Docker buildx It shines: it allows you to do a multi-arch build directly, automatically generating and pushing the manifest to the registry.
For example, starting with a Dockerfile like this for Alpine:
FROM alpine
RUN apk add util-linux
CMD ["lscpu"]
You could launch a multi-arch build and upload it to Docker Hub with:
docker buildx build –platform linux/amd64,linux/arm64 \
-t foo4u/demo-multiarch:2
–push .
What buildx does under the hood is:
- Transfer the build context to the builder container.
- Create an image for each architecture listed in –platform.
- Upload all images to the registry with the indicated tag.
- Generate a list of manifests that reference them and publish it under that same tag.
If you then run docker buildx imagetools inspect foo4u/demo-mutliarch:2You will see an output indicating a manifest list with platforms Linux/amd64 and Linux/arm64each with its own digest. When you run docker on an x86 host, you'll see that lscpu displays x86_64, and on an ARM host it will show aarch64.
This flow is much easier to automate in CI/CD, as it only requires a single buildx command. –platform y –push to have everything ready in the registry.
Multi-arch with GitHub Actions and other CI systems
When your team grows and you want to avoid manual builds, the logical thing to do is integrate multi-arch building into your CI/CD pipeline. Nowadays, GitHub Actions, GitLab CI, Jenkins, CircleCI and company offer runners with support for Docker and buildx, as well as specific actions to configure QEMU and multi-arch.
A typical workflow with GitHub Actions would be:
- In the repository, you create a workflow that runs on every push to the main branch.
- You use an action like docker/setup-buildx-action to install and configure buildx and QEMU.
- You log in to Docker Hub or another registry with credentials stored in GitHub Secrets.
- You throw a docker buildx build –platform linux/amd64,linux/arm64 –push with your image tag.
The result is that, without consuming local resources, your repository generates images ready for multiple platforms, always synchronized with the code in the main branch. For public projects, the free GitHub Actions tier is usually sufficient; for private projects, you get included minutes and then pay as you go.
ACR Tasks and manifest lists in Azure Container Registry
If you work in AzureACR Tasks offers a very convenient way to create and insert multi-architecture images directly in Azure Container Registry, using multi-step YAML definitions.
The strategy is usually similar to the Docker manifest manual: on one hand, you create and upload architecture-specific images, and then you invoke docker manifest create y docker manifest push from a task.
An example of a YAML file for ACR Tasks could be:
- Building an ARM64 image from dockerfile.arm64 and label it with an execution ID.
- Build an AMD64 image from dockerfile.amd64, also with that ID.
- Upload both to the registry.
- Create a multi-archive manifest labeled as latest combining the two previous tags.
- Push the manifest and optionally inspect it at the end.
This fits very well with Azure DevOps or GitHub Actions pipelines integrated with ACR, and delegates the management of builds and publication of manifests to the Azure platform itself.
Multi-architecture imaging on AWS: CodeBuild, CodePipeline, and Graviton2
In AWS, the pattern is usually to combine CodePipeline y CodeBuild to orchestrate builds across different architectures. AWS provides build environments based on both Intel/AMD x86 as in ARM (Graviton2)And Amazon ECR seamlessly understands the manifest lists generated with Docker.
A typical design to withstand x86 and ARM on Amazon EKS would:
- Un EKS cluster with two managed node groups: one AMD64 and one ARM64, each labeled with its architecture (kubernetes.io/arch).
- A pipeline of AWS CodePipeline with three stages: Source (GitHub), Build (two parallel actions for x86 and ARM) and BuildManifest (creates the multi-arch image).
- At Build, two projects from CodeBuildOne runs on x86 instances and generates the image for AMD64; the other runs on ARM instances and generates the image for ARM64. Both are published on Amazon ECR.
- In BuildManifest, a job combines both images into a single multi-arch manifest, also uploaded to ECR.
From that moment on, the EKS cluster uses a unique image URI (for example, 123456789012.dkr.ecr.region.amazonaws.com/miapp:multi) to deploy on both types of nodes. Kubernetes, with the help of the architecture information and the scheduler, takes care of placing each Pod on the appropriate node for the corresponding image.
This approach takes advantage of Graviton2's best cost/performance ratio In many workloads, without giving up the flexibility to revert to x86 if something isn't working properly, simply by reusing the AMD64 part of the multi-arch image.
Tips and considerations when creating multi-archive images
Beyond theory and examples, there are several details that should be clear when designing images for AMD64 and ARM64 systematically:
- Make sure that the basic image (FROM …) may also be multi-arch or have separate variants. You can check this with docker buildx imagetools inspect on the base image (for example, alpine, python:alpine3.10, amazoncorretto, etc.).
- If your language allows it (Go, Java, .NET), then favor cross-platform builds that generate portable binaries or bytecode that are not architecture-dependent, so that the same artifact can be used on multiple platforms.
- When using native tools (C/C++, libraries with native extensions, etc.), you will need to ensure that the compilation process supports cross-compiling or that you can compile natively on each architecture.
- For local scenarios on macOS or Windows, Docker Desktop already comes with integrated buildx and QEMUSo, many times you don't need to set up anything else for multi-arch builds.
- On pure Linux, if you want to install buildx "manually," you can download the binary from GitHub and place it in ~/.docker/cli-plugins/docker-buildxgranting it execution permissions. On ARM64, for example, you can automate this with a small script that uses the GitHub API to locate the latest release and download the arm64 binary.
With all this in mind, setting up a robust flow for Create multi-architecture images and publish manifests for AMD64 and ARM64 It ceases to be a rare experiment and becomes a fairly standard practice, and above all, very useful when working with hybrid environments, gradual migrations to ARM, or Kubernetes clusters with heterogeneous nodes.
Ultimately, the key is combining the pieces well: using QEMU and buildx when you want to compile from a single host, relying on tools like docker manifest or ACR Tasks when you need more granular control, and scaling all of this to CI/CD with GitHub Actions, CodeBuild, or similar pipelines to keep your multi-arch images always up-to-date and ready to run on any architecture without the team having to rack their brains on every deployment.
Passionate writer about the world of bytes and technology in general. I love sharing my knowledge through writing, and that's what I'll do on this blog, show you all the most interesting things about gadgets, software, hardware, tech trends, and more. My goal is to help you navigate the digital world in a simple and entertaining way.
