En los capítulos anteriores de este tutorial de Docker has visto como crear imágenes, gestionar contenedores, orquestar servicios con Docker Compose y aplicar buenas prácticas de seguridad. Pero hay un detalle fino: todas esas imágenes las has construido para la arquitectura de tu máquina local.
¿Qué pasa si tienes una Raspberry Pi en casa y quieres ejecutar tu aplicación en ella? ¿O si tu servidor en la nube usa un procesador ARM como los AWS Graviton? Si intentas ejecutar un contenedor x86 en una Raspberry Pi, te encuentras con este mensaje:
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8)
Fastidio, ¿verdad? Tu imagen funciona perfectamente en el portátil, pero en la Raspberry Pi no arranca. Y no es culpa de Docker ni de la Raspberry Pi. Es que el binario que compilaste es para procesadores Intel/AMD (x86_64) y el de la Raspberry Pi habla otro idioma (ARM64).
La computación en el borde (edge), el auge de Apple Silicon (ARM64), los servidores ARM de bajo consumo en la nube (AWS Graviton, Ampere Altra) y los dispositivos IoT como Raspberry Pi han fragmentado el ecosistema de procesadores. Antes solo tenías que preocuparte de x86_64. Ahora conviven media docena de arquitecturas y tus imágenes tienen que funcionar en todas.
Docker BuildX resuelve este problema de raíz. Es un plugin oficial de la CLI de Docker que aprovecha BuildKit para construir imágenes para múltiples arquitecturas simultáneamente desde un solo punto. Te permite construir para cualquier plataforma desde cualquier plataforma, y publicar un único tag que Docker resuelve automáticamente a la arquitectura correcta en cada cliente.
Vamos a ver qué trae este capítulo:
- Fundamentos de multi-arquitectura: por qué importa, diferencias entre amd64, arm64, arm/v7, y cómo Docker maneja los manifiestos multi-plataforma
- Instalación y configuración de BuildX: docker buildx create/ls/use, builders nativos y remotos
- QEMU y binfmt: emulación binaria transparente para arquitecturas que no tienes en hardware
- Construcción multi-plataforma:
--platform,--output, tipos de drivers (docker, docker-container, kubernetes, remote) - Caché avanzada:
--mount=type=cache, caché inline, registry, local, S3/GCS - Docker Buildx Bake: orquestación declarativa multi-target con HCL/JSON/YAML
- CI/CD multi-arquitectura: GitHub Actions y GitLab CI con BuildX
- Lenguajes compilados vs interpretados: Go (cross-compile nativa) y Python (multi-stage + multi-plataforma)
- Testing y depuración local: verificación de imágenes multi-arch sin necesidad de hardware real
- Script de gestión completo: funciones reutilizables para el día a día
Al terminar este capítulo serás capaz de construir una imagen que funcione en amd64, arm64 y arm/v7 desde un solo comando, publicarla en tu registry, y desplegarla en cualquier infraestructura sin sobresaltos.
El problema de las arquitecturas múltiples
Cuando ejecutas docker pull ubuntu:24.04 en un portátil x86_64, Docker descarga la imagen linux/amd64. En una Raspberry Pi 5 (ARM64), descarga linux/arm64/v8. En un Raspberry Pi Zero (ARMv6), descarga linux/arm/v6. Todo esto ocurre de forma transparente porque las imágenes oficiales publican manifiestos multi-arquitectura.
Es como si en una estantería tuvieras una misma caja etiquetada como «Ubuntu 24.04», pero dentro hubiera versiones distintas para cada tipo de procesador. Docker elige la que corresponde automáticamente según la máquina donde ejecutas docker pull.
¿Qué es un manifiesto multi-arquitectura?
Un manifest list (también llamado fat manifest o index OCI) es un JSON que contiene referencias a imágenes específicas por plataforma. Piensa en él como un índice: el tag ubuntu:24.04 apunta a este índice, y el índice contiene las direcciones exactas de las imágenes para cada arquitectura.
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"platform": { "architecture": "amd64", "os": "linux" },
"digest": "sha256:a1b2c3..."
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"platform": { "architecture": "arm64", "os": "linux", "variant": "v8" },
"digest": "sha256:d4e5f6..."
}
]
}
Cuando haces docker pull, el cliente elige la entrada que coincide con tu arquitectura nativa. Si no hay coincidencia exacta, Docker puede emular con QEMU.
Arquitecturas más comunes
Estas son las plataformas que te vas a encontrar en el mundo real:
- linux/amd64 — Servidores Intel/AMD, portátiles x86 tradicionales. Sigue siendo la más común en servidores cloud tradicionales.
- linux/arm64 (v8) — Apple Silicon M1-M4, AWS Graviton, Raspberry Pi 4-5, servidores ARM. Es la que más está creciendo.
- linux/arm/v7 — Raspberry Pi 2-3, tablets ARM de 32 bits. Aún hay mucho dispositivo legacy.
- linux/arm/v6 — Raspberry Pi Zero/Zero W, dispositivos IoT muy extendidos.
- linux/arm/v5 — Dispositivos embebidos muy antiguos. Cada vez más raros.
- linux/386 — Servidores x86 de 32 bits. Casi extintos pero aún aparecen en entornos legacy.
- windows/amd64 — Contenedores Windows. Requieren un host Windows, no funcionan en Linux.
¿Por qué no basta con docker build tradicional?
docker build (sin BuildX) solo construye para la arquitectura del host. Es como tener una cocina que solo sabe hacer tortillas de patatas. Si quieres hacer una tortilla francesa, necesitas otra cocina.
Para obtener una imagen multi-arch con docker build tradicional tendrías que:
- Tener una máquina de cada arquitectura
- Construir en cada una por separado
- Subir cada imagen con un tag diferente (ej:
mi-app:latest-amd64,mi-app:latest-arm64) - Crear un manifest list manualmente con
docker manifest create
Es un proceso tedioso y propenso a errores. BuildX automatiza todo esto en un solo comando. Una receta de cocina única que produce el plato adaptado a cada comensal.
Instalación y configuración de BuildX
BuildX viene preinstalado con Docker Desktop (macOS y Windows) y con las versiones recientes de Docker Engine en Linux (a partir de Docker 23.0+). Para verificarlo:
docker buildx version
Si el comando devuelve la versión, ya lo tienes. Si no, vamos a instalarlo.
Si no lo tienes, instálalo desde GitHub:
# Última versión estable
BUILDX_VER=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | grep tag_name | cut -d'"' -f4)
mkdir -p ~/.docker/cli-plugins
curl -sSL "https://github.com/docker/buildx/releases/download/${BUILDX_VER}/buildx-${BUILDX_VER}.linux-amd64" -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
docker buildx version
Gestión de builders
BuildX introduce el concepto de builder — una instancia de BuildKit que ejecuta las builds. Es como tener varios talleres: cada uno con herramientas diferentes, y puedes elegir cuál usar según lo que necesites construir.
Puedes tener múltiples builders y cambiar entre ellos:
# Listar builders disponibles
docker buildx ls
# Crear un builder con driver docker-container (recomendado multi-arch)
docker buildx create --name multiarch --driver docker-container --bootstrap
# Crear un builder que use el socket de Docker nativo (solo arch local)
docker buildx create --name native --driver docker
# Cambiar al builder multiarch
docker buildx use multiarch
# Inspeccionar un builder
docker buildx inspect multiarch
# Eliminar un builder
docker buildx rm multiarch
La salida de docker buildx ls muestra algo como esto:
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
multiarch * docker-container
multiarch0 unix:///var/run/docker.sock running v0.20.0 linux/amd64, linux/amd64/v2, linux/arm64, linux/arm/v7, linux/arm/v8
native docker
native0 unix:///var/run/docker.sock running v0.20.0 linux/amd64
Fíjate en la diferencia: el builder multiarch (driver docker-container) soporta múltiples plataformas, mientras que native (driver docker) solo soporta la arquitectura local.
Drivers de BuildX
Cada driver tiene sus fortalezas:
- docker — No soporta multi-arch (solo arquitectura nativa). No requiere QEMU. Ideal para builds simples en local donde no necesitas multi-plataforma.
- docker-container — Soporta multi-arch completo. Requiere QEMU para arquitecturas que no tienes en hardware. Es el más usado y el que te recomiendo para casi todo.
- kubernetes — Soporta multi-arch. QEMU opcional. Ideal si ya tienes un cluster Kubernetes y quieres usarlo como granja de builds.
- remote — Soporta multi-arch. No requiere QEMU porque usas builders nativos remotos. La opción más rápida para producción si tienes máquinas de cada arquitectura.
El driver docker-container es el más usado porque ejecuta BuildKit en un contenedor aislado con soporte completo de multi-plataforma. Es el que usaremos en este capítulo.
QEMU y binfmt: emulación transparente
Para construir imágenes para una arquitectura que no tienes en hardware, Docker utiliza QEMU en modo usuario a través del sistema binfmt_misc del kernel de Linux. Suena complejo, pero la idea es sencilla: el kernel reconoce binarios de otras arquitecturas y los ejecuta automáticamente con QEMU, sin que tú tengas que hacer nada.
Instalación de QEMU binfmt
El método más sencillo es usar el contenedor oficial de tonistiigi:
# Instalar QEMU binfmt (se ejecuta con privilegios para registrar los intérpretes)
docker run --privileged --rm tonistiigi/binfmt --install all
# Verificar que está instalado
ls /proc/sys/fs/binfmt_misc/
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
Después de esto, el sistema puede ejecutar binarios ARM64, ARMv7, etc. de forma transparente. BuildKit detecta automáticamente los intérpretes registrados y los usa cuando necesita construir para una plataforma que no es la nativa.
Importante: en Docker Desktop (macOS/Windows), QEMU binfmt ya viene preinstalado. Solo necesitas instalarlo manualmente en Linux. Si usas Docker Desktop, no hace falta que hagas este paso.
Limitaciones de QEMU
No todo es perfecto. QEMU tiene sus limitaciones y conviene conocerlas:
- Rendimiento: la emulación es 2-5x más lenta que la ejecución nativa. Para compilaciones pesadas (compilar un kernel, por ejemplo), mejor usa builders nativos.
- Instrucciones no emuladas: extensiones vectoriales avanzadas como AVX-512 o SVE pueden fallar porque QEMU no las implementa.
- Procesos multi-hilo: QEMU no escala perfectamente con múltiples hilos. Si tu build lanza muchos procesos paralelos, notarás la degradación.
Para builds de producción, lo ideal es combinar builders nativos de cada arquitectura mediante el driver remote. Así evitas la emulación y obtienes velocidad nativa en cada plataforma.
Construcción multi-plataforma
Vamos a la práctica. Ha llegado el momento de construir tu primera imagen multi-arch.
Primer build multi-arch
# Construir para amd64 + arm64 simultáneamente
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t usuario/mi-app:latest \
--push .
Este comando hace cuatro cosas en secuencia:
- Crea un builder implícito si no hay ninguno activo
- Construye la imagen para cada plataforma (en paralelo si es posible)
- Fusiona ambas en un manifest list
- Sube el manifest list al registry
Importante: necesitas --push o --output para que BuildX pueda escribir el manifest list. docker buildx build no carga la imagen en el almacén local de imágenes de Docker a menos que uses --load, y --load solo funciona con una plataforma a la vez.
# Cargar solo amd64 en local (para testing)
docker buildx build \
--platform linux/amd64 \
-t usuario/mi-app:latest \
--load .
Salidas (--output)
BuildX soporta múltiples tipos de salida. Cada uno sirve para un propósito distinto:
# Cargar en el almacén local de Docker (solo 1 plataforma)
docker buildx build --output type=docker .
# Exportar a un tar
docker buildx build --output type=tar,dest=output.tar .
# Exportar a directorio local (solo capa final)
docker buildx build --output type=local,dest=./output .
# Exportar como imagen OCI
docker buildx build --output type=oci,dest=image.tar .
El tipo docker carga la imagen en tu almacén local, como si hubieras hecho docker build de toda la vida. tar y oci son útiles para exportar imágenes a entornos sin conexión de red. local extrae el contenido del contenedor a un directorio, útil para debugging.
Dockerfile multi-plataforma: buenas prácticas
No todos los Dockerfiles funcionan igual en todas las arquitecturas. Aquí las claves para que los tuyos sean multi-arch sin dolores de cabeza:
# Usa imágenes base multi-arch (oficiales de Docker)
FROM alpine:3.21
# Los binarios deben ser multi-arch o compilados para cada plataforma
# Si instalas paquetes APK/APT, funcionan automáticamente
RUN apk add --no-cache curl
# Para lenguajes compilados, cross-compila en un stage previo
# Cuidado con comandos específicos de arquitectura (ej: hardcoding de paths)
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]
Regla de oro: usa siempre imágenes base oficiales de Docker (ubuntu, alpine, python, node, golang). Estas ya soportan multi-arquitectura. Las imágenes de terceros pueden no tenerla, y si dependes de una imagen que solo existe para amd64, tu build multi-arch fallará.
Caché avanzada con BuildX
Una de las ventajas más potentes de BuildX es el caché distribuido. No se limita a la capa local de Docker. Puedes compartir caché entre builds, entre desarrolladores, y entre máquinas CI. Esto reduce drásticamente los tiempos de construcción.
Tipos de caché
# Caché inline (incluye la caché en la imagen subida)
docker buildx build \
--cache-from type=registry,ref=usuario/mi-app:cache \
--cache-to type=inline \
--push -t usuario/mi-app:latest .
# Caché en registry (separada de la imagen, más eficiente)
docker buildx build \
--cache-from type=registry,ref=usuario/mi-app:cache \
--cache-to type=registry,ref=usuario/mi-app:cache,mode=max \
--push -t usuario/mi-app:latest .
# Caché local (compartir entre builds locales)
docker buildx build \
--cache-from type=local,src=.buildx-cache \
--cache-to type=local,dest=.buildx-cache,mode=max \
-t usuario/mi-app:latest --load .
Cada tipo de caché tiene su caso de uso:
- inline — La caché se guarda dentro de la imagen en el registry. Es la más sencilla pero engorda la imagen. Válida para CI básica.
- registry — La caché vive en un tag separado en el registry. No engorda la imagen y es muy eficiente. Ideal para CI avanzada y equipos.
- local — La caché está en tu disco. Es la más rápida. Perfecta para desarrollo local.
- gha — Usa el sistema de caché de GitHub Actions. Se integra de forma natural con workflows de GitHub.
- s3 — Almacena la caché en S3 o compatible (GCS, MinIO). Útil para equipos grandes con infraestructura compartida.
Caché con –mount=type=cache
Para acelerar builds donde se usan gestores de paquetes (apt, pip, npm, go mod), puedes montar directorios de caché que persisten entre builds sin engrosar las capas de la imagen final:
FROM ubuntu:24.04
# Caché de apt
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
# Caché de pip
RUN --mount=type=cache,target=/root/.cache/pip \
pip install flask requests
COPY app.py .
CMD ["python3", "app.py"]
Ventaja: estos directorios cacheados persisten entre builds sin engrosar las capas de la imagen final. La primera build descarga todo, las siguientes usan la caché. Es como tener una despensa donde guardas los ingredientes que ya has usado.
Docker Buildx Bake
docker buildx bake es una herramienta declarativa para orquestar builds complejas. En lugar de pasar mil flags a docker buildx build, defines todo en un archivo de configuración. Puedes usar HCL (el formato de HashiCorp, el más potente), JSON o YAML.
Ejemplo básico con HCL
# docker-bake.hcl
variable "TAG" {
default = "latest"
}
variable "REGISTRY" {
default = "ghcr.io/usuario"
}
group "default" {
targets = ["app", "worker"]
}
target "app" {
context = "."
dockerfile = "Dockerfile"
platforms = ["linux/amd64", "linux/arm64"]
tags = ["${REGISTRY}/myapp:${TAG}"]
cache-from = ["type=registry,ref=${REGISTRY}/myapp:cache"]
cache-to = ["type=registry,ref=${REGISTRY}/myapp:cache,mode=max"]
}
target "worker" {
context = "./worker"
dockerfile = "worker.Dockerfile"
platforms = ["linux/amd64", "linux/arm64"]
tags = ["${REGISTRY}/myapp-worker:${TAG}"]
}
Ejecutar Bake
# Construir todo (el grupo "default")
docker buildx bake
# Construir un target específico
docker buildx bake app
# Pasar variables
docker buildx bake --set "TAG=v2.0.0"
# Con push
docker buildx bake --push
# Ver los targets disponibles
docker buildx bake --print
Matriz multi-target
Para construir múltiples combinaciones de servicio/plataforma sin repetir configuración:
variable "SERVICE" {
default = ["api", "web", "worker"]
}
target "default" {
name = "${SERVICE}"
matrix = {
SERVICE = ["api", "web", "worker"]
}
context = "./services/${SERVICE}"
tags = ["usuario/${SERVICE}:latest"]
platforms = ["linux/amd64", "linux/arm64"]
}
Esto genera 6 builds (3 servicios × 2 plataformas) con una configuración mínima. Es como tener una plantilla de receta que aplicas a varios platos cambiando solo el ingrediente principal.
CI/CD Multi-Arquitectura
Construir imágenes multi-arch localmente está bien, pero donde realmente brilla BuildX es en la integración continua. Integrarlo en tu pipeline de CI/CD te permite publicar imágenes multi-plataforma automáticamente con cada push.
GitHub Actions
name: Multi-arch build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
El action docker/setup-qemu-action registra los intérpretes QEMU automáticamente en el runner de GitHub. docker/setup-buildx-action crea y configura un builder docker-container. El resultado: tres arquitecturas construidas y publicadas desde un solo workflow.
GitLab CI
build-multiarch:
stage: build
image: docker:27-cli
services:
- docker:27-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: /certs
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
before_script:
- apk add --no-cache qemu-user-static
- docker run --privileged --rm tonistiigi/binfmt --install all
- docker buildx create --name multiarch --driver docker-container --use
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker buildx build
--platform linux/amd64,linux/arm64,linux/arm/v7
--tag $IMAGE_TAG
--tag $CI_REGISTRY_IMAGE:latest
--cache-from type=registry,ref=$CI_REGISTRY_IMAGE:cache
--cache-to type=registry,ref=$CI_REGISTRY_IMAGE:cache,mode=max
--push
.
En GitLab CI tienes que hacer un paso adicional: instalar qemu-user-static e iniciar el contenedor de tonistiigi/binfmt manualmente, porque los runners de GitLab no lo traen preinstalado. Una vez hecho, el flujo es el mismo.
Lenguajes compilados vs interpretados
No todos los lenguajes se comportan igual cuando construyes imágenes multi-arquitectura. La diferencia fundamental está entre los lenguajes compilados (que generan binarios específicos para cada plataforma) y los interpretados (que usan un intérprete que ya es multi-arch).
Go — Cross-compile nativa
Go es el rey de la multi-arquitectura. El compilador produce binarios estáticos para cualquier plataforma desde un solo build. No necesitas máquinas distintas ni emulación para compilar:
# Etapa de compilación
FROM --platform=$BUILDPLATFORM golang:1.23 AS builder
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -o /app/server .
# Etapa final
FROM alpine:3.21
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]
Variables automáticas de BuildKit: BuildKit inyecta automáticamente BUILDPLATFORM, TARGETPLATFORM, TARGETOS y TARGETARCH en cada build multi-plataforma. Las usas con ARG en el Dockerfile.
BUILDPLATFORM: la plataforma donde se ejecuta el build (ej:linux/amd64)TARGETPLATFORM: la plataforma para la que se construye (ej:linux/arm64)TARGETOS: solo el sistema operativo (ej:linux)TARGETARCH: solo la arquitectura (ej:arm64)
Python — Intérprete nativo multi-plataforma
Python es interpretado, así que el mismo bytecode funciona en todas las arquitecturas. La clave está en elegir una imagen base multi-arch y tener cuidado con bibliotecas nativas (C extensions):
# Multi-stage: primero determinamos la plataforma
FROM --platform=$BUILDPLATFORM python:3.12-slim AS builder
ARG TARGETPLATFORM
RUN echo "Construyendo para $TARGETPLATFORM"
# Instalamos dependencias en un directorio temporal
RUN pip install --user --no-cache-dir numpy pandas flask
# Etapa final
FROM python:3.12-slim
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY app.py .
CMD ["python3", "app.py"]
Atención: bibliotecas como numpy, pandas, psycopg2 compilan código C nativo durante la instalación. Asegúrate de que la imagen base tenga las herramientas de compilación o usa wheels precompilados para cada arquitectura. PyPI ya distribuye wheels para amd64 y arm64 en la mayoría de los paquetes populares, así que normalmente funciona sin problemas.
Node.js — Sin complicaciones
Node.js también funciona de maravilla con multi-arquitectura. Los binarios nativos (V8, libuv) vienen ya compilados para cada plataforma en la imagen oficial:
FROM --platform=$BUILDPLATFORM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Testing y depuración local
No necesitas tener una Raspberry Pi en tu escritorio para probar que tu imagen multi-arch funciona. Con BuildX y QEMU puedes verificarlo todo desde tu máquina local.
Verificar que una imagen soporta múltiples arquitecturas
# Inspeccionar manifest list
docker buildx imagetools inspect usuario/mi-app:latest
# Salida esperada:
# Name: usuario/mi-app:latest
# MediaType: application/vnd.oci.image.index.v1+json
# Manifest:
# * linux/amd64
# * linux/arm64
# * linux/arm/v7
Probar una imagen ARM64 en x86_64
# Forzar ejecución con emulación QEMU
docker run --platform linux/arm64 --rm usuario/mi-app:latest uname -m
# Debe devolver: aarch64
Probar con –pull y verificación de imágenes
# Descargar la imagen exacta de una plataforma
docker pull --platform linux/arm64 usuario/mi-app:latest
docker inspect usuario/mi-app:latest | jq '.[0].Architecture'
Depuración de fallos en builds multi-arch
# Construir solo una plataforma para identificar errores
docker buildx build --platform linux/arm64 --load -t test-arm64 .
docker run --platform linux/arm64 --rm test-arm64
# Construir con salida verbose
docker buildx build --platform linux/amd64,linux/arm64 --no-cache --progress=plain .
# Ver logs del builder
docker logs buildx_buildkit_multiarch0
Errores comunes y soluciones
La experiencia con BuildX te irá dando soltura, pero estos son los errores más frecuentes con los que te toparás y cómo resolverlos.
Error: «multiple platforms feature is not supported»
docker: 'buildx build' requires a builder with support for multiple platforms.
Solución: el builder activo usa el driver docker (nativo), que no soporta multi-arch. Crea un builder con driver docker-container.
docker buildx create --name multiarch --driver docker-container --bootstrap
docker buildx use multiarch
Error: «exec format error» al ejecutar contenedor ARM en x86
exec /bin/sh: exec format error
Solución: QEMU binfmt no está instalado. Instala los intérpretes:
docker run --privileged --rm tonistiigi/binfmt --install all
Error: «no matching manifest for unknown in the manifest list entries»
no matching manifest for linux/amd64 in the manifest list entries
Solución: la imagen existe pero no tiene un manifest para la plataforma solicitada. Verifica las plataformas disponibles:
docker buildx imagetools inspect <imagen>
O construye la imagen incluyendo esa plataforma.
Error: «failed to solve: process … did not complete successfully» en build multi-arch
#0 ERROR: process "/bin/sh -c apt-get update" did not complete successfully: exit code: 137
Solución: exit code 137 = SIGKILL por falta de memoria. QEMU emulación consume más RAM. Aumenta la memoria disponible para el contenedor BuildKit o limita los procesos paralelos:
# Limitar paralelismo
docker buildx build --build-arg BUILDKIT_STEP_NUM=1 ...
# O configurar el builder con límites
docker buildx create --name multiarch --driver docker-container \
--config buildkitd.toml --bootstrap
Error: «docker buildx build –load solo funciona con una plataforma»
error: multiple platforms currently not supported for docker driver
Solución: --load solo carga una plataforma en el almacén local. Para testing, construye para una plataforma con --load:
# Solo una plataforma con --load
docker buildx build --platform linux/amd64 --load -t test:latest .
# Varias plataformas requieren --push
docker buildx build --platform linux/amd64,linux/arm64 --push -t reg/test:latest .
Error: «docker: ‘buildx’ is not a docker command»
docker: 'buildx' is not a docker command. See 'docker --help'
Solución: BuildX no está instalado o no está en el PATH. Instálalo como plugin:
# Verificar versión de Docker (necesitas 19.03+)
docker --version
# Instalar buildx
mkdir -p ~/.docker/cli-plugins
curl -sSL "https://github.com/docker/buildx/releases/latest/download/buildx-$(uname -s)-$(uname -m)" -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
docker buildx version
Error: «tls: failed to verify certificate» con registry local
error: failed to solve: error: tls: failed to verify certificate: x509: certificate signed by unknown authority
Solución: el registry usa HTTPS con certificado autofirmado. Configura Docker para confiar en el CA o usa HTTP (solo desarrollo):
# Para registry local sin TLS (desarrollo)
export DOCKER_CONTENT_TRUST=0
docker buildx build --push --platform linux/amd64,linux/arm64 \
--tag localhost:5000/mi-app:latest .
O añade insecure-registries en /etc/docker/daemon.json.
Error: «cache import failed» al usar –cache-from type=registry
cache import failed: failed to get cache from <registry>: manifest unknown
Solución: el cache ref no existe aún (primera build). Usa --cache-from condicional o ignora el error. BuildX continuará sin caché de importación:
# Primera build: solo cache-to
docker buildx build \
--cache-to type=registry,ref=usuario/mi-app:cache,mode=max \
--push -t usuario/mi-app:latest .
# Siguientes builds: cache-from + cache-to
docker buildx build \
--cache-from type=registry,ref=usuario/mi-app:cache \
--cache-to type=registry,ref=usuario/mi-app:cache,mode=max \
--push -t usuario/mi-app:latest .
Error: «context deadline exceeded» en builds lentos
error: failed to solve: context deadline exceeded
Solución: el builder tiene un timeout por defecto. Aumenta el timeout o construye por etapas:
# Aumentar timeout (valor en segundos)
export BUILDKIT_PROGRESS=plain
docker buildx build --platform linux/amd64,linux/arm64 \
--build-arg BUILDKIT_STEP_TIMEOUT=600 \
--push -t usuario/mi-app:latest .
Error: «unsupported architecture» al instalar paquetes APT en ARM
Package 'openjdk-17-jre-headless' has no installation candidate
Solución: no todos los paquetes están disponibles en todas las arquitecturas. Verifica la disponibilidad y, si es necesario, usa imágenes base específicas para esa plataforma:
# Verificar paquetes disponibles para arm64
docker run --platform linux/arm64 --rm ubuntu:24.04 bash -c "apt-get update && apt-cache search openjdk"
Verificación
Antes de dar el capítulo por cerrado, aquí tienes una serie de comprobaciones para verificar que todo funciona correctamente.
Verificar instalación de BuildX
# Comprobar que BuildX está instalado y su versión
docker buildx version
# Deberías ver algo como:
# github.com/docker/buildx v0.20.0 123abc
Verificar que el builder multi-arch soporta varias plataformas
# Crear un builder de prueba y comprobar sus plataformas
docker buildx create --name verify-builder --driver docker-container --bootstrap
docker buildx inspect verify-builder --bootstrap | grep -i platforms
# La salida debería incluir linux/amd64, linux/arm64, linux/arm/v7
# Si falta alguna, instala QEMU binfmt
# Limpiar el builder de prueba
docker buildx rm verify-builder
Verificar que QEMU binfmt está operativo
# Ejecutar un contenedor ARM64 en una máquina x86_64
docker run --platform linux/arm64 --rm alpine:3.21 uname -m
# Debe devolver: aarch64
# Si devuelve "exec format error", QEMU no está instalado
Verificar que puedes construir una imagen multi-arch
# Crear un Dockerfile mínimo
mkdir -p /tmp/test-multiarch && cd /tmp/test-multiarch
cat > Dockerfile << 'EOF'
FROM alpine:3.21
CMD echo "Arquitectura: $(uname -m)"
EOF
# Construir para amd64 y cargar en local
docker buildx build --platform linux/amd64 --load -t test-multiarch:latest .
# Ejecutar y comprobar
docker run --rm test-multiarch:latest
# Debe mostrar: Arquitectura: x86_64
# Limpiar
rm -rf /tmp/test-multiarch
Verificar un manifest list remoto
# Inspeccionar una imagen oficial conocida por ser multi-arch
docker buildx imagetools inspect alpine:3.21
# Deberías ver múltiples plataformas listadas:
# * linux/amd64
# * linux/arm64
# * linux/arm/v7
# * linux/arm/v6
# * linux/386
# * linux/ppc64le
# * linux/s390x
Conclusión
Este capítulo ha sido una inmersión completa en Docker BuildX y la construcción de imágenes multi-arquitectura. Es una habilidad esencial en el ecosistema actual donde ARM64 domina el edge computing, los portátiles Apple Silicon y cada vez más servidores cloud.
Has aprendido que el problema de las múltiples arquitecturas nace de la diversidad de procesadores (x86_64, ARM64, ARMv7) y que Docker lo resuelve mediante manifest lists (OCI image index) que asocian un mismo tag a imágenes específicas por plataforma.
Dominaste la instalación y configuración de BuildX: el plugin docker buildx, la creación de builders con distintos drivers (docker, docker-container, remote), y la gestión de múltiples builders con docker buildx create/ls/use.
Exploraste QEMU y binfmt_misc, el mecanismo que permite al kernel Linux ejecutar binarios de otras arquitecturas mediante emulación transparente. Aprendiste a instalarlo con tonistiigi/binfmt y conociste sus limitaciones de rendimiento.
Construiste tus primeras imágenes multi-plataforma con --platform, entendiendo la diferencia entre --load (una plataforma, almacén local) y --push (múltiples plataformas, manifest list remoto). Dominaste los tipos de salida con --output.
Implementaste caché avanzada con --mount=type=cache para acelerar gestores de paquetes y con type=registry para compartir caché entre máquinas CI. Viste la diferencia entre mode=min y mode=max en la exportación de caché.
Descubriste Docker Buildx Bake, la herramienta declarativa para orquestar builds complejas con HCL/JSON/YAML, targets múltiples, matrices y variables. Aprendiste que Bake es ideal para equipos que necesitan construir múltiples servicios para múltiples plataformas con una configuración DRY.
Integraste BuildX en CI/CD: GitHub Actions con docker/setup-buildx-action y GitLab CI con el patrón docker-in-docker + QEMU. Viste cómo el cache GHA y el cache registry reducen drásticamente los tiempos de build.
Analizaste el comportamiento de diferentes lenguajes: Go con cross-compilation nativa usando TARGETOS/TARGETARCH, Python con intérpretes nativos multi-arch pero cuidado con C extensions, y Node.js con su naturaleza híbrida.
Finalmente, practicaste testing y depuración: inspección de manifest lists con docker buildx imagetools inspect, ejecución forzada en plataformas no nativas con --platform, y diagnóstico de fallos con --progress=plain y logs del builder.
El script de gestión buildx_manager.sh integra todas estas capacidades en una herramienta reutilizable que puedes adaptar a tu flujo de trabajo diario.
| Concepto | Comando | Descripción |
|---|---|---|
| Versión BuildX | docker buildx version | Verifica instalación |
| Listar builders | docker buildx ls | Muestra builders y plataformas |
| Crear builder | docker buildx create --name m --driver docker-container | Crea builder multi-arch |
| Usar builder | docker buildx use m | Activa un builder |
| Build multi-arch | docker buildx build --platform linux/amd64,linux/arm64 | Construye para varias plataformas |
| Construir y subir | docker buildx build --push -t reg/img . | Build + push con manifest list |
| Cargar local | docker buildx build --load -t img . | Solo 1 plataforma en local |
| Inspeccionar manifest | docker buildx imagetools inspect img | Ver plataformas de una imagen |
| Bake | docker buildx bake | Build declarativo multi-target |
| Caché registry | --cache-to type=registry,ref=img:cache | Caché distribuida |
| Caché local | --mount=type=cache,target=/root/.cache | Caché de paquetes |
| Instalar QEMU | docker run --privileged --rm tonistiigi/binfmt --install all | Emulación multi-arch |
Con estas herramientas y conocimientos, ya puedes construir imágenes Docker que funcionen en cualquier plataforma, desde un Raspberry Pi Zero hasta un servidor ARM64 en AWS Graviton, pasando por tu portátil x86_64 de desarrollo. La fragmentación de arquitecturas deja de ser un problema: un solo comando, un solo tag, todas las plataformas.
En el próximo capítulo veremos cómo usar Docker en entornos de desarrollo de forma eficiente, con volúmenes en caliente, recarga automática y depuración en contenedores. Dale caña.
Más información,
- Multi-platform builds (Docker Docs) — Guía oficial sobre construcción multi-plataforma
- Docker BuildX (Documentación) — Referencia completa del comando
docker buildx - Docker BuildX GitHub — Código fuente y releases
- Docker BuildKit — Motor de construcción subyacente
- Docker Buildx Bake — Orquestación declarativa de builds