--- title: "Cómo crear imágenes multiarquitectura con Docker Buildx" description: "Construye y publica imágenes de Docker para amd64 y arm64 desde un único Dockerfile con Buildx y QEMU en Ubuntu 24.04, con compilación cruzada y caché." category: "Docker Avanzado" language: es source: https://cubepath.com/es/docs/docker-avanzado/docker-buildx-para-imagenes-de-multiples-arquitecturas updated: 2026-09-25 --- # Cómo crear imágenes multiarquitectura con Docker Buildx Cada vez hay más servidores y equipos ARM (instancias arm64 en la nube, Apple Silicon, Raspberry Pi), y una imagen construida en un servidor x86_64 no se ejecuta en ellos. Docker Buildx, la interfaz de BuildKit, construye una misma imagen para varias arquitecturas y la publica bajo una sola etiqueta: cada máquina descarga automáticamente la variante que le corresponde. En este tutorial prepararás un servidor Ubuntu 24.04 para construir imágenes `linux/amd64` y `linux/arm64`, compilarás una aplicación de Go con compilación cruzada y publicarás la imagen en un registro. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM y un usuario no root con privilegios `sudo`. - Docker Engine instalado desde el repositorio oficial de Docker, que incluye el plugin `docker-buildx-plugin`. - Tu usuario en el grupo `docker`, o anteponer `sudo` a los comandos `docker`. - Una cuenta en un registro de contenedores (Docker Hub en esta guía, con tu usuario `your_user`). Las imágenes multiarquitectura se publican en un registro, no se guardan como una sola imagen local. Comprueba que Buildx está disponible: ```bash docker buildx version ``` ```output github.com/docker/buildx v0.17.1 257815a ``` ## Cómo funciona una imagen multiarquitectura Una etiqueta multiarquitectura, como `nginx:alpine`, no apunta a una imagen sino a un *índice* (manifest list) que contiene una imagen por plataforma. Al hacer `docker pull`, el cliente elige la entrada que coincide con su arquitectura. Buildx tiene dos formas de producir la variante de otra arquitectura: | Método | Cómo funciona | Cuándo usarlo | |---|---|---| | Emulación con QEMU | Ejecuta las instrucciones `RUN` de la otra arquitectura mediante un emulador | Cuando no puedes cambiar el proceso de build; es lento en pasos de compilación | | Compilación cruzada | El compilador se ejecuta de forma nativa y genera binarios para la arquitectura de destino | Go, Rust, .NET y otros lenguajes que lo admiten; mucho más rápido | En este tutorial configurarás QEMU (necesario en cualquier caso para instrucciones como `apk add` en la imagen final) y usarás compilación cruzada para la parte pesada. ## Paso 1: Registrar los emuladores de QEMU Para que el kernel pueda ejecutar binarios arm64 en un servidor x86_64, hay que registrar los emuladores de QEMU en `binfmt_misc`. La forma que documenta Docker es la imagen `tonistiigi/binfmt`: ```bash docker run --privileged --rm tonistiigi/binfmt --install arm64 ``` ```output installing: arm64 OK { "supported": [ "linux/amd64", "linux/arm64", "linux/386" ], "emulators": [ "qemu-aarch64" ] } ``` Usa `--install all` si necesitas más arquitecturas, como `arm/v7` o `riscv64`. Este registro no sobrevive a un reinicio del servidor. Si quieres que sea permanente, instala el paquete de Ubuntu, que registra los emuladores al arrancar mediante `systemd-binfmt`: ```bash sudo apt install qemu-user-static ``` Comprueba que el emulador de arm64 está registrado: ```bash ls /proc/sys/fs/binfmt_misc/ | grep aarch64 ``` ```output qemu-aarch64 ``` ## Paso 2: Crear un builder con el controlador docker-container El builder por defecto de Docker (controlador `docker`) no puede exportar imágenes multiplataforma con el almacén de imágenes clásico. Crea un builder nuevo con el controlador `docker-container`, que ejecuta BuildKit en un contenedor aparte, y márcalo como el builder activo: ```bash docker buildx create --name multiarch --driver docker-container --use --bootstrap ``` Lista los builders y las plataformas que admite cada uno: ```bash docker buildx ls ``` ```output NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS multiarch* docker-container \_ multiarch0 \_ unix:///var/run/docker.sock running v0.16.0 linux/amd64*, linux/arm64*, linux/386 default docker \_ default \_ default running v0.16.0 linux/amd64, linux/386 ``` El asterisco junto al nombre indica el builder activo. `linux/arm64` aparece en la lista gracias al emulador del paso 1. ## Paso 3: Preparar una aplicación y un Dockerfile multiplataforma Crea un directorio para el proyecto: ```bash mkdir -p ~/hello-arch && cd ~/hello-arch ``` Crea una aplicación de Go que muestra la arquitectura en la que se ejecuta: ```bash nano main.go ``` ```go package main import ( "fmt" "runtime" ) func main() { fmt.Printf("Hola desde %s/%s\n", runtime.GOOS, runtime.GOARCH) } ``` Crea el Dockerfile: ```bash nano Dockerfile ``` ```dockerfile # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS build ARG TARGETOS ARG TARGETARCH WORKDIR /src COPY main.go . RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/hello main.go FROM alpine:3.20 RUN apk add --no-cache ca-certificates COPY --from=build /out/hello /usr/local/bin/hello ENTRYPOINT ["hello"] ``` Las piezas clave son las variables que BuildKit define automáticamente en cada build: - `BUILDPLATFORM` es la plataforma de la máquina que construye (aquí `linux/amd64`). Con `FROM --platform=$BUILDPLATFORM`, la etapa de compilación se ejecuta siempre de forma nativa, sin emulación. - `TARGETOS` y `TARGETARCH` indican la plataforma de destino de cada variante (`linux` y `amd64` o `arm64`). Go las usa para compilar el binario de la arquitectura correcta. - La etapa final `FROM alpine:3.20` no lleva `--platform`, así que Buildx usa la variante de Alpine de cada plataforma de destino. Su `RUN apk add` es lo único que se ejecuta con QEMU en la variante arm64, y es un paso rápido. ## Paso 4: Construir y publicar la imagen Inicia sesión en Docker Hub. Usa un token de acceso en lugar de tu contraseña: ```bash docker login -u your_user ``` Construye las dos variantes y publícalas en una sola etiqueta: ```bash docker buildx build \ --platform linux/amd64,linux/arm64 \ -t your_user/hello-arch:1.0 \ --push . ``` Buildx construye ambas plataformas en paralelo y, al terminar, sube las imágenes y el índice que las agrupa: ```output [+] Building 38.2s (22/22) FINISHED docker-container:multiarch => [linux/amd64 build 5/5] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ... => [linux/arm64 build 5/5] RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ... ... => pushing manifest for docker.io/your_user/hello-arch:1.0@sha256:... ``` Observa que las dos etapas `build` aparecen con su plataforma de destino, pero ambas se ejecutan de forma nativa. ## Paso 5: Verificar el índice y probar cada arquitectura Inspecciona la etiqueta publicada para ver las variantes que contiene: ```bash docker buildx imagetools inspect your_user/hello-arch:1.0 ``` ```output Name: docker.io/your_user/hello-arch:1.0 MediaType: application/vnd.oci.image.index.v1+json Digest: sha256:4c1e... Manifests: Name: docker.io/your_user/hello-arch:1.0@sha256:9a2b... MediaType: application/vnd.oci.image.manifest.v1+json Platform: linux/amd64 Name: docker.io/your_user/hello-arch:1.0@sha256:e7f0... MediaType: application/vnd.oci.image.manifest.v1+json Platform: linux/arm64 Name: docker.io/your_user/hello-arch:1.0@sha256:1d3c... MediaType: application/vnd.oci.image.manifest.v1+json Platform: unknown/unknown Annotations: vnd.docker.reference.type: attestation-manifest ``` Las entradas `unknown/unknown` son atestaciones de procedencia que BuildKit añade por defecto; no son imágenes ejecutables. Ejecuta la imagen en la arquitectura nativa del servidor: ```bash docker run --rm your_user/hello-arch:1.0 ``` ```output Hola desde linux/amd64 ``` Y fuerza la variante arm64, que se ejecutará con QEMU: ```bash docker run --rm --platform linux/arm64 your_user/hello-arch:1.0 ``` ```output Hola desde linux/arm64 ``` En un servidor arm64 real, `docker run your_user/hello-arch:1.0` descargará directamente la variante arm64 sin que tengas que indicar nada. ## Paso 6: Acelerar builds con caché en el registro El builder `docker-container` guarda su caché dentro de su propio contenedor, y se pierde si lo eliminas o si construyes desde otra máquina, como un runner de CI. Puedes guardar la caché en el registro junto a la imagen: ```bash docker buildx build \ --platform linux/amd64,linux/arm64 \ -t your_user/hello-arch:1.1 \ --cache-from type=registry,ref=your_user/hello-arch:buildcache \ --cache-to type=registry,ref=your_user/hello-arch:buildcache,mode=max \ --push . ``` `mode=max` guarda también las capas de las etapas intermedias, como la de compilación, que son las que más tiempo ahorran. Si repites el comando sin cambiar el código, verás `CACHED` en casi todos los pasos. ## Paso 7: Probar localmente una sola plataforma Con el almacén de imágenes clásico de Docker no puedes cargar un índice multiplataforma en local con `--load`, pero sí una sola plataforma. Es útil para probar sin publicar: ```bash docker buildx build --platform linux/arm64 -t hello-arch:arm64-test --load . docker run --rm hello-arch:arm64-test ``` ```output Hola desde linux/arm64 ``` ## Solución de problemas **`ERROR: Multi-platform build is not supported for the docker driver`.** Estás usando el builder por defecto. Activa el builder del paso 2 con `docker buildx use multiarch`. **`exec format error` al construir o ejecutar.** El emulador de QEMU no está registrado, habitual después de reiniciar el servidor si usaste solo `tonistiigi/binfmt`. Repite el paso 1 o instala `qemu-user-static`. **El build de arm64 es muy lento.** Alguna instrucción pesada (compilar, `npm install` con módulos nativos) se ejecuta con emulación. Muévela a una etapa con `FROM --platform=$BUILDPLATFORM` y usa compilación cruzada, o añade un nodo arm64 nativo al builder con `docker buildx create --append --name multiarch ssh://usuario@servidor-arm64`. **`denied: requested access to the resource is denied` al hacer push.** No has iniciado sesión o la etiqueta no empieza por tu usuario u organización del registro. ## Conclusión Has registrado QEMU, creado un builder de Buildx y publicado una imagen para amd64 y arm64 bajo una sola etiqueta, usando compilación cruzada para que el paso pesado no dependa de la emulación, y has añadido caché en el registro. Como siguientes pasos, puedes automatizar el build en CI con las acciones oficiales `docker/setup-qemu-action`, `docker/setup-buildx-action` y `docker/build-push-action`, añadir un nodo ARM nativo al builder o publicar las imágenes en un registro privado.