DevOps y Contenedores 5 min lectura

Healthchecks en Docker Compose: cómo evitar que tus servicios arranquen antes de tiempo

Aprende a configurar healthchecks reales en Docker Compose para que tu app no intente conectarse a una base de datos que todavía está arrancando. Guía práctica con ejemplos para PostgreSQL, MySQL, Redis y MongoDB, y el uso correcto de depends_on con condition: service_healthy.

Por Equipo Starbyte

Healthchecks en Docker Compose: cómo evitar que tus servicios arranquen antes de tiempo

Healthchecks en Docker Compose: cómo evitar que tus servicios arranquen antes de tiempo

Uno de los errores más frecuentes con Docker Compose es asumir que si un contenedor arrancó, el servicio dentro ya está listo. No es así. Tu contenedor de PostgreSQL puede estar "running" mientras el motor de base de datos todavía está inicializando. Si tu app intenta conectarse en ese momento, falla.

Los healthchecks resuelven exactamente eso: le dicen a Docker cuándo un servicio está realmente listo para recibir conexiones.

Qué es un healthcheck y cómo funciona

Un healthcheck es un comando que Docker ejecuta periódicamente dentro del contenedor para verificar si el servicio responde. Según el resultado, Docker marca el contenedor con uno de estos estados:

Estado Significado
starting El contenedor arrancó pero aún no pasa el primer chequeo
healthy El comando de healthcheck retorna código 0
unhealthy El comando falla después de agotar los reintentos

Anatomía de un healthcheck

healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

Cada parámetro cumple un rol específico:

  • test: el comando que determina si el servicio está sano. Retorna 0 (healthy) o 1 (unhealthy).
  • interval: cada cuánto se ejecuta el chequeo.
  • timeout: tiempo máximo que puede tardar el comando antes de considerarse fallido.
  • retries: cuántas veces puede fallar antes de marcarse como unhealthy.
  • start_period: ventana de gracia inicial donde los fallos no cuentan. Fundamental para servicios que tardan en arrancar.

Healthchecks para los servicios más comunes

PostgreSQL

db:
  image: postgres:16
  environment:
    POSTGRES_PASSWORD: ${DB_PASSWORD}
    POSTGRES_DB: miapp
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U postgres -d miapp"]
    interval: 5s
    timeout: 5s
    retries: 5
    start_period: 30s

pg_isready es una utilidad nativa de PostgreSQL diseñada específicamente para verificar si el servidor acepta conexiones.

MySQL

mysql:
  image: mysql:8
  environment:
    MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 30s

Redis

redis:
  image: redis:7-alpine
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 5s
    timeout: 3s
    retries: 5

Redis arranca rápido, así que normalmente no necesita start_period.

MongoDB

mongo:
  image: mongo:7
  healthcheck:
    test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 30s

La pieza clave: depends_on con condition

Definir healthchecks sin conectarlos a las dependencias no sirve de mucho. La magia está en combinarlos con depends_on usando la forma larga:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secreto
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 30s

  cache:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_healthy
    ports:
      - "8000:8000"

Con esta configuración, Docker Compose no arrancará el servicio app hasta que tanto db como cache pasen sus healthchecks. Es la diferencia entre "el contenedor existe" y "el servicio funciona".

Patrón avanzado: migraciones antes de arrancar la app

Si necesitas ejecutar migraciones de base de datos antes de que tu app arranque, puedes usar service_completed_successfully:

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 30s

  migrate:
    build: .
    command: npm run migrate
    depends_on:
      db:
        condition: service_healthy

  app:
    build: .
    depends_on:
      migrate:
        condition: service_completed_successfully
      db:
        condition: service_healthy

La secuencia queda así: primero arranca db, cuando está healthy arranca migrate, y cuando migrate termina exitosamente arranca app.

Errores comunes

No usar start_period: sin esta ventana de gracia, los primeros chequeos fallidos cuentan como reintentos. Una base de datos que necesita 20 segundos para inicializar se marcará como unhealthy antes de tener oportunidad de arrancar. Siempre configura un start_period generoso para bases de datos.

Chequear solo que el puerto está abierto: un curl al puerto no garantiza que el servicio esté listo. Usa las herramientas nativas de cada servicio (pg_isready, redis-cli ping, mysqladmin ping).

Usar la forma corta de depends_on: escribir depends_on: [db] solo garantiza orden de arranque, no espera al healthcheck. Siempre usa la forma larga con condition: service_healthy.

Olvidar que version ya no es necesario: en Docker Compose V2, el campo version en el YAML está deprecado y se ignora. Puedes eliminarlo de tus archivos.

Cómo depurar healthchecks

Si un servicio se queda en estado unhealthy, estas herramientas te ayudarán:

# Ver el estado de salud de todos los contenedores
docker compose ps

# Inspeccionar el historial de healthchecks de un contenedor
docker inspect --format='{{json .State.Health}}' nombre_contenedor | jq

# Ejecutar manualmente el comando de healthcheck dentro del contenedor
docker compose exec db pg_isready -U postgres

# Ver logs del servicio problemático
docker compose logs db

Requisitos de versión

Los healthchecks con condition: service_healthy requieren Docker Compose V2 versión 2.20.0 o superior. Verifica tu versión con:

docker compose version

Idea clave

Un contenedor en estado "running" no es un servicio listo. Los healthchecks transforman Docker Compose de una herramienta que solo ordena arranques en una que realmente orquesta dependencias. Configurarlos lleva 5 minutos por servicio y elimina la categoría entera de errores de "mi app arrancó antes que la base de datos".

Etiquetas: #docker #docker-compose #healthcheck #devops #postgresql #redis #mysql #mongodb #contenedores #microservicios