Empieza como una “prueba rápida” inocente. Alguien ejecuta docker compose up -d en un directorio nuevo, en un host que ya ejecuta tres stacks Compose. Cinco minutos después, suena una alarma. Un contenedor fue “recreado”, se reusó el nombre de una red y un volumen fue montado por el servicio equivocado. La producción no solo derivó. Fue asaltada en un callejón por una convención de nombres.
Compose es amigable hasta que deja de serlo. El modo de fallo es sutil: nada “se cae” de inmediato; en su lugar, los stacks se solapan en silencio, los contenedores son adoptados y down elimina las cosas equivocadas con la confianza de un niño con tijeras.
Qué es realmente un “nombre de proyecto” en Compose (y por qué duele)
En Compose, el nombre de proyecto es el límite del espacio de nombres. Es cómo Compose decide qué contenedores, redes y volúmenes pertenecen juntos como “un mismo stack”. Si piensas “el nombre del directorio es el proyecto”, estás medio en lo cierto. Esa mitad es la que causa problemas.
Compose usa el nombre del proyecto para generar nombres de recursos y para aplicar etiquetas que después utiliza para encontrar y gestionar el stack. Eso significa:
- Los nombres de contenedor a menudo se ven como
<project>_<service>_1(o con guiones, dependiendo de la versión y configuración de Compose). - Los nombres de red por defecto a menudo se ven como
<project>_default. - Los volúmenes con nombre suelen convertirse en
<project>_<volume>a menos que los nombres explícitamente. - Se adjuntan etiquetas como
com.docker.compose.projectycom.docker.compose.service, y Compose usa esas etiquetas más tarde duranteup,down,psy la limpieza.
Una colisión de nombre de proyecto ocurre cuando dos ejecuciones de Compose usan el mismo nombre de proyecto (intencionalmente o no) en el mismo demonio Docker. Compose entonces las trata como el mismo stack. Procederá a “reconciliar” el estado: recrear contenedores, volver a conectar redes, reutilizar volúmenes o eliminar recursos que el otro stack necesitaba.
La parte que sorprende a la gente: las colisiones no requieren archivos Compose idénticos. Solo requieren el mismo nombre de proyecto y nombres/etiquetas de recursos que se solapen. Compose no es un schedulador de clúster. Es un reconciliador de estado que asume que quisiste lo que escribiste.
Broma #1: Los nombres de proyecto de Docker Compose son como las etiquetas en la cocina de la oficina: si dos personas escriben “Lunch”, alguien terminará comiendo tristeza.
Dónde viene el nombre de proyecto (el orden de precedencia importa)
Compose elige un nombre de proyecto usando un orden de precedencia. Si no lo estableces deliberadamente, dejas que el entorno lo defina por ti, que es una forma optimista de ejecutar producción.
Fuentes comunes, más o menos de “más explícito” a “menos”:
docker compose -p <name>COMPOSE_PROJECT_NAMEvariable de entorno- Nivel superior
name:en el archivo Compose (compatible con Compose v2 moderno) - El nombre base del directorio del proyecto (a menudo el directorio actual)
Si tu organización tiene múltiples repos llamados app, varios directorios llamados docker o la costumbre de clonar cosas en /srv sin nombres de carpeta únicos, ya has construido una máquina de colisiones. Añade runners de CI que usan la misma ruta de checkout y puedes fabricar interrupciones a demanda.
Cómo ocurren las colisiones: las tres capas de nombres
Para detener las colisiones, necesitas entender qué está colisionando. Los stacks de Compose se solapan de tres maneras principales:
1) Namespace de proyecto de Compose (etiquetas y búsqueda)
Compose encuentra “sus” recursos por etiquetas. La importante es com.docker.compose.project. Si dos stacks comparten el mismo nombre de proyecto, Compose seleccionará recursos de ambos cuando realice acciones como down, restart y, a veces, up con limpieza de huérfanos.
Aquí es donde ocurre realmente que los “stacks se sobrescriban”. Compose no está sobrescribiendo archivos; está reconciliando definiciones de contenedores contra un conjunto de objetos etiquetados que cree que son un proyecto.
2) Nombres de objetos Docker (contenedores, redes, volúmenes)
Incluso si las etiquetas son correctas, los nombres de objetos Docker pueden colisionar si hardcodeas nombres:
- Nombres de contenedor hardcodeados vía
container_name:son globales en un demonio. Dos stacks no pueden tener amboscontainer_name: postgres. Uno fallará, o peor, lo gestionas manualmente y olvidas quién lo posee. - Nombres de red hardcodeados vía
networks: default: name: foopueden hacer que proyectos separados compartan una red. Eso puede ser intencional. También puede ser una fiesta de fuga de datos. - Nombres de volumen hardcodeados pueden hacer que proyectos separados compartan datos. Otra vez, quizá intencional. Pero la mayoría de las veces es un “¿por qué staging usa datos de prod?” de lo intencional.
3) Enlaces de puertos y recursos del host
Incluso si el nombrado es perfecto, el host sigue siendo un único host:
- Dos stacks vinculando
0.0.0.0:80colisionarán. Compose devolverá error (mejor escenario) o el tráfico se dirigirá a un sitio raro (peor escenario: el proxy frontal apunta al backend equivocado). - Dos stacks montando la misma ruta del host (como
/var/lib/app) pueden sobrescribir el estado de uno y otro. - Dos stacks usando las mismas rutas de secretos/config en el host pueden causar fallos de “funciona en mi máquina” con un giro de producción.
Las colisiones más sigilosas son las donde Docker permite compartir (volúmenes, redes) y Compose lo fomenta a menos que le digas que no.
Hechos interesantes y contexto histórico
Algo de contexto ayuda porque el comportamiento de Compose no apareció de la nada. Aquí hay hechos concretos que explican por qué el nombre de proyecto es potente y peligroso:
- Compose empezó como “Fig” (era 2013–2014). Su trabajo inicial fue: “levantar un montón de contenedores desde YAML”, no “gestionar entornos multi-tenant en el mismo demonio”. El namespacing era pragmático, no paranoico.
- Compose v1 fue una herramienta en Python durante años. El
docker composemoderno (v2) es un plugin del CLI de Docker, y algunos valores por defecto y salidas de nombres cambiaron en la transición. - Los nombres de proyecto se diseñaron para derivarse de directorios porque facilitaba el desarrollo local: clona el repo, ejecuta Compose, obtén recursos aislados “automáticamente”. En hosts compartidos, ese “automático” se vuelve “aleatorio”.
- Las etiquetas se convirtieron en el mecanismo principal de propiedad a medida que Docker maduró. Compose depende mucho de etiquetas como
com.docker.compose.projectporque los nombres de contenedor por sí solos no son fiables una vez que los usuarios los personalizan. container_name:se desaconseja intencionalmente en muchos patrones de producción porque rompe el escalado y aumenta las colisiones de nombres globales. Compose no puede crear múltiples réplicas de un servicio con un nombre de contenedor fijo.- La red por defecto por proyecto fue una gran mejora de usabilidad: redujo la necesidad de enlace manual y dio descubrimiento de servicios dentro de un proyecto. También convirtió el nombre de proyecto en el namespace de la red.
- La limpieza de “contenedores huérfanos” evolucionó con el tiempo. Opciones como
--remove-orphansson útiles, pero cuando la identidad del proyecto es errónea, se convierten en una motosierra. - Las semánticas de “down” de Compose son intencionalmente destructivas: elimina recursos que cree haber creado. Eso es correcto—salvo que tu límite de proyecto esté equivocado.
- Los stacks de Swarm y los proyectos de Compose son conceptos diferentes, pero las personas los confunden porque ambos usan YAML y ambos dicen “stack”. Esa confusión provoca atajos operativos y errores de nombre.
Tres micro-historias corporativas desde las trincheras de las colisiones
Micro-historia 1: El incidente causado por una suposición equivocada
En una empresa SaaS mediana, un equipo ejecutó pruebas de carga “temporales” en el mismo host Docker que ejecutaba un entorno de staging. Dijeron que fueron cuidadosos: directorios distintos, archivos Compose distintos, nombres de servicio distintos. ¿Qué podría colisionar?
La suposición fue: “Directorios distintos significan stacks distintos.” No pasaron -p, no establecieron COMPOSE_PROJECT_NAME, y su directorio de trabajo para ambas ejecuciones resultó ser /srv/app porque usaban un script de automatización compartido que hacía cd ahí incondicionalmente.
El archivo Compose de la prueba de carga definía un servicio Redis y un servicio worker. Staging también tenía Redis y workers. Mismo nombre de proyecto significó que Compose creyó que era un solo proyecto. Cuando empezó la prueba de carga, Compose “amablemente” recreó el contenedor worker con la imagen de la prueba de carga porque coincidía el nombre del servicio bajo la misma etiqueta de proyecto.
Nada falló inmediatamente. El proceso worker arrancó bien. Simplemente era el worker equivocado, conectado al mismo Redis y cola de staging. Los usuarios de staging vieron trabajos procesados fuera de orden y algunos trabajos “desaparecieron” por formatos de mensaje incompatibles. El incidente no fue un crash; fue comportamiento corrupto. Esos son los que te roban el fin de semana.
La solución fue aburrida: nombres de proyecto explícitos para cada entorno y automatización que se niega a ejecutar Compose sin un nombre de proyecto. La línea del postmortem que importó: “Los nombres de directorio no son aislamiento”.
Micro-historia 2: La optimización que salió mal
Un equipo de plataforma quería despliegues más rápidos. Notaron que las recreaciones de Compose tardaban en un host ocupado, así que optimizaron fijando nombres de objetos: container_name explícito, nombres de red explícitos, nombres de volumen explícitos. La idea era facilitar la resolución de problemas y reducir la “oscilación”.
Funcionó durante un mes. Luego dos equipos internos desplegaron servicios diferentes que usaban un fragmento Compose “base” compartido. El fragmento ponía container_name: api porque alguien pensó que era “limpio”. Ahora dos stacks intentaron crear un contenedor llamado api en el mismo demonio.
Compose se negó a iniciar el segundo stack, y el equipo lo “arregló” quitando manualmente el contenedor existente y reiniciando. Eso puso en marcha el segundo stack… y silenciosamente mató el primer servicio. El outage no vino de Docker; vino de humanos peleando con una decisión de nombres que eliminó la capacidad de Compose para gestionar varias instancias de forma segura.
El final irónico: el nombre de red explícito causó comunicación cruzada. Contenedores de depuración de un stack podían resolver y alcanzar servicios en otro stack porque estaban en la misma red definida por el usuario. Nadie lo intentó. Simplemente pasó porque la “optimización” sustituyó aislamiento por conveniencia.
Revirtieron la “optimización” de nombres, conservaron nombres de proyecto explícitos y solo pusieron nombres fijos cuando el requisito era real (por ejemplo, integrar con sistemas externos que esperan un nombre de red estable). La ganancia de rendimiento no valía el impuesto de ambigüedad.
Micro-historia 3: La práctica aburrida pero correcta que salvó el día
Una empresa regulada ejecutaba Compose en una flota de VMs únicas (snowflake VMs). Tenían una costumbre que parecía excesiva: cada stack tenía un nombre de proyecto único, el nombre de proyecto incluía entorno y propietario, y se establecía en dos lugares: CI pasaba -p y el repo incluía un .env con COMPOSE_PROJECT_NAME para ejecuciones locales.
Un día un ingeniero necesitó parchar rápidamente un problema en staging y ejecutó Compose accidentalmente desde el directorio equivocado en el host. El archivo Compose era correcto, pero el directorio de trabajo estaba dentro de otro repo. Sin las salvaguardas, eso habría reutilizado el nombre de proyecto del otro repo y empezado la danza del solapamiento.
En su lugar, su wrapper de despliegue imprimió el nombre de proyecto resuelto y se negó a continuar a menos que coincidiera con una lista permitida para ese host. Era simple: parsear docker compose config, comprobar el nombre, comprobar las etiquetas esperadas y luego ejecutar up. El ingeniero refunfuñó, corrigió el comando y siguió.
Semanas después, en una revisión de incidente no relacionada, se dieron cuenta de que el wrapper había evitado una colisión que habría agravado el apagón. Nadie escribió un hilo heroico en Slack sobre ello. Ese es el punto. Los controles aburridos son los que evitan que protagonices tu propio postmortem.
Tareas prácticas: 12+ comandos que exponen colisiones (y qué hacer después)
Estos no son “bonito saber”. Son lo que ejecutas cuando alguien dice “Compose sobrescribió mi stack” y quieres evidencia, no sensaciones. Cada tarea incluye: comando, qué significa la salida y la decisión que tomas.
Tarea 1: Ver qué nombre de proyecto usará Compose antes de tocar nada
cr0x@server:~$ cd /srv/staging/app
cr0x@server:~$ docker compose config --format json | head
{
"name": "app",
"services": {
...
Qué significa: El nombre de proyecto resuelto es app. Si esperabas staging-app, ya estás en territorio de colisión.
Decisión: Si el nombre es demasiado genérico, para y vuelve a ejecutar con -p o establece name:/COMPOSE_PROJECT_NAME. No ejecutes up.
Tarea 2: Confirmar el nombre de proyecto que Compose está usando para un stack en ejecución
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}' | head
NAMES COM.DOCKER.COMPOSE.PROJECT COM.DOCKER.COMPOSE.SERVICE
app-web-1 app web
app-db-1 app db
payments-web-1 payments web
Qué significa: Los dos primeros contenedores pertenecen al proyecto app. El tercero pertenece a payments. Si ves contenedores que no esperabas bajo la misma etiqueta de proyecto, tienes una colisión.
Decisión: Identifica qué repo/automatización también está usando ese nombre de proyecto. Aíslalo escogiendo un nuevo nombre de proyecto para uno de ellos.
Tarea 3: Listar “proyectos” de Compose en el daemon (inventario rápido)
cr0x@server:~$ docker compose ls
NAME STATUS CONFIG FILES
app running(2) /srv/staging/app/docker-compose.yml
payments running(1) /srv/payments/docker-compose.yml
Qué significa: Compose piensa que hay un proyecto llamado app y lo asocia con una ruta de archivo de configuración. Si múltiples despliegues usan el mismo nombre de proyecto, esta lista se vuelve engañosa o cambia entre rutas.
Decisión: Si el proyecto muestra una ruta de configuración inesperada, asume una colisión y audita despliegues recientes.
Tarea 4: Inspeccionar las etiquetas de un contenedor sospechoso para encontrar su origen
cr0x@server:~$ docker inspect app-web-1 --format '{{json .Config.Labels}}' | head
{"com.docker.compose.config-hash":"b1b4...","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"app","com.docker.compose.project.config_files":"docker-compose.yml","com.docker.compose.project.working_dir":"/srv/staging/app","com.docker.compose.service":"web"}
Qué significa: El contenedor indica que fue creado desde /srv/staging/app. Si encuentras contenedores con la misma etiqueta de proyecto pero diferente working_dir, tienes dos fuentes alimentando un mismo proyecto.
Decisión: Elige una fuente de verdad. Renombra un proyecto y redepliega limpiamente. No dejes que repos compartan la etiqueta de proyecto.
Tarea 5: Identificar redes que están compartidas (a veces sin querer)
cr0x@server:~$ docker network ls --format 'table {{.Name}}\t{{.Driver}}' | grep -E 'app|payments'
NAME DRIVER
app_default bridge
payments_default bridge
shared_frontend bridge
Qué significa: app_default es la red por defecto para el proyecto app. Si ves dos proyectos adjuntos a una misma red, eso puede ser deliberado—o una fuga.
Decisión: Si no creaste intencionalmente una red compartida, no adjuntes múltiples proyectos a ella. Usa redes distintas por proyecto por defecto.
Tarea 6: Comprobar qué contenedores están conectados a una red
cr0x@server:~$ docker network inspect app_default --format '{{json .Containers}}' | head
{"3c1f...":{"Name":"app-web-1","IPv4Address":"172.20.0.2/16"},"a77b...":{"Name":"app-db-1","IPv4Address":"172.20.0.3/16"}}
Qué significa: Solo app-web-1 y app-db-1 están en app_default. Si vieras payments-web-1 aquí, sería una conexión entre proyectos.
Decisión: Las conexiones entre proyectos requieren una conversación de seguridad y depuración. Si es accidental, desconecta y redepliega con definiciones de red corregidas.
Tarea 7: Encontrar volúmenes que parecen por proyecto pero no lo son
cr0x@server:~$ docker volume ls --format 'table {{.Name}}\t{{.Driver}}' | grep -E '^app_|^payments_'
NAME DRIVER
app_dbdata local
payments_pg local
Qué significa: Estos son volúmenes con nombre, probablemente creados por Compose. Si dos stacks usan el mismo nombre de volumen mediante name: explícito, compartirán datos.
Decisión: Si el intercambio de volúmenes no está intencionado y documentado, renombra volúmenes y migra datos. Trata el compartir accidental de volúmenes como un incidente de datos hasta demostrar lo contrario.
Tarea 8: Confirmar qué contenedores montan un volumen dado
cr0x@server:~$ docker ps -a --filter volume=app_dbdata --format 'table {{.Names}}\t{{.Status}}'
NAMES STATUS
app-db-1 Up 3 hours
Qué significa: Solo app-db-1 usa app_dbdata. Si ves contenedores DB de múltiples stacks usando el mismo volumen, tienes estado compartido.
Decisión: Para y evalúa la integridad de los datos. Separa volúmenes antes de realizar cualquier “limpieza”.
Tarea 9: Detectar contenedores huérfanos que Compose podría borrar
cr0x@server:~$ docker compose -p app ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
app-web-1 nginx:1.25 "/docker-entrypoint.…" web 3 hours ago Up 3 hours 0.0.0.0:8080->80/tcp
app-db-1 postgres:16 "docker-entrypoint.s…" db 3 hours ago Up 3 hours
Qué significa: Compose lista lo que cree que pertenece a -p app. Si cambiaste recientemente servicios en YAML y ahora ves contenedores inesperados con la misma etiqueta de proyecto, --remove-orphans podría eliminar algo que todavía necesitas.
Decisión: No ejecutes --remove-orphans hasta confirmar que el límite del proyecto es correcto.
Tarea 10: Previsualizar lo que se creará/recreará con un cambio de Compose
cr0x@server:~$ docker compose -p app up -d --no-start
[+] Running 2/2
✔ Network app_default Created
✔ Container app-web-1 Created
Qué significa: --no-start aún crea recursos pero no los inicia. Es una forma más segura de ver si Compose está a punto de crear objetos bajo un nombre de proyecto que no pretendías usar.
Decisión: Si ves recursos que se están creando y que ya existen bajo las expectativas de otro stack, aborta y corrige el nombre de proyecto primero.
Tarea 11: Probar una colisión comparando hashes de configuración entre contenedores
cr0x@server:~$ docker inspect app-web-1 --format '{{.Config.Labels.com.docker.compose.config-hash}}'
b1b4c7f2d4a0...
cr0x@server:~$ docker inspect app-db-1 --format '{{.Config.Labels.com.docker.compose.config-hash}}'
b1b4c7f2d4a0...
Qué significa: Hashes coincidentes sugieren que los contenedores fueron creados desde la misma configuración Compose resuelta. Si dentro de un “proyecto” ves hashes diferentes para lo que debería ser la misma generación del stack, puede que tengas dos configs de Compose distintas gestionando el mismo nombre de proyecto a lo largo del tiempo.
Decisión: Congela cambios. Decide qué configuración es la autoritativa y redepliega con un nombre de proyecto nuevo si es necesario para separar el estado de forma segura.
Tarea 12: Buscar nombres globales hardcodeados que colisionarán
cr0x@server:~$ grep -R --line-number -E 'container_name:|name:' /srv/staging/app/docker-compose.yml
14: container_name: postgres
33: default:
34: name: shared_frontend
Qué significa: container_name: postgres es un nombre de contenedor global. La red shared_frontend está explícitamente compartida entre proyectos (o lo estará, si otro stack usa el mismo nombre).
Decisión: Elimina container_name salvo que tengas un requisito operativo muy específico. Trata los nombres de red explícitos como una decisión de límite de seguridad, no como una conveniencia.
Tarea 13: Verificar qué nombre de proyecto piensa Compose cuando hay un archivo .env presente
cr0x@server:~$ cat /srv/staging/app/.env
COMPOSE_PROJECT_NAME=app
cr0x@server:~$ docker compose config --format json | head
{
"name": "app",
"services": {
...
Qué significa: El nombre de proyecto está siendo forzado por .env. La gente olvida que existen estos archivos, especialmente durante la respuesta a incidentes.
Decisión: Si el valor es demasiado genérico, cámbialo y redepliega. También comprueba si otros repos copiaron la misma plantilla .env.
Tarea 14: Mostrar eventos durante la “sobrescritura” para detectar recreación y eliminación
cr0x@server:~$ docker events --since 10m --filter type=container --filter event=destroy
2026-01-03T10:11:22.123456789Z container destroy 3c1f... (name=app-web-1, image=nginx:1.25)
Qué significa: Tienes prueba de que un contenedor fue destruido, cuándo y con qué nombre. Combínalo con los logs de despliegue para correlacionar la ejecución de Compose responsable.
Decisión: Si la destrucción ocurrió durante un despliegue en un repo/ruta de host diferente a la esperada, tienes una colisión de nombre de proyecto o un operador ejecutó down en el lugar equivocado.
Tarea 15: Confirmar si “down” apuntará al proyecto correcto antes de ejecutarlo
cr0x@server:~$ docker compose -p app ps --all
NAME IMAGE SERVICE STATUS
app-web-1 nginx:1.25 web Up 3 hours
app-db-1 postgres:16 db Up 3 hours
Qué significa: Este es tu chequeo previo. Si la lista incluye contenedores que no pretendías gestionar, down los eliminará.
Decisión: Si algo te resulta desconocido, no ejecutes down. Corrige la nomenclatura del proyecto primero, o usa operaciones de contenedor dirigidas en su lugar.
Broma #2: Ejecutar docker compose down con el nombre de proyecto equivocado es como despedir al becario porque el lector de tarjetas falló.
Guía rápida de diagnóstico
Cuando un stack “sobrescribe” a otro, no tienes tiempo para filosofar sobre YAML. Quieres el camino más corto a: qué cambió, quién lo posee y cómo detener la hemorragia.
Primero: establece el namespace del proyecto que está colisionando
- Ejecuta
docker compose lsy busca nombres sospechosamente genéricos (comoapp,backend,prod). - Ejecuta
docker pscon salida de etiquetas y confirma qué contenedores compartencom.docker.compose.project. - Inspecciona al menos las etiquetas de un contenedor para
com.docker.compose.project.working_dir. Eso te dice desde dónde fue creado.
Segundo: identifica qué se está compartiendo realmente (redes, volúmenes, puertos)
- Lista redes e inspecciona la red en busca de contenedores inesperados.
- Lista volúmenes y encuentra qué contenedores los montan.
- Comprueba las vinculaciones de puertos en el servicio “sobrescrito”: si un puerto se movió, el tráfico se movió.
Tercero: detén la automatización y evita reconciliaciones adicionales
- Pausa trabajos de CI/CD o scripts de despliegue programados que puedan volver a ejecutar Compose.
- No ejecutes
downhasta que puedas probar que el límite del proyecto es correcto. - Si debes estabilizar el servicio rápidamente, prefiere
docker stop/docker startsobre contenedores específicos mientras desenredas la propiedad.
Cuarto: recupera con un nombre de proyecto limpio y explícito
- Elige un nuevo nombre de proyecto único para el stack que vas a restaurar (estilo
team-env-app). - Despliega en paralelo usando puertos diferentes (o detrás de un cambio de proxy) y con volúmenes aislados salvo que la continuidad de datos requiera reutilizar.
- Sólo después de la recuperación, limpia los recursos colisionados cuidadosamente y de forma explícita.
Una cita que debería estar en la pared de todos los equipos de operaciones, porque explica por qué estandarizas cosas aburridas como el nombrado: paráfrasis de una idea
de John Allspaw: los incidentes vienen del trabajo normal interactuando de maneras inesperadas.
Cómo prevenir colisiones de nombres de proyecto (reglas estrictas, no sensaciones)
Si ejecutas múltiples stacks de Compose en el mismo demonio, necesitas nombres explícitos y aplicados. No “acuérdate de pasar -p.” Aplicados. Los humanos olvidan. La automatización repite.
Regla 1: Siempre establece un nombre de proyecto único de forma explícita
Escoge un método y estandarízalo:
- Preferido para automatización: usa siempre
docker compose -pcon un nombre calculado (comopayments-staging). - Preferido para laptops de desarrollador:
name:a nivel superior en el archivo Compose, o un.envpor desarrollador que incluya el nombre de usuario en el nombre de proyecto.
Lo que debes evitar: confiar en el nombre del directorio, especialmente en hosts compartidos y runners de CI.
Regla 2: Prohíbe container_name: salvo que haya una justificación por escrito
Rompe el escalado, dificulta la reutilización multi-stack y fomenta comandos docker manuales que hacen que el estado se desvíe de Compose.
Si necesitas un nombre DNS estable para otros contenedores, ya lo tienes: los nombres de servicio en la red del proyecto. Si necesitas integración externa estable, usa un reverse proxy, un hostname estable o un alias de red estable—no un nombre de contenedor global.
Regla 3: Trata los nombres explícitos de redes y volúmenes como infraestructura compartida
Si defines name: para una red o volumen, estás saliendo del espacio de nombres por proyecto de Compose. Eso puede ser correcto. También puede convertirlo en un puente multi-tenant accidental.
Haz que sea política: los nombres explícitos deben incluir entorno y propietario, como shared_frontend_prod o payments_pg_prod. “shared” debe significar “revisado”, no “alguien lo tipeó una vez”.
Regla 4: Haz obligatorio el “pre-flight” en los scripts de despliegue
Tu wrapper debe imprimir el nombre de proyecto resuelto y fallar rápido si no es lo que esperas. Puedes hacerlo sin construir una plataforma:
- Resuelve la config (
docker compose config) - Comprueba el nombre resultante
- Comprueba que
docker compose -p NAME pscoincida con los servicios esperados (o esté vacío en el primer despliegue) - Luego ejecuta
up -d
Regla 5: Separa entornos por demonio cuando puedas
Sí, puedes ejecutar prod y staging en el mismo host. También puedes intentar andar en bicicleta dentro de un túnel de lavado de autos. Si debes co-ubicar, la disciplina de nombres se vuelve innegociable y debes aislar puertos, volúmenes y redes con intención.
Errores comunes: síntoma → causa raíz → solución
Esta es la sección que desearías haber leído antes del incidente. Cada ítem es específico y apunta a la palanca real que debes accionar.
1) Síntoma: docker compose up “recreó” un contenedor de otro stack
Causa raíz: Mismo nombre de proyecto, mismo nombre de servicio. Compose reconciliará y reemplazará la definición del contenedor.
Solución: Usa nombres de proyecto únicos vía -p, COMPOSE_PROJECT_NAME o name: a nivel superior. Redepliega el stack sobrescrito bajo un nombre nuevo.
2) Síntoma: Ejecutar docker compose down eliminó contenedores que no pretendías
Causa raíz: Ejecutaste down con un nombre de proyecto que coincidía con múltiples despliegues; Compose eliminó todo lo etiquetado con ese proyecto.
Solución: Antes de down, siempre ejecuta docker compose -p NAME ps --all. Si la lista es errónea, detente y corrige el nombre del proyecto.
3) Síntoma: Un servicio es accesible desde otro stack “no relacionado”
Causa raíz: Red definida por el usuario compartida debido a un nombre de red explícito o conexiones manuales a la red.
Solución: Elimina nombres de red compartidos explícitos salvo que la intención sea compartir; audita con docker network inspect las conexiones; redepliega con redes aisladas.
4) Síntoma: La base de datos de staging contiene datos que parecen de producción
Causa raíz: Volumen con nombre compartido causado por name: explícito en un volumen o por el mismo nombre de proyecto, lo que lleva al mismo nombre de volumen generado.
Solución: Separa volúmenes por entorno. Si debes clonar datos, hazlo mediante respaldo/restauración explícita, no por compartir volúmenes accidentalmente.
5) Síntoma: Compose se niega a iniciar con “Conflict. The container name is already in use”
Causa raíz: container_name: fuerza un nombre global. Otro contenedor ya lo usa.
Solución: Elimina container_name:. Si necesitas direccionamiento estable, usa nombres DNS de servicio o un proxy.
6) Síntoma: Un despliegue funcionó ayer, falla hoy en el mismo host
Causa raíz: Otro equipo introdujo un nombre de proyecto o nombre de red que colisiona; tu stack ahora comparte namespace o puertos.
Solución: Haz inventario con docker compose ls, comprueba etiquetas y aplica una convención de nombres. Añade guardarraíles en CI.
7) Síntoma: --remove-orphans borró algo “importante”
Causa raíz: El límite del proyecto estaba equivocado; Compose consideró el contenedor “importante” como huérfano dentro de ese proyecto.
Solución: No uses --remove-orphans en hosts compartidos a menos que la nomenclatura de proyectos esté aplicada y auditada. Prefiere limpieza explícita tras validación.
Listas de verificación / plan paso a paso
Checklist A: Estándar seguro para cada despliegue de Compose
- Elige un esquema de nombres de proyecto:
<team>-<env>-<app>. Manténlo corto, único y legible por humanos. - Hazlo cumplir en la automatización: los scripts de despliegue siempre llaman a
docker compose -p "$NAME". - Hazlo cumplir en los repos: añade un
.env.exampleque establezcaCOMPOSE_PROJECT_NAMEcon un patrón seguro para desarrollo local (incluye el nombre de usuario). - Prohíbe
container_name:por política salvo revisión. - Audita nombres explícitos de red/volumen: cualquier
name:debe revisarse como infraestructura. - Pre-flight antes de cambios:
docker compose -p NAME configydocker compose -p NAME psdeben parecer correctos.
Checklist B: Plan paso a paso de recuperación tras una colisión
- Detén la oscilación: pausa pipelines de despliegue y trabajos programados que puedan ejecutar Compose nuevamente.
- Identifica el nombre de proyecto colisionado: usa etiquetas para confirmar qué contenedores comparten
com.docker.compose.project. - Haz snapshots si hay datos: si los volúmenes están compartidos, haz un backup antes de cambiar nada.
- Levanta un stack limpio con un nombre de proyecto nuevo: usa redes y volúmenes aislados a menos que intencionalmente reutilices datos.
- Mueve el tráfico: actualiza la configuración del proxy inverso o los bindings de puertos para enrutar al stack restaurado.
- Limpia el stack colisionado cuidadosamente: elimina solo lo que puedas probar que es incorrecto; evita un
downa ciegas si la propiedad está mezclada. - Añade un guardarraíl: wrapper de despliegue que imprime el nombre de proyecto resuelto y falla si no coincide con lo esperado para el host.
Checklist C: Higiene del host para demonios Docker compartidos
- Inventario semanal de proyectos:
docker compose lsy comprueba nombres genéricos. - Inventario de redes/volúmenes colgantes: confirma que los nombres explícitos tienen propietarios.
- Documenta redes compartidas: si realmente necesitas comunicación entre stacks, trata la red compartida como una interfaz controlada.
- Mantén entornos separados: si es posible, separa demonios/hosts para prod vs staging.
Preguntas frecuentes
1) ¿Qué se “sobrescribe” exactamente durante una colisión de nombre de proyecto?
Compose no sobrescribe YAML. Reconciliar objetos Docker bajo la misma etiqueta de proyecto. Eso puede significar que contenedores se recrean con imágenes/entornos distintos, las redes se comparten y los volúmenes se reutilizan.
2) Si uso archivos Compose diferentes, ¿puedo aún colisionar?
Sí. El mismo nombre de proyecto es suficiente. Diferentes archivos de configuración pueden seguir generando recursos bajo el mismo namespace de proyecto, especialmente si los nombres de servicio coinciden.
3) ¿El nombre del directorio es siempre el nombre de proyecto por defecto?
A menudo, sí, pero no siempre. Un archivo .env, COMPOSE_PROJECT_NAME o un name: a nivel superior pueden sobrescribirlo. Por eso siempre debes comprobar docker compose config antes de desplegar.
4) ¿Debo establecer el nombre de proyecto en .env o siempre pasar -p?
Para CI/CD y producción: prefiere -p para que el sistema de despliegue lo controle. Para desarrolladores: .env está bien, pero incluye el nombre de usuario para evitar colisiones en hosts de desarrollo compartidos.
5) ¿Por qué se considera dañino container_name?
Fuerza un nombre global en el demonio, rompe el escalado y hace que los stacks sean menos portables. También fomenta intervención manual, que es donde las “soluciones temporales” se convierten en outages permanentes.
6) ¿Puedo compartir intencionalmente una red o volumen entre proyectos?
Puedes y a veces deberías (por ejemplo, para una red de proxy reverso compartida). Pero trátalo como un contrato de interfaz. Nómbralo explícitamente, documéntalo y revisa las conexiones. No lo hagas “porque funcionó”.
7) ¿Cómo sé qué repo creó un contenedor?
Inspecciona las etiquetas. Busca com.docker.compose.project.working_dir y com.docker.compose.project.config_files. Eso suele apuntar directamente a la ruta usada durante la creación.
8) ¿Es seguro docker compose down?
Es seguro cuando el límite del nombre de proyecto es correcto. En hosts compartidos con nombres laxos, es una herramienta de demolición. Siempre ejecuta primero docker compose -p NAME ps --all.
9) ¿Este problema desaparece si uso Kubernetes/Swarm?
Cambias este modo de fallo por otros distintos. Los namespaces en Kubernetes ayudan, pero aún puedes colisionar con volúmenes compartidos, recursos a nivel de clúster y selectores mal configurados. La disciplina no queda obsoleta.
Conclusión: próximos pasos que puedes desplegar hoy
Las colisiones de nombres de proyecto en Compose no son un caso raro. Son el resultado predecible de dejar que los valores por defecto decidan tus límites de aislamiento. La solución es sencilla, pero debe aplicarse, no sugerirse.
Haz lo siguiente:
- Elige una convención de nombres de proyecto que codifique equipo + entorno + app.
- Actualiza cada comando de despliegue para pasar
-pexplícitamente. - Audita archivos Compose en busca de
container_name:y nombres explícitos de red/volumen; elimínalos o formalízalos. - Añade una comprobación pre-flight en la automatización que imprima el nombre de proyecto resuelto y se niegue a ejecutar si es genérico o inesperado.
- Ejecuta los comandos de inventario de este artículo en tu host más ocupado y busca colisiones antes de que ellas te busquen a ti.