Docker Compose no es el problema. El problema son tus suposiciones de local a producción

El artículo explica por qué docker compose es una herramienta excelente para desarrollo local, pero un mal sustituto de un manifiesto real de producción, y qué hacer para cerrar esa brecha antes de que cause un incidente.

Desarrollador de software trabajando
28 jul 20269 min de lectura
Actualizado el 28 jul 2026

Corres docker compose up, todo levanta en doce segundos, y la feature que llevas toda la semana construyendo por fin habla con Postgres, Redis y ese mock de API interna sin un solo paso manual. Después la subes a producción. Tres días más tarde estás en una llamada a las 11 de la noche porque el servicio que funcionaba perfecto en tu laptop está tirando timeouts de conexión en producción, y nadie puede explicar por qué eso que "sin dudas funciona" no funciona.

Esto no es un problema de Docker Compose. Compose hizo exactamente lo que fue diseñado para hacer. El problema es que en algún punto entre tu laptop y el clúster, el equipo empezó a tratar compose.yml como si fuera una especificación de producción, en lugar de lo que realmente es: una herramienta buenísima para desarrollo local.

Lo que Docker Compose realmente hace bien

Antes de meternos en dónde se tuerce esto, vale la pena reconocer qué tan bien hace Compose su trabajo real. Levantar un entorno con varios servicios solía significar un README con quince pasos, la mitad desactualizados. Ahora es un archivo y un comando. Alguien nuevo entra al equipo, clona el repo, corre docker compose up, y para la hora del almuerzo ya tiene todo el stack corriendo en local, igual que el resto del equipo.

Esa consistencia tiene un valor real. Compose mata el clásico "en mi máquina funciona" a nivel individual. Cada persona del equipo tiene la misma versión de Postgres, las mismas variables de entorno, la misma topología de servicios, sin que nadie configure nada a mano. Para iterar en local, para el onboarding, para correr tests de integración contra dependencias reales en vez de mocks, es realmente una de las mejores herramientas del ecosistema. Nada de lo que sigue es un argumento en contra de usarlo así.

Dónde compose.yml se convierte, en silencio, en un contrato de producción

El problema arranca con una idea que suena de lo más razonable: "ya tenemos todos los servicios definidos acá, ¿por qué no usar este mismo archivo para el deploy?" Alguien agrega un par de overrides de variables de entorno, lo apunta a un VPS o a un solo host de Docker, y funciona. Sigue funcionando por un tiempo, así que se queda.

Lo que en realidad pasó es que un archivo diseñado para responder "¿cómo corro esto en mi laptop?" terminó respondiendo "¿cómo corremos esto frente a los clientes?" sin que nadie lo haya decidido a propósito. Nadie se sentó a decir "saltemos la orquestación como corresponde". Se fue acumulando, un deploy a la vez, hasta que el archivo de docker compose era lo más parecido a una especificación de infraestructura que tenía el equipo, y para ese momento nadie quería ser quien dijera que no estaba pensado para eso.

Ese es el verdadero modo de falla. No que se haya usado Compose, sino que se usó más allá del límite para el que fue diseñado, y el equipo no notó que había cruzado ese límite hasta que algo se rompió.

La brecha entre "funciona" y "está realmente en producción"

Acá es donde aparece la brecha, en concreto, porque casi nunca es el código. Es todo lo que Compose abstrae para darte una experiencia local fluida.

La red es lo primero. Compose le da a cada servicio una red plana donde cualquiera puede hablarle a cualquiera por nombre de servicio, sin preguntas. La producción, si de verdad es producción, tiene políticas de red, security groups, service meshes, o como mínimo un load balancer entre cosas que nunca debieron ser directamente alcanzables. El servicio que resolvía api:8080 al instante en tu máquina se choca con un muro de reglas de DNS y firewall en las que nunca tuvo que pensar antes.

Los límites de recursos son lo segundo. Tus configuraciones locales de mem_limit y cpus, si es que te molestaste en definirlas, son estimaciones hechas en una máquina con mucho más margen que el que va a tener cualquier nodo de producción. Kubernetes o ECS aplican límites reales, con consecuencias reales: OOM kills, throttling, pods desalojados porque pidieron más de lo que el scheduler podía darles. Un contenedor que en local vivía tranquilo con 200MB puede comportarse completamente distinto bajo carga real y presión de memoria real.

Los secretos son lo tercero, y lo más peligroso, porque es lo que más probablemente parezca estar bien hasta que deja de estarlo. Los setups locales de Compose cargan secretos desde un archivo .env que está ahí mismo en el repo, o directamente hardcodeados en el compose file por comodidad. Eso está bien en una laptop a la que nadie más tiene acceso. La situación cambia por completo cuando ese mismo patrón se copia en un script de deploy que apunta a un servidor real, porque ahora hay credenciales en texto plano en un lugar donde nunca debieron persistir.

Y el service discovery es lo cuarto. El DNS por nombre de servicio de Compose es elegante, y también no tiene nada que ver con cómo un orquestador real encuentra y enruta hacia instancias sanas a través de varios nodos, con health checks, rolling restarts, e instancias que aparecen y desaparecen. En el momento en que tienes más de un nodo, o más de una réplica de un servicio, las suposiciones que trae incorporadas la red de Compose dejan de aplicar, y nada en el archivo te avisa.

Nada de esto es un bug de Docker Compose. Es un desajuste entre lo que la herramienta promete, un entorno local que funciona, y lo que el equipo empezó a esperar de ella, un runtime equivalente a producción.

Docker Compose nunca fue diseñado para orquestar nada

Vale la pena decirlo sin vueltas: Compose no tiene ningún concepto de clúster. No reprograma un contenedor caído hacia otro nodo, porque en su modelo no existe otro nodo. No hace rolling deploys, no maneja redes multi-host, no tiene un manejador de secretos incorporado, y nunca intentó tenerlo. Compararlo con Kubernetes o ECS no es una pelea justa, porque resuelven problemas distintos. Uno responde "¿cómo corro este stack ahora mismo, en esta máquina, para poder construir sobre él?". El otro responde "¿cómo mantengo esto corriendo, correctamente, a través de fallas, escala y tiempo, en infraestructura que no controlo del todo?".

Llamar a compose.yml infraestructura como código es donde arranca la confusión. Infra como código implica que el archivo es la fuente de verdad de cómo corre el sistema en el entorno que le importa a los usuarios. Compose nunca intentó ser eso. Es una herramienta de developer experience, y muy buena, que además usa una sintaxis lo suficientemente parecida a un manifiesto de Kubernetes como para que, bajo presión de deadline, los equipos se convenzan de que el salto es más chico de lo que en realidad es.

Qué es lo que realmente cierra la brecha

Nada de esto significa tirar Compose a la basura o sentirte mal por usarlo. Significa ser explícito sobre para qué sirve. Mantenlo como la herramienta rápida, descartable y orientada al desarrollador que hace bien, y trata el destino real del deploy como un asunto aparte, con su propio manifiesto, ya sea YAML de Kubernetes, una task definition de ECS o un job spec de Nomad, escrito para el orquestador sobre el que realmente corres.

Los equipos que se libran del incidente de las 11 de la noche no son los que encontraron una forma ingeniosa de reusar compose.yml en producción. Son los que aceptaron temprano que local y producción son entornos distintos resolviendo problemas distintos, y construyeron dos cosas separadas, con nombres honestos, en lugar de un solo archivo cumpliendo doble función en silencio. Las imágenes de Docker Compose pueden seguir siendo las mismas imágenes que se promueven a través de un registry, las variables de entorno pueden seguir definiéndose una sola vez y templatizarse, pero la capa de orquestación en sí, el modelo de red, la aplicación de límites de recursos, el manejo de secretos, necesita su propia definición, revisada con la misma seriedad que el código de la aplicación.

Un entorno de staging que realmente refleje producción, mismo orquestador, misma topología de red, mismos techos de recursos, es lo que detecta la brecha antes de que la detecte un cliente. No necesita ser caro. Necesita ser honesto sobre correr sobre lo real, en vez de una aproximación local. Si tu staging también es nada más que docker compose up en un servidor más grande, no cerraste la brecha, solo la moviste un salto más lejos de tu laptop.

Hay una señal más simple para vigilar. Si nadie en el equipo puede señalar el manifiesto real de producción sin también señalar compose.yml, eso no es un vacío de documentación. Es la brecha ya en movimiento, y vale la pena marcarla en un standup antes de que se convierta en un incident report. Arreglarlo cuesta una tarde escribiendo un manifiesto de verdad. La alternativa cuesta una noche debuggeando en producción mientras los clientes miran.

La postura real acá

Docker Compose no es una versión liviana de la infraestructura de producción. Es una categoría de herramienta completamente distinta, y el momento en que un equipo empieza a tratarlo como si lo fuera es el momento en que arranca a acumularse una brecha silenciosa entre lo que los ingenieros ven en local y lo que realmente corre detrás de un dominio. La solución no es un mejor compose file. Es admitir que una herramienta de developer experience y una especificación de infraestructura no son el mismo artefacto, por más parecido que se vea el YAML, y negarse a dejar que la presión de deadline difumine esa línea hasta que un incidente de producción la vuelva a trazar por ti.

ESCRITO POR

Logotipo de Howdy.com
Redacción Howdy.com
COMPARTIR