Tutorial

Docker Compose en producción: checklist de 10 puntos

Despliegue12 min de lectura10 pasos

Un archivo docker-compose.yml que funciona en su equipo no sobrevive tal cual a la producción. Reinicio tras un reboot, límites de recursos, secretos, rotación de logs: son ajustes ausentes por defecto que provocan averías silenciosas una vez el servicio está expuesto. Esta checklist reúne los 10 parámetros que hay que controlar antes de desplegar una stack Compose en un VPS, y después los health checks, los secretos, el rollback y la solución de errores. También cubre el paso de `docker-compose` a `docker compose`, que la actualización a Ubuntu 26.04 obliga a resolver.

Contenido· Por qué una checklist antes de producción1/13
  1. 01Por qué una checklist antes de producción
  2. 02Los 10 puntos de un vistazo
  3. 03Requisitos previos
  4. 04Los 10 pasos en detalle
  5. 05Valores por defecto de dev frente a ajustes de producción
  6. 06docker-compose o docker compose: la ruptura de Ubuntu 26.04
  7. 07Migrar sus scripts y añadir un shim
  8. 08Health checks avanzados: supervisar el estado real del servicio
  9. 09Secretos Docker sin Swarm: montar archivos en /run/secrets/
  10. 10Rollback: volver atrás tras un despliegue fallido
  11. 11Solución de problemas: 6 errores comunes y sus soluciones
  12. 12Copias de seguridad de volúmenes sin corrupción
  13. 13Conclusión

Por qué una checklist antes de producción

Docker Compose se diseñó para el desarrollo: sus valores por defecto priorizan la simplicidad, no la robustez. Un contenedor no se reinicia tras un reinicio del host, sus logs crecen sin límite, puede consumir toda la RAM de la máquina y escucha en todas las interfaces de red. En desarrollo, ninguno de estos comportamientos supone un problema. En producción, esos ajustes ausentes se convierten en incidentes: disco lleno a las tres de la madrugada, base de datos perdida tras un docker compose down, servicio inaccesible tras un corte de corriente. La buena noticia: endurecer una stack Compose no exige reescribirla, solo una decena de añadidos concretos. Repase cada punto antes de publicar y su servicio aguantará la carga y los reinicios sin vigilancia constante.

Los 10 puntos de un vistazo

  • restart: unless-stopped — el contenedor se reinicia tras un reinicio del host o un fallo
  • healthcheck — Docker detecta un contenedor bloqueado y permite reinicios progresivos
  • límites de CPU/memoria — un servicio desbocado no puede asfixiar a sus vecinos
  • secretos via .env o Docker secrets — nunca una contraseña en texto plano en el Compose
  • volúmenes con nombre — los datos sobreviven a un docker compose down
  • rotación de logs — max-size y max-file evitan que el disco se llene
  • aislamiento de red — separe frontend y backend, no exponga todo en el bridge por defecto
  • binding de puertos — 127.0.0.1:PORT detrás de un proxy inverso, no 0.0.0.0
  • tags de imagen fijados — una versión o digest, nunca latest
  • depends_on con condición — service_healthy evita las carreras de arranque

Requisitos previos

Necesita un VPS con Docker Engine y el plugin Compose v2. El comando es docker compose, sin guion: compruébelo con docker compose version, porque la sintaxis deploy.resources fuera de Swarm exige Compose v2 (el antiguo docker-compose se trata más abajo). Controle también el motor con docker version. Docker Engine 29.7.2 corrige CVE-2026-17106 (CopyEscape), una race condition de docker cp divulgada el 10 de agosto de 2026: un contenedor no fiable puede sobrescribir con ella un archivo del host, incluido el binario runc. En los equipos de trabajo, el parche llegó con Docker Desktop 4.86.0. Mientras no actualice, no ejecute docker cp desde un contenedor cuyo contenido no controla. Guarde el proyecto en una carpeta dedicada (/opt/miapp), con un .env en chmod 600. Prevea un proxy inverso delante — Traefik, Caddy o Nginx —, porque el binding de puertos supone que el tráfico público nunca llega a sus contenedores, y versione el archivo Compose.

Los 10 pasos en detalle

  1. 1. Política de reinicio

    Añada restart: unless-stopped a cada servicio. El contenedor se reinicia tras un crash o reinicio del host, pero permanece detenido si usted lo paró voluntariamente. Evite restart: always que relanzaría incluso un contenedor que desea mantener parado, y prefiera unless-stopped para servicios de aplicación y on-failure para tareas puntuales.

  2. 2. Healthcheck

    Declare un bloque healthcheck: con un test (p. ej. curl -f http://localhost:8080/health || exit 1), un interval (frecuencia de comprobación, p. ej. 30s), un timeout (p. ej. 10s) y retries (p. ej. 3). Añada también start_period: 40s para servicios que tardan en arrancar, para no marcarlos unhealthy durante la inicialización. Docker marca entonces el contenedor healthy o unhealthy, permitiendo a los demás servicios reaccionar mediante depends_on: condition: service_healthy.

  3. 3. Límites de recursos

    Bajo deploy.resources.limits, fije cpus y memory (p. ej. memory: 512M). Sin límite, un servicio con fugas puede consumir toda la RAM y hacer que el OOM killer mate a los demás. Añada también reservations para garantizar un mínimo en el arranque.

  4. 4. Secretos fuera del archivo

    Nunca ponga una contraseña en texto plano en el YAML. Referenciarlos mediante env_file: .env o ${VARIABLE}, o use el mecanismo secrets: de Docker que monta el secreto como archivo en /run/secrets/ dentro del contenedor, fuera del alcance de docker inspect. Añada .env a su .gitignore.

  5. 5. Volúmenes con nombre

    Declare sus datos en volúmenes con nombre con un driver explícito en lugar de bind-mounts anónimos. Un volumen con nombre sobrevive a docker compose down; solo down -v lo elimina. Documente cada volumen y asócielo a una estrategia de restauración probada.

  6. 6. Rotación de logs

    Añada un bloque logging: con driver: json-file y opciones max-size: "10m" y max-file: "3". Sin esto, los logs de un contenedor verboso llenan el disco hasta el fallo. Aplíquelo a cada servicio.

  7. 7. Aislamiento de red

    Cree redes con nombre — una frontend para lo expuesto, una backend para la base de datos — y adjunte cada servicio solo a las redes que necesita. Añada internal: true a la red backend para impedir cualquier acceso desde el host o Internet, incluso ante un error de configuración de puertos.

  8. 8. Binding de puertos

    Detrás de un proxy inverso, publique en 127.0.0.1:8080:8080 y no en 8080:8080 (equivalente a 0.0.0.0). Docker modifica directamente las reglas iptables y puede saltarse UFW — el binding en 127.0.0.1 es la única garantía fiable en Compose.

  9. 9. Tags de imagen fijados

    Sustituya image: postgres:latest por una versión precisa (postgres:16.3) o, mejor, por un digest. Combine con pull_policy: missing para un comportamiento predecible: Compose solo descarga una imagen nueva si está ausente en local.

  10. 10. Orden de arranque

    Use depends_on con condition: service_healthy para que un servicio no espere solo el arranque sino la disponibilidad real de su dependencia. Para servicios sin healthcheck nativo, puede usar condition: service_started combinado con un script de espera (wait-for-it.sh o dockerize).

Valores por defecto de dev frente a ajustes de producción

Desplace la tabla

ParámetroPor defecto en devRecomendado en prod
restartnounless-stopped
healthcheckausentedefinido con interval y retries
memoriasin restricciónlímite fijado (p. ej. 512M)
secretostexto plano posible.env o Docker secrets
volúmenesanónimoscon nombre y driver
logssin límitemax-size + max-file
redbridge por defectofrontend / backend separados
puertos0.0.0.0127.0.0.1 detrás del proxy
imagenlatestversión o digest fijado
depends_onsolo arranquecondition: service_healthy

docker-compose o docker compose: la ruptura de Ubuntu 26.04

Dos herramientas llevan casi el mismo nombre. docker-compose, con guion, es Compose v1, escrito en Python: su repositorio lo declara al final de su vida útil desde la disponibilidad general de Compose v2, anunciada por Docker el 26 de abril de 2022. docker compose, con un espacio, es Compose v2, escrito en Go e integrado en la CLI de Docker como plugin. Ubuntu 26.04 completa la transición: el paquete docker-compose ya no está en el archivo desde Ubuntu 25.10, y el paquete docker-compose-v2 solo instala el plugin, en /usr/libexec/docker/cli-plugins/. Tras la actualización, cualquier script que siga llamando al nombre antiguo se detiene en docker-compose: command not found. En la CI, GitHub sacó Compose v1 de sus runners alojados el 9 de julio de 2024, y pasa ubuntu-latest a Ubuntu 26.04 entre el 19 de octubre y el 19 de noviembre de 2026: conviene revisar un workflow que instalaba docker-compose a mano. Hay dos diferencias de comportamiento documentadas. Los nombres de contenedor usan un guion en lugar de un guion bajo (miapp-web-1 frente a miapp_web_1), salvo con --compatibility. Y la clave version: se ignora, con la advertencia «the attribute version is obsolete, it will be ignored».

Migrar sus scripts y añadir un shim

  • Inventario — grep -rnE 'docker-compose( |$)' /opt /etc/cron* /etc/systemd ~/.bashrc lista las llamadas que hay que corregir.
  • Scripts y Makefile — sustituya el guion por un espacio (docker compose up -d); los subcomandos habituales no cambian.
  • cron y systemd — llame a /usr/bin/docker compose con ruta absoluta, porque cron se ejecuta con un PATH reducido.
  • GitHub Actions — pruebe sus workflows con runs-on: ubuntu-26.04 antes del cambio, o fije ubuntu-24.04 mientras los corrige, como recomienda GitHub.
  • Shim temporal — un archivo ejecutable /usr/local/bin/docker-compose de dos líneas, #!/bin/sh y luego exec docker compose "$@", recoge las llamadas olvidadas. Docker mantiene también compose-switch, que traduce la sintaxis v1 a v2.
  • Nombres de contenedor — sustituya los docker exec miapp_web_1 escritos a mano por docker compose exec web, que no depende del separador.
  • Verificación — docker compose version responde, docker compose config pasa sin advertencias, command -v docker-compose solo apunta al shim.

Health checks avanzados: supervisar el estado real del servicio

Un health check bien configurado va más allá de un simple curl: mide el estado funcional del servicio, no solo su disponibilidad de red. Para una API HTTP, pruebe un endpoint ligero (/health o /ready) que devuelva 200 solo si la conexión a la base de datos está establecida. Para PostgreSQL, use pg_isready -U postgres. Para Redis, basta con redis-cli ping. Compruebe el estado de sus contenedores con docker compose ps: la columna STATUS muestra healthy, unhealthy o starting. Un contenedor atascado en starting más allá del start_period indica un problema de inicialización — consulte docker compose logs service. El parámetro start_period define una ventana de gracia durante la cual los fallos no cuentan en los retries, imprescindible para servicios que necesitan tiempo para migrar su esquema.

Secretos Docker sin Swarm: montar archivos en /run/secrets/

Fuera de Swarm, Compose acepta secretos de tipo archivo. Declare el secreto en la sección secrets: de nivel raíz, apuntando a un archivo local (file: ./secrets/db_password.txt), y referéncielo en el servicio con secrets: [db_password]. Docker monta ese archivo en solo lectura en /run/secrets/db_password: la aplicación lo lee como un archivo normal, y su valor no aparece ni en las variables de entorno que muestra docker inspect ni en los logs. Para rotarlo, versione el secreto: cree db_password_v2 junto a db_password_v1, haga que el servicio lea v2, redespliéguelo solo con docker compose up -d --no-deps service y elimine v1 una vez validado el despliegue. La interrupción se limita a recrear ese contenedor, y el secreto anterior sigue disponible para volver atrás.

Rollback: volver atrás tras un despliegue fallido

El rollback de una stack Compose se prepara antes del despliegue, no después del incidente. Mantenga la imagen anterior etiquetada y disponible en local: añada un tag explícito miapp:stable antes de cada actualización. Para volver atrás, restablezca el tag anterior en el archivo Compose (o en el .env si la versión es una variable) y relance docker compose up -d. Los volúmenes no se ven afectados por un cambio de imagen, lo que hace rápido el rollback aplicativo; una migración de esquema, en cambio, necesita su migración inversa lista antes del despliegue. Para publicaciones críticas, separe docker-compose.yml (versión estable) y docker-compose.override.yml (versión candidata): docker compose -f docker-compose.yml up -d ignora el override y vuelve al estado estable.

Pruebe siempre su stack endurecida en local antes de llevarla a producción: docker compose config para validar la sintaxis, luego docker compose up y simule un reboot con docker compose restart. Verifique que los contenedores vuelven healthy y que los datos persisten tras un down seguido de un up.

Solución de problemas: 6 errores comunes y sus soluciones

docker: unknown command: docker compose: el plugin Compose no está instalado. En Ubuntu, instale docker-compose-v2 desde el archivo, o docker-compose-plugin si Docker viene del repositorio oficial de Docker, y compruebe con docker compose version. network not found: tras un docker compose down, las redes con nombre se eliminan. Solución: declare las redes compartidas como external: true o sincronice el reinicio de las stacks dependientes. port is already in use: otro proceso ocupa el puerto. Identifíquelo con ss -tlnp | grep :PORT y deténgalo antes de relanzar. OOM (Out Of Memory): el kernel mata un contenedor sin aviso — busque OOMKilled: true en docker inspect container_id. Aumente el límite de memoria o identifique la fuga con docker stats. permission denied on volume: el proceso del contenedor corre con un UID sin acceso al directorio montado. Compruebe el UID con docker compose exec service id y ajuste los permisos o añada user: "UID:GID". Contenedor reiniciándose en bucle: docker compose logs --tail=50 service revela el error de arranque. Causas más frecuentes: variable de entorno ausente, archivo de configuración no encontrado o dependencia no lista.

Copias de seguridad de volúmenes sin corrupción

Copiar archivos de un volumen PostgreSQL o MySQL en caliente con rsync o tar produce casi siempre una copia corrupta. La regla es hacer siempre un dump SQL antes del snapshot del volumen: pg_dump o mysqldump producen un estado coherente. Herramientas como Restic y Offen Docker Backup automatizan este proceso. Documente cada volumen con nombre y asócielo a una estrategia de restauración probada.

Conclusión

Estos diez ajustes transforman un archivo Compose de desarrollo en una stack capaz de sobrevivir a reinicios, picos de carga y noches sin supervisión. Los health checks, los secretos montados como archivo, un rollback preparado y el paso completo a docker compose completan esta base. Nada de esto requiere una herramienta adicional: todo cabe en el YAML y los scripts que ya tiene. Repase la checklist antes de cada publicación, por ejemplo con una revisión de docker compose config integrada en el despliegue. Después podrá añadir un proxy inverso como Traefik o Caddy, o una capa de orquestación como Coolify.

Despliegue su stack Compose en producción

Un VPS ServOrbit le da el acceso root, la RAM y el control de red necesarios para hacer funcionar una stack Docker Compose endurecida. Elija su configuración y despliegue en unos minutos.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva