ToolsOps

Docker Compose healthchecks: depends_on, secretos y .env

Cómo escribir healthchecks correctos en Docker Compose, usar depends_on con condition: service_healthy, gestionar secretos con .env.example y no exponer puertos de base de datos.

Compose moderno: sin version:

En Docker Compose v2 la clave version: está obsoleta. Compose la ignora y emite un aviso. Los archivos modernos empiezan directamente por services: y, si hace falta, declaran volumes: y networks: al mismo nivel.

Healthchecks por servicio

Un healthcheck le dice a Docker si un servicio está realmente listo, no solo si el proceso arrancó. Es la base para que depends_on pueda esperar de forma fiable. Los comandos referencian variables del propio contenedor con $$VAR: Compose convierte $$ en un único $, que el shell del contenedor evalúa en tiempo de ejecución (donde la variable del servicio sí existe).

  • PostgreSQL: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB
  • MySQL: mysqladmin ping -h localhost -u $$MYSQL_USER -p$$MYSQL_PASSWORD
  • Redis: redis-cli ping (con -a "$$REDIS_PASSWORD" si hay contraseña)
  • HTTP / web: wget --no-verbose --tries=1 --spider http://localhost/ || exit 1

depends_on con condition: service_healthy

La forma corta de depends_on (una lista) solo garantiza el orden de arranque. Para esperar a que la dependencia esté sana, usa la forma larga:

services:
  app:
    build: .
    depends_on:
      postgres:
        condition: service_healthy
  postgres:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s

Secretos y .env.example

Nunca pongas contraseñas literales en el compose.yaml: acaban en el repositorio y en el historial de git. Usa ${VARIABLE} en environment y define los valores en un .env que no se commitea. Versiona solo un .env.example con placeholders como CHANGE_ME para documentar qué variables hacen falta.

Puertos de base de datos: local frente a producción

Publicar 5432:5432 o 3306:3306 expone la base de datos a toda la red del host. En desarrollo, si necesitas conectarte desde el host, bindea a loopback (127.0.0.1:5432:5432). En producción, mantén la base de datos en la red interna de Compose y no publiques su puerto: los demás servicios la alcanzan por su nombre.

Volúmenes y backups

Los datos de una base de datos deben vivir en un volumen con nombre (por ejemplo postgres_data:/var/lib/postgresql/data) para que sobrevivan a docker compose down. Un volumen no es un backup: programa volcados periódicos (pg_dump, mysqldump) y guárdalos fuera del host. Para Nginx como reverse proxy, monta la configuración en solo lectura (./nginx.conf:/etc/nginx/nginx.conf:ro).

Siguientes pasos

Genera un compose.yaml de partida y valida el tuyo con el generador y validador de Docker Compose. Construye healthchecks listos para pegar y comprueba que tu Compose no expone puertos ni secretos por error.

Preguntas frecuentes

¿Cómo escribo un healthcheck para Postgres en Docker Compose?
Usa pg_isready dentro del propio contenedor: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]. El doble $$ le dice a Compose que pase un único $ al contenedor, donde POSTGRES_USER y POSTGRES_DB existen como variables de entorno del servicio. Acompáñalo de interval, timeout, retries y start_period.
¿Por qué depends_on no espera a que la base de datos esté lista?
La forma corta de depends_on (una lista de nombres) solo espera a que el contenedor arranque, no a que el servicio acepte conexiones. Para esperar de verdad necesitas un healthcheck en el servicio destino y la forma larga: depends_on con condition: service_healthy.
¿Dónde pongo las contraseñas de mi compose.yaml?
En un archivo .env que NO se commitea. En el compose.yaml referencia las variables con ${VARIABLE} y versiona solo un .env.example con placeholders (por ejemplo CHANGE_ME). Así el secreto real nunca entra en git ni en su historial.
¿Debo exponer el puerto de la base de datos?
En producción, no: deja la base de datos en la red interna de Compose y conéctate desde los otros servicios por su nombre. En desarrollo, si necesitas acceder desde el host, bindea a 127.0.0.1 (127.0.0.1:5432:5432) para no abrirla a toda la red.
¿Hace falta la clave version: en Compose moderno?
No. En Docker Compose v2 la clave version: está obsoleta: se ignora y genera un aviso. Empieza el archivo directamente por services:.