Colisiones de nombres de proyecto en Docker Compose: evita que los stacks se sobrescriban

¿Te fue útil?

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.project y com.docker.compose.service, y Compose usa esas etiquetas más tarde durante up, down, ps y 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_NAME variable 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 ambos container_name: postgres. Uno fallará, o peor, lo gestionas manualmente y olvidas quién lo posee.
  • Nombres de red hardcodeados vía networks: default: name: foo pueden 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:80 colisionará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:

  1. 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.
  2. Compose v1 fue una herramienta en Python durante años. El docker compose moderno (v2) es un plugin del CLI de Docker, y algunos valores por defecto y salidas de nombres cambiaron en la transición.
  3. 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”.
  4. Las etiquetas se convirtieron en el mecanismo principal de propiedad a medida que Docker maduró. Compose depende mucho de etiquetas como com.docker.compose.project porque los nombres de contenedor por sí solos no son fiables una vez que los usuarios los personalizan.
  5. 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.
  6. 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.
  7. La limpieza de “contenedores huérfanos” evolucionó con el tiempo. Opciones como --remove-orphans son útiles, pero cuando la identidad del proyecto es errónea, se convierten en una motosierra.
  8. 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.
  9. 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

  1. Ejecuta docker compose ls y busca nombres sospechosamente genéricos (como app, backend, prod).
  2. Ejecuta docker ps con salida de etiquetas y confirma qué contenedores comparten com.docker.compose.project.
  3. 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)

  1. Lista redes e inspecciona la red en busca de contenedores inesperados.
  2. Lista volúmenes y encuentra qué contenedores los montan.
  3. 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

  1. Pausa trabajos de CI/CD o scripts de despliegue programados que puedan volver a ejecutar Compose.
  2. No ejecutes down hasta que puedas probar que el límite del proyecto es correcto.
  3. Si debes estabilizar el servicio rápidamente, prefiere docker stop/docker start sobre contenedores específicos mientras desenredas la propiedad.

Cuarto: recupera con un nombre de proyecto limpio y explícito

  1. Elige un nuevo nombre de proyecto único para el stack que vas a restaurar (estilo team-env-app).
  2. 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.
  3. 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 -p con un nombre calculado (como payments-staging).
  • Preferido para laptops de desarrollador: name: a nivel superior en el archivo Compose, o un .env por 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 ps coincida 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

  1. Elige un esquema de nombres de proyecto: <team>-<env>-<app>. Manténlo corto, único y legible por humanos.
  2. Hazlo cumplir en la automatización: los scripts de despliegue siempre llaman a docker compose -p "$NAME".
  3. Hazlo cumplir en los repos: añade un .env.example que establezca COMPOSE_PROJECT_NAME con un patrón seguro para desarrollo local (incluye el nombre de usuario).
  4. Prohíbe container_name: por política salvo revisión.
  5. Audita nombres explícitos de red/volumen: cualquier name: debe revisarse como infraestructura.
  6. Pre-flight antes de cambios: docker compose -p NAME config y docker compose -p NAME ps deben parecer correctos.

Checklist B: Plan paso a paso de recuperación tras una colisión

  1. Detén la oscilación: pausa pipelines de despliegue y trabajos programados que puedan ejecutar Compose nuevamente.
  2. Identifica el nombre de proyecto colisionado: usa etiquetas para confirmar qué contenedores comparten com.docker.compose.project.
  3. Haz snapshots si hay datos: si los volúmenes están compartidos, haz un backup antes de cambiar nada.
  4. Levanta un stack limpio con un nombre de proyecto nuevo: usa redes y volúmenes aislados a menos que intencionalmente reutilices datos.
  5. Mueve el tráfico: actualiza la configuración del proxy inverso o los bindings de puertos para enrutar al stack restaurado.
  6. Limpia el stack colisionado cuidadosamente: elimina solo lo que puedas probar que es incorrecto; evita un down a ciegas si la propiedad está mezclada.
  7. 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

  1. Inventario semanal de proyectos: docker compose ls y comprueba nombres genéricos.
  2. Inventario de redes/volúmenes colgantes: confirma que los nombres explícitos tienen propietarios.
  3. Documenta redes compartidas: si realmente necesitas comunicación entre stacks, trata la red compartida como una interfaz controlada.
  4. 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:

  1. Elige una convención de nombres de proyecto que codifique equipo + entorno + app.
  2. Actualiza cada comando de despliegue para pasar -p explícitamente.
  3. Audita archivos Compose en busca de container_name: y nombres explícitos de red/volumen; elimínalos o formalízalos.
  4. 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.
  5. 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.
← Anterior
Embebidos responsivos que no rompen el layout: YouTube, iframes y mapas en producción
Siguiente →
Paginación vs desplazamiento infinito: patrones de UI que no molestan a los usuarios

Deja un comentario