Docker Compose en Apple container: qué se rompió
Si tu contenedor corre perfecto en la laptop y muere apenas toca el cluster con exec /app/server: exec format error, casi seguro compilaste el binario para una arquitectura de CPU distinta a la del servidor. El caso típico: construís en una Mac con Apple Silicon (ARM64) y desplegás en nodos x86_64. El kernel del server no reconoce el formato del binario y lo rechaza. La solución de fondo es armar imágenes multiarch con docker buildx.
Pasó de verdad, y hay un relato reciente que lo cuenta bien. Un desarrollador publicó en dev.to “It Works on My Machine: A Docker War Story About exec format error”: armó la imagen en una laptop nueva Apple Silicon, corrió bárbaro en local, la pusheó, y cada pod del cluster entró en crash loop con esas cuatro palabras y nada más. Sin stack trace, sin panic, sin pista.
El error exec format error docker es un rechazo del kernel de Linux (código ENOEXEC): el sistema intentó ejecutar un archivo y no reconoció su formato binario. No es un problema de Docker ni de permisos. Es que el binario dentro de la imagen fue compilado para una arquitectura de CPU (ARM64) y el host donde corre es de otra (x86_64), y esos dos conjuntos de instrucciones no son intercambiables.
En 30 segundos
- Qué es: el kernel rechaza el binario (ENOEXEC) porque su arquitectura no coincide con la del host. No es un bug de Docker.
- Causa #1: compilás en Apple Silicon (ARM64) y corrés en servidores x86_64. Docker empaqueta binarios, no virtualiza la CPU.
- Cómo lo detectás:
docker inspectte muestra la arquitectura de la imagen; el warning “platform does not match” aparece al correr. - Fix rápido (dev):
--platform linux/amd64o activar Rosetta 2 en Docker Desktop. - Fix de fondo (prod): imagen multiarch con
docker buildx build --platform linux/amd64,linux/arm64 --push.
¿Por qué el mismo contenedor corre en un lado y no en otro?
La promesa de los contenedores era matar el “en mi máquina funciona”. Misma imagen, mismo comportamiento en todos lados. Por eso duele tanto cuando pasa esto.
Acá viene el malentendido de fondo: Docker no virtualiza el procesador. Empaqueta un binario ya compilado para un conjunto de instrucciones específico. Cuando escribís tu app en Go, Node o lo que sea, el compilador produce código máquina que habla el idioma de una CPU concreta. Si ese idioma es ARM64 y el server habla x86_64, el kernel mira el archivo y dice, con razón, “no sé cómo ejecutar esta forma de binario”. Sobre eso hablamos en cuando ejecutas Docker en tu pipeline de CI/CD.
En el caso del relato pasó desapercibido por un motivo simple: la laptop era Apple Silicon y el Docker daemon local también corría en ARM64. O sea, en local el binario ARM64 estaba ejecutándose sobre una CPU ARM64. Nativo. Todo funcionaba porque estaba corriendo justo en la arquitectura para la que lo habían compilado.
¿Cuál es la diferencia entre ARM64 y x86_64 en contenedores?
Son dos familias de arquitectura de CPU con conjuntos de instrucciones distintos. x86_64 (también llamada amd64) es la que usan la enorme mayoría de servidores Intel y AMD, y las PC de escritorio de siempre. ARM64 (aarch64) es la de los chips Apple Silicon (M1 en adelante), los Raspberry Pi modernos y una parte creciente de instancias cloud.
Un binario compilado para una no arranca en la otra sin traducción. Es como pasarle un texto en un idioma que la CPU no lee.
| Arquitectura | Alias en Docker | Dónde la encontrás | Ejecuta binario x86_64 nativo |
|---|---|---|---|
| x86_64 | linux/amd64 | Servidores Intel/AMD, PCs, mayoría de nodos Linux en cloud | Sí |
| ARM64 | linux/arm64 | Apple Silicon (M1/M2/M3/M4), Raspberry Pi 4/5, instancias ARM cloud | No (necesita emulación) |
| x86 (32 bits) | linux/386 | Hardware legacy, algunos IoT | Parcial |

La historia real: compilar en Apple Silicon y desplegar en x86_64
Reconstruyamos el flujo del caso de dev.to, porque es el patrón que se repite en casi todos estos incidentes. Relacionado: alternativas a Docker Desktop para Apple Silicon.
- Build local: el dev arma la imagen en su MacBook con chip Apple Silicon. El compilador produce un binario ARM64.
- Prueba local: corre
docker run, funciona perfecto. Lógico: CPU ARM64 ejecutando binario ARM64. - Push: sube la imagen al registry sin declarar plataforma. Queda como imagen single-arch ARM64.
- Deploy: el cluster tiene nodos x86_64 (lo habitual en cloud). Cada pod baja la imagen ARM64.
- Crash: el kernel x86_64 mira el binario ARM64 y devuelve
exec format error. Crash loop.
El detalle traicionero: nadie lo vio venir porque toda la validación previa ocurrió en la misma arquitectura del build. Subís el modelo, lo probás en local, funciona, lo mandás a producción y de repente todo se rompe porque la máquina destino habla otro idioma de CPU, el pipeline no lo avisó y el log solo escupe cuatro palabras sin contexto.
¿Cómo diagnosticar si tu contenedor tiene la arquitectura incompatible?
Antes de tocar nada, confirmá que el problema es de arquitectura y no otra cosa. Tres comandos alcanzan.
- Ver la arquitectura de la imagen:
docker inspect --format '{.Architecture}' tu-imagen. Si dicearm64y tu server es amd64, ahí está. - Ver la CPU del host:
uname -mdentro del nodo. Devuelvex86_64oaarch64. - El warning que te grita la respuesta: al correr una imagen de plataforma distinta, Docker imprime
WARNING: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64). Ojo con ese mensaje: es literal el diagnóstico.
Si el Architecture de la imagen no coincide con lo que devuelve uname -m en el host, ya sabés qué pasa. No sigas buscando en el código de la app.
Soluciones rápidas: platform, Rosetta 2 y emulación
Para desarrollo hay atajos. Para producción, no tanto. Distingamos.
Forzar la plataforma al correr
Corré docker run --platform linux/amd64 tu-imagen para que Docker emule x86_64 sobre tu ARM. Sirve para probar rápido una imagen amd64 en tu Mac. También podés setear DOCKER_DEFAULT_PLATFORM=linux/amd64 como variable de entorno para que aplique a todos los builds y runs. Cubrimos ese tema en detalle en integrando Docker con tu servidor de CI.
Activar Rosetta 2 en Docker Desktop
Si estás en Mac con Apple Silicon, en Docker Desktop podés activar “Use Rosetta for x86/amd64 emulation”. Rosetta 2 es la capa de traducción de Apple y es bastante más rápida que la emulación genérica. ¿Sirve para producción? No. Es un parche para tu entorno local mientras armás la imagen buena.
Rosetta 2 vs QEMU para emular x86 en ARM
Se confunden todo el tiempo y no son lo mismo. QEMU es un emulador genérico y multiplataforma que Docker usa por debajo (vía binfmt) para correr binarios de otra arquitectura en cualquier sistema. Rosetta 2 es propietario de Apple, solo existe en Apple Silicon, y traduce x86_64 a ARM64 con mucho menos overhead.
| Criterio | Rosetta 2 | QEMU |
|---|---|---|
| Plataforma | Solo macOS Apple Silicon | Multiplataforma (Linux, Mac, Windows) |
| Qué es | Traductor nativo de Apple | Emulador genérico |
| Performance relativa | Notablemente más ágil para compilar y correr | Más lento, sobre todo en cargas con dependencias nativas |
| Uso recomendado | Dev local en Mac | Builds multiarch en CI, entornos no-Mac |
La regla práctica: en tu Mac, Rosetta para el día a día. En el pipeline de CI (que suele correr en Linux x86_64), QEMU + buildx para emitir las dos arquitecturas.
¿Cómo crear una imagen Docker multiarch que funcione en ARM64 y x86_64?
Esta es la solución de fondo. Con docker buildx generás una sola referencia de imagen que contiene binarios para varias arquitecturas, y el runtime baja el que corresponde a cada host. Esto se conecta con lo que analizamos en ejecutar servicios sin depender de APIs externas.
El comando base es docker buildx build --platform linux/amd64,linux/arm64 -t tu-registry/app:tag --push .. Fijate el --push: es clave y acá se traba mucha gente.
- Con
--push: buildx arma las dos arquitecturas y sube el manifiesto multiarch al registry. Funciona. - Sin
--push: buildx no puede cargar una imagen multiarch al store local de Docker (que es single-arch) y tira error. Para pruebas locales de una sola arch usá--load, pero solo funciona con una plataforma a la vez. - Compilación condicional: en el Dockerfile usá el ARG automático
TARGETARCHpara bajar el binario correcto según la arquitectura destino. Ejemplo:RUN curl -o app https://tu-cdn/app-$TARGETARCH.
Si desplegás esto en tu propio VPS o servidor cloud, verificá primero la arquitectura del host. En donweb.com podés levantar el entorno y correr uname -m antes de definir a qué plataforma le vas a compilar, así evitás el crash loop de entrada.
Para profundizar en esto, mirá nuestro artículo sobre Problemas reales Compose.
Esto se conecta directamente con nuestro artículo sobre container runtime de Apple.
Si querés profundizar en esto, tenemos un artículo sobre virtualización ligera en Apple Silicon.
Errores comunes que agravan el problema
- Creer que Docker virtualiza la CPU. No lo hace: empaqueta binarios de una arquitectura. Síntoma: asumís portabilidad total y no declarás plataforma. Solución: siempre pensá para qué arch estás compilando.
- Asumir que Rosetta actúa solo. En Docker Desktop hay que activarlo, y aun activado no resuelve producción. Solución: usalo para dev y armá multiarch para deploy.
- Scripts .sh con line endings CRLF. Si editás en Windows, el
#!/bin/sh\rpuede dar errores raros de ejecución que confundís con arquitectura. Solución: forzá LF con.gitattributesodos2unix. - npm install que compila dependencias nativas para el host. Paquetes con binarios nativos (node-gyp, sharp) se compilan para la arch del build. Solución: compilá dentro de un contenedor de la arch destino o usá buildx con la plataforma correcta.
- Usar una imagen base que no declara multiarch. Si tu
FROMapunta a una imagen single-arch, tu build hereda esa limitación. Solución: verificá condocker manifest inspectque la base tenga las dos plataformas.
Preguntas Frecuentes
¿Qué significa exec format error en Docker?
Es un error del kernel de Linux (código ENOEXEC) que aparece cuando el sistema intenta ejecutar un binario cuya arquitectura no reconoce. En Docker suele significar que la imagen fue compilada para ARM64 y el host es x86_64, o viceversa. No es un fallo de Docker sino una incompatibilidad de arquitectura de CPU.
¿Por qué mi imagen Docker falla en producción pero funciona local?
Porque tu máquina local y el servidor de producción tienen arquitecturas de CPU distintas. Si construís en una Mac Apple Silicon (ARM64) y desplegás en nodos x86_64, el binario ARM64 corre nativo en tu laptop pero el kernel del server no puede ejecutarlo. La solución es armar una imagen multiarch con docker buildx.
¿Cómo compilo en Mac M1 una imagen que corra en servidores x86?
Usá docker buildx build --platform linux/amd64 -t tu-imagen --push . para forzar la compilación a x86_64, o --platform linux/amd64,linux/arm64 para generar ambas. También conviene activar Rosetta 2 en Docker Desktop para acelerar los builds x86 en tu Apple Silicon.
¿Cuál es la diferencia entre ARM64 y x86_64 en Docker?
Son dos conjuntos de instrucciones de CPU incompatibles entre sí. x86_64 (amd64) es la de la mayoría de servidores Intel y AMD; ARM64 (aarch64) es la de Apple Silicon, Raspberry Pi y varias instancias cloud modernas. Un binario compilado para una no se ejecuta en la otra sin emulación.
¿Cómo uso docker buildx para crear imágenes multiarch?
El comando es docker buildx build --platform linux/amd64,linux/arm64 -t tu-registry/app:tag --push .. El flag --push es obligatorio para multiarch, porque el store local de Docker no puede guardar una imagen con varias arquitecturas. Sin --push, el build falla.
Conclusión
El exec format error no es un bug misterioso: es el kernel avisándote que compilaste para una arquitectura y estás corriendo en otra. El auge de Apple Silicon volvió esto un incidente cotidiano, porque cada vez más devs construyen en ARM64 y despliegan en clusters x86_64 sin darse cuenta.
Qué hacer, en concreto: para dev, activá Rosetta 2 y usá --platform cuando pruebes. Para producción, dejá de pushear imágenes single-arch y pasá tus pipelines a docker buildx con --platform linux/amd64,linux/arm64 --push. Y antes de deployar, corré uname -m en el host destino. Ese chequeo de treinta segundos te ahorra la tarde entera que le costó al autor del relato de dev.to.
¿Cómo configuro Docker Compose en Apple Silicon sin que se rompa en el server?
Usá docker-compose con plataformas multiarch desde el principio. En el Dockerfile, especificá ARG TARGETARCH para bajar el binario correcto según la arquitectura. Al hacer build, forzá –platform linux/amd64,linux/arm64 con buildx y –push al registry. Así la misma imagen funciona en Apple (ARM64) y servidores Linux (x86_64) sin crashes.
¿Qué debería declarar en mi docker-compose.yml para Apple Silicon?
En local para desarrollo, agregá platform: ‘linux/amd64’ o ‘linux/arm64’ en cada servicio si necesitás forzar una arquitectura. Pero la solución real es armar imágenes multiarch en build time, no en compose time. Compose tira warning si detecta incompatibilidad, pero es el Dockerfile el que decide si funciona o explota.
¿Es Rosetta 2 suficiente para producción con Docker Compose?
No. Rosetta 2 es solo para desarrollo local en Mac. En producción, tu cluster en Linux x86_64 no tiene Rosetta. Necesitás imágenes multiarch reales con docker buildx. Rosetta acelera tests y compilaciones en tu MacBook, pero no reemplaza la arquitectura correcta en el server.
¿Docker Compose funciona en Mac con Apple Silicon?
Sí, Docker Compose funciona nativamente en Mac Apple Silicon. El problema no es Compose sino la arquitectura del binario adentro de la imagen. Si compilás en ARM64 (tu Mac) y despliegas en x86_64 (servidor), falla con ‘exec format error’. Soluciona compilando imágenes multiarch con docker buildx.
¿Cómo hago que mi docker-compose.yml funcione igual en Mac y en servidor Linux?
El archivo docker-compose.yml es agnóstico de arquitectura. Lo que importa es que tu imagen Docker sea multiarch. Compilá con `docker buildx build –platform linux/amd64,linux/arm64 –push` y el mismo docker-compose.yml funcionará en cualquier arquitectura sin cambios.
¿Puedo probar una imagen x86_64 en mi Mac antes de desplegar?
Sí. Usá `docker buildx build –platform linux/amd64 -t app:test –load .` para cargar la versión x86_64 en local con emulación QEMU o Rosetta 2. Así verificás que funciona antes de enviar a producción.
¿Cómo usar Docker Compose con Apple Silicon?
En Mac con chip M1/M2/M3, instalá Docker Desktop para Apple Silicon. Usá docker-compose como siempre; los contenedores ARM64 corren nativos. Si necesitás imágenes x86_64, activá Rosetta 2 en Docker Desktop o añadí platform: linux/amd64 en tu docker-compose.yml.
¿Qué hacer si un contenedor de Docker Compose no funciona en Mac M1?
El error más común es exec format error. Solucionálo forzando la plataforma con platform: linux/amd64 en el docker-compose.yml o construyendo imágenes multiarch con docker buildx. Verificá la arquitectura de la imagen con docker inspect.
¿Cómo forzar la arquitectura en Docker Compose para Apple Silicon?
Agregá platform: linux/amd64 debajo del servicio en tu docker-compose.yml. Por ejemplo: services: app: platform: linux/amd64. Esto obliga a emular x86_64 en tu Mac usando Rosetta 2 o QEMU.
¿Docker Compose funciona en Apple Silicon?
Sí, pero la imagen que generás tiene la arquitectura del build. Si compilás en Apple Silicon (ARM64), Docker empaqueta un binario ARM64 que falla en servidores x86_64 con ‘exec format error’. La solución es armar imágenes multiarch con docker buildx para ambas arquitecturas.
¿Por qué falla mi contenedor cuando lo despliego en el servidor?
Probablemente compilaste en Apple Silicon (ARM64) y desplegás en un servidor x86_64. Docker empaqueta binarios compilados para una arquitectura específica, no virtualiza la CPU. Verificá con docker inspect y uname -m cuál es la arquitectura de tu imagen y tu host.
¿Cómo creo una imagen que funcione en Apple y en servidores Linux?
Usá docker buildx build –platform linux/amd64,linux/arm64 -t tu-registry/app:tag –push . El –push es obligatorio para subir el manifiesto multiarch. Esto genera una referencia con binarios para ambas arquitecturas, y cada host baja el suyo automáticamente.
¿Cómo sé si mi contenedor Docker se compiló en Apple Silicon?
Revisá la arquitectura de la imagen con: docker inspect –format ‘{.Architecture}’ tu-imagen. Si dice arm64, fue compilada en Apple Silicon. Si tu servidor usa x86_64, ahí tenés el problema.
¿Qué hago si tengo un Docker Compose con Apple Silicon y quiero que funcione en producción x86_64?
En tu docker-compose.yml agregá platform: linux/amd64 en cada servicio que necesites. Pero la solución definitiva es generar una imagen multiarch con docker buildx –platform linux/amd64,linux/arm64 y pushearla al registry.
¿Rosetta 2 sirve para producción con Docker Compose en servidores Linux?
No, Rosetta 2 solo funciona en macOS Apple Silicon. Para producción en servidores Linux usá compilación multiarch con buildx o forzá la plataforma con –platform linux/amd64 al hacer build.
¿Qué es docker buildx y por qué lo necesito en Apple Silicon?
docker buildx es una herramienta que te permite compilar imágenes para múltiples arquitecturas en un solo build. En Apple Silicon, te deja generar una imagen que funciona tanto en Mac (arm64) como en servidores x86_64 con el comando `docker buildx build –platform linux/amd64,linux/arm64 –push`.
¿Puedo usar docker-compose con arquitecturas mixtas?
Sí, pero solo si las imágenes que especificás en tu docker-compose.yml son multiarch. Docker Compose en sí no elige arquitectura; eso lo maneja el runtime. Si usás una imagen single-arch (solo arm64), va a fallar en x86_64 sin importar el compose.
¿Cuál es el flujo correcto: compilar en Mac para x86_64 o hacer multiarch?
Lo ideal es multiarch: compilás una sola vez para ambas arquitecturas y después cualquier máquina baja la versión correcta. Pero si solo necesitás x86_64, podés compilar con `docker buildx build –platform linux/amd64` y subirlo directo al registry. El riesgo de no especificar plataforma es terminar con imágenes arm64 huérfanas.
¿Cómo especifico la arquitectura en docker compose?
En tu docker-compose.yml podés agregar `platform: linux/amd64` al servicio, o setear `DOCKER_DEFAULT_PLATFORM=linux/amd64` como variable de entorno. Esto emula x86_64 en tu Mac para pruebas, aunque sea más lento que ejecutar nativo.
¿Debo usar docker buildx si estoy usando docker compose?
`docker buildx` se usa en CI/CD para construir imágenes multiarch ANTES de que compose las descargue. Si desplegás compose en producción, la imagen ya debe estar compilada en multiarch en el registry. Para desarrollo en tu Mac, con `–platform` o Rosetta te alcanza.
¿Por qué docker compose tira exec format error en producción si funciona en mi Mac?
Docker compose solo orquesta contenedores, no define su arquitectura. Si compilaste la imagen en ARM64 en tu Mac y la desplegás en servidores x86_64, el kernel rechaza el binario. La solución es compilar con `docker buildx –platform linux/amd64,linux/arm64` para crear una imagen multiarch.
¿Cómo fuerzo la plataforma linux/amd64 en docker-compose en una Mac con Apple Silicon?
Agregá platform: linux/amd64 dentro del servicio correspondiente en tu docker-compose.yml y volvé a levantar con docker compose up. Así Compose baja y ejecuta la imagen para x86_64 aunque tu Mac sea ARM64, usando emulación vía Rosetta o QEMU. Es la forma más directa de reproducir en local lo que después va a pasar en tus servidores.
¿La herramienta container de Apple soporta docker-compose?
Hasta ahora, la herramienta container de Apple corre contenedores Linux individuales sobre su propio runtime y no incluye un orquestador equivalente a Docker Compose. Si tu flujo depende de un archivo compose, el camino sigue siendo Docker Compose u otro runtime compatible, fijando la plataforma por servicio. Como el proyecto evoluciona rápido, conviene revisar el repo oficial antes de planear una migración.
¿Puedo construir una imagen multiarch directamente desde docker-compose?
Sí: en la sección build del servicio podés declarar platforms: [linux/amd64, linux/arm64] y Compose delega ese build multiarch a buildx. También podés usar el comando directo docker buildx build –platform … –push si preferís control fino. Lo importante es publicar el manifiesto multiarch al registry, porque una imagen cargada solo en local queda limitada a una sola arquitectura.
Fuentes
- dev.to – It Works on My Machine: A Docker War Story About exec format error (relato original)
- OneUptime – Cómo arreglar exec format error en imágenes multiplataforma
- Docker Forums – exec format error tras upgrade en Apple Silicon M1
- GeeksforGeeks – Docker exec /usr/bin/sh: exec format error
- Medium – Solucionar exec format error al desplegar en Amazon ECS






