[{"data":1,"prerenderedAt":177},["ShallowReactive",2],{"seo-verification":3,"blog-docker-compose-healthcheck-postgresql-es":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-docker-compose-healthcheck-postgresql-es",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":121,"ctaBody":122,"ctaButton":123,"ctaUrl":124,"relatedPosts":125},282,"docker-compose-healthcheck-postgresql",{"fr":12,"en":13,"ar":14,"es":10},"docker-compose-depends-on-healthcheck","depends-on-is-not-enough-postgresql-healthcheck-in-compose","depends-on-لا-يكفي-healthcheck-لـ-postgresql-في-compose","depends_on no basta: healthcheck de PostgreSQL en Compose","Por qué `depends_on` por sí solo no garantiza que PostgreSQL esté listo, y cómo configurar un healthcheck fiable con `service_healthy` para evitar las race conditions.",8,0,false,"2026-08-19T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},3,"Despliegue","deploiement","bg-success\u002F10 text-success",[28],{"id":23,"name":24,"slug":25,"color":26,"icon":25},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fdocker-compose-depends-on-healthcheck-poster.svg","Su stack Docker arranca, la base de datos pasa al estado `running` — y su aplicación falla de inmediato con `connection refused` o `FATAL: role does not exist`. La causa es casi siempre la misma: `depends_on` por defecto espera a que el contenedor arranque, no a que el servicio esté operativo. Esta guía le muestra cómo configurar un healthcheck de PostgreSQL fiable en `docker-compose.yml` para eliminar esa race condition de una vez por todas.",[34,38,48,51,79,111,115,118],{"type":35,"title":36,"body":37},"h2","Por qué `depends_on` falla por defecto","Por defecto, `depends_on` usa la condición `service_started`. Esto significa que Docker solo espera a que el contenedor de destino esté **lanzado** — es decir, a que su proceso principal haya arrancado. Eso no dice nada sobre el estado interno del servicio.\n\nPostgreSQL, como la mayoría de las bases de datos, atraviesa varias fases durante la inicialización: la imagen oficial ejecuta scripts de arranque, crea los roles, inicializa las extensiones y prepara el clúster antes de empezar a aceptar conexiones. Esta secuencia puede tardar desde unos segundos hasta más de treinta segundos en un VPS con un disco cargado, un volumen sin preparar o un conjunto grande de extensiones.\n\nMientras tanto, su aplicación — que sí respeta la directiva `depends_on` — ya intenta conectarse, y recibe un rechazo seco.",{"type":39,"title":40,"items":41},"ul","Las consecuencias prácticas de una race condition en el arranque",[42,43,44,45,46,47],"**`connection refused`** — el socket TCP de PostgreSQL todavía no está abierto y la aplicación falla en la primera llamada PDO o SQLAlchemy.","**`FATAL: role does not exist`** — PostgreSQL escucha, pero los scripts de inicialización (`docker-entrypoint-initdb.d`) todavía no han creado el rol ni la base de datos.","**`FATAL: the database system is starting up`** — el clúster está recuperándose tras una parada limpia; las conexiones se rechazan temporalmente.","**Crash loop silencioso** — con `restart: unless-stopped`, Docker relanza la aplicación indefinidamente, los logs se repiten y el problema se toma por un error de la aplicación.","**Falsos positivos en CI** — las pruebas de integración fallan de forma intermitente según la velocidad de arranque del runner.","**Dependencias en cascada** — una API que depende de una aplicación que depende de la base hereda el mismo problema si la cadena `depends_on` no es correcta de forma uniforme.",{"type":35,"title":49,"body":50},"Requisitos previos: Docker Compose v2 y el plugin oficial","La condición `service_healthy` **no** está disponible en Docker Compose v1 (el binario `docker-compose` en Python, hoy obsoleto). Se admite desde **Docker Compose v2**, distribuido como plugin en Go bajo el comando `docker compose` (sin guion).\n\nPara comprobar su versión:\n\n```bash\ndocker compose version\n```\n\nLa salida debe mostrar `Docker Compose version v2.x.x` o superior. En Debian 12 y Ubuntu 22.04+, el plugin está disponible en los repositorios oficiales de Docker. Si todavía usa `docker-compose` (v1), migre: el proyecto está archivado y ya no recibe parches de seguridad.\n\nNo hace falta ninguna dependencia adicional para PostgreSQL: `pg_isready` es una herramienta nativa de la imagen oficial `postgres`, presente en todos los tags desde hace años.",{"type":52,"title":53,"steps":54},"steps","Configurar un healthcheck de PostgreSQL fiable, paso a paso",[55,58,61,64,67,70,73,76],{"title":56,"body":57},"Entender service_started frente a service_healthy","`depends_on` acepta tres condiciones:\n\n- `service_started` (por defecto) — espera a que el contenedor simplemente haya arrancado.\n- `service_healthy` — espera a que el healthcheck del contenedor devuelva `healthy`.\n- `service_completed_successfully` — para contenedores de vida corta (jobs, migraciones).\n\nPara cualquier base de datos, `service_healthy` es la única condición que garantiza que el servicio acepta conexiones.",{"title":59,"body":60},"Escribir el healthcheck de PostgreSQL en el servicio `db`","Añada el bloque `healthcheck` directamente en la definición del servicio `db`:\n\n```yaml\nservices:\n  db:\n    image: postgres:16\n    environment:\n      POSTGRES_USER: app\n      POSTGRES_PASSWORD: secret\n      POSTGRES_DB: appdb\n    healthcheck:\n      test: [\"CMD\", \"pg_isready\", \"-U\", \"app\", \"-d\", \"appdb\"]\n      interval: 5s\n      timeout: 5s\n      retries: 5\n      start_period: 30s\n```\n\nEl campo `test` recibe una lista: el primer elemento es `CMD` (Docker ejecuta el comando directamente), seguido de los argumentos. `pg_isready` devuelve `0` si PostgreSQL está listo para aceptar conexiones con el usuario y la base indicados, y un código distinto de cero en caso contrario — lo que Docker interpreta como `healthy` o `unhealthy`.",{"title":62,"body":63},"Entender el papel de `start_period`","`start_period` es la **ventana de gracia** concedida al contenedor para inicializarse antes de que los fallos del healthcheck empiecen a contabilizarse en `retries`. Durante esa ventana, Docker sí ejecuta el healthcheck, pero un fallo no incrementa el contador.\n\nSin `start_period`, un PostgreSQL que tarda 15 segundos en inicializarse fallaría sus 5 primeras comprobaciones (`interval: 5s` × 5 intentos = 25 segundos) y pasaría a `unhealthy` antes incluso de estar operativo.\n\nEl valor recomendado son **30 segundos** para un PostgreSQL estándar: lo bastante largo para absorber las inicializaciones lentas (primer arranque con volumen vacío, extensiones pesadas) sin retrasar innecesariamente el arranque en régimen permanente. `interval` y `start_period` son distintos: `interval` marca el ritmo de las comprobaciones en régimen normal, `start_period` protege la fase de arranque.",{"title":65,"body":66},"Escribir el `depends_on` con `condition: service_healthy`","En cada servicio que dependa de la base de datos, sustituya la forma corta de `depends_on` por la forma larga con condición:\n\n```yaml\nservices:\n  app:\n    image: monapp:latest\n    depends_on:\n      db:\n        condition: service_healthy\n    environment:\n      DATABASE_URL: postgresql:\u002F\u002Fapp:secret@db:5432\u002Fappdb\n```\n\nCon esta configuración, Docker espera a que el healthcheck del servicio `db` devuelva `healthy` antes de arrancar `app`. Si `db` pasa a `unhealthy` tras `retries` fallos, `app` no arranca.",{"title":68,"body":69},"Probar con `docker compose up`","Lance la stack y observe la secuencia:\n\n```bash\ndocker compose up\n```\n\nVerá en los logs líneas de este tipo:\n\n```\ndb  | database system is ready to accept connections\napp | Waiting for db to be healthy...\napp | Starting application server\n```\n\nPara comprobar el estado del healthcheck en cualquier momento:\n\n```bash\ndocker inspect \u003Cnom_du_conteneur_db> | grep -A 5 '\"Health\"'\n```\n\nLa salida indica `Status: healthy`, `starting` o `unhealthy`, y lista las últimas salidas de la comprobación.",{"title":71,"body":72},"Caso Redis: healthcheck adaptado","Redis no dispone de `redis-isready`, pero su equivalente es `redis-cli ping`:\n\n```yaml\n  redis:\n    image: redis:7-alpine\n    healthcheck:\n      test: [\"CMD\", \"redis-cli\", \"ping\"]\n      interval: 5s\n      timeout: 3s\n      retries: 5\n      start_period: 10s\n```\n\n`redis-cli ping` devuelve `PONG` y el código de salida `0` si Redis acepta conexiones. Como Redis arranca más rápido que PostgreSQL, `start_period: 10s` suele ser suficiente.",{"title":74,"body":75},"Caso MySQL \u002F MariaDB: `mysqladmin ping`","Para MySQL o MariaDB, use `mysqladmin ping`:\n\n```yaml\n  mysql:\n    image: mariadb:11\n    environment:\n      MYSQL_ROOT_PASSWORD: secret\n      MYSQL_DATABASE: appdb\n    healthcheck:\n      test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\", \"-u\", \"root\", \"-psecret\"]\n      interval: 5s\n      timeout: 5s\n      retries: 5\n      start_period: 30s\n```\n\nAtención: la contraseña se concatena directamente a `-p` sin espacio (`-psecret`), es el comportamiento esperado de `mysqladmin`. Este comando aparece en `docker inspect`, así que prefiera un secret de Compose o una variable de entorno si la confidencialidad es una restricción.",{"title":77,"body":78},"Resolución de problemas: cuatro errores frecuentes","**`pg_isready: command not found`** — no está usando la imagen oficial `postgres` (ni una imagen derivada que la incluya). Compruébelo con `docker compose exec db which pg_isready`.\n\n**Healthcheck en bucle, nunca `healthy`** — el `test` nunca devuelve `0`. Pruébelo manualmente: `docker compose exec db pg_isready -U app -d appdb`. Si el comando falla, revise las variables de entorno `POSTGRES_USER` y `POSTGRES_DB`.\n\n**`start_period` demasiado corto** — en un primer arranque con un volumen vacío, PostgreSQL puede tardar más de 30 segundos. Auméntelo a `60s` u observe los logs: `database system was shut down at … LOG:  database system is ready to accept connections` indica el retraso real.\n\n**Contenedor en `unhealthy` permanente** — tras `retries` fallos, Docker marca el contenedor como `unhealthy` pero no lo reinicia (eso es tarea de `restart`). Consulte `docker inspect` para ver la salida de las últimas comprobaciones e identificar el comando que falla.",{"type":80,"title":81,"headers":82,"rows":86},"comparison","Comportamiento por defecto frente a healthcheck",[83,84,85],"Caso","Comportamiento por defecto (`service_started`)","Con `service_healthy`",[87,91,95,99,103,107],[88,89,90],"Primer arranque, volumen vacío","La app arranca antes de que la base esté lista → crash loop","La app espera a que PostgreSQL se inicialice y acepte conexiones",[92,93,94],"Reinicio tras una parada limpia","La app puede arrancar durante la fase de recovery de PostgreSQL","La app queda en espera hasta el final de la recovery",[96,97,98],"Base lenta (extensiones, init pesado)","Race condition según la velocidad del host","Sin race condition: el healthcheck valida el estado real",[100,101,102],"Dependencias en cascada (app → worker → db)","Cada eslabón debe gestionar por su cuenta los reintentos de conexión","La cadena de condiciones garantiza el orden de arranque",[104,105,106],"Pruebas de integración en CI","Resultados intermitentes según la velocidad del runner","Resultados deterministas",[108,109,110],"Redis o MySQL en lugar de PostgreSQL","El mismo problema: `depends_on` por defecto no distingue","La misma solución, con el comando de comprobación adaptado a cada motor",{"type":112,"title":113,"body":114},"tip","`pg_isready` o `SELECT 1`: ¿cuál elegir?","En la práctica se ven a menudo dos variantes de healthcheck de PostgreSQL:\n\n- `[\"CMD\", \"pg_isready\", \"-U\", \"postgres\"]`\n- `[\"CMD-SHELL\", \"psql -U postgres -c 'SELECT 1'\"]\"`\n\n`pg_isready` es **más fiable** por una razón sencilla: solo comprueba la capacidad del servidor para aceptar conexiones TCP, sin abrir una sesión SQL. Devuelve `0` en cuanto el servidor escucha y acepta el saludo de conexión, que es exactamente lo que una aplicación necesita para intentar su propia conexión.\n\n`SELECT 1` mediante `psql` abre una sesión SQL real y ejecuta una consulta. Es una prueba más profunda, pero puede fallar por motivos ajenos a la disponibilidad del servidor (cuota de conexiones alcanzada, `pg_hba.conf` mal configurado). Para un healthcheck, la prueba mínima y directa es preferible.",{"type":112,"title":116,"body":117},"Adaptar el healthcheck a Redis y MySQL","Para Redis, sustituya `pg_isready` por `CMD redis-cli PING` — el comando devuelve `PONG` en cuanto el servidor acepta conexiones. Para MySQL o MariaDB, use `CMD mysqladmin ping -h localhost -u root --password=$$MYSQL_ROOT_PASSWORD`: ajuste `start_period: 60s`, porque la inicialización de una base MySQL tarda más que la de PostgreSQL. El patrón `condition: service_healthy` es idéntico sea cual sea el servicio de destino.",{"type":35,"title":119,"body":120},"Enlaces internos y próximos pasos","El healthcheck `service_healthy` es uno de los ajustes de robustez que conviene activar en producción. Otros puntos merecen la misma atención antes de un despliegue duradero: la política de reinicio `restart: unless-stopped`, los límites de recursos `deploy.resources.limits` y la rotación de logs `logging.options`. Encontrará la lista de comprobación completa en **Docker Compose en producción: 10 puntos que verificar**.\n\nSi su stack crece — varios servicios, varios hosts — un reverse proxy como Caddy o Traefik se vuelve imprescindible para gestionar el enrutamiento HTTPS. La guía **Caddy, Traefik o Nginx Proxy Manager** detalla los criterios de elección según su perfil.\n\nPara automatizar el despliegue de toda la infraestructura (VPS, Docker, configuración) de forma reproducible, **Ansible para automatizar sus servidores VPS** le guiará paso a paso.","Aloje su stack Docker en un VPS dedicado","Un VPS ServOrbit le da acceso root, una IPv4 dedicada y los recursos necesarios para ejecutar sus stacks Docker Compose en producción. Despliegue en unos minutos.","Desplegar mi stack en un VPS","\u002Fvps-cloud",[126,140,158],{"id":127,"slug":128,"slugs":129,"title":133,"excerpt":134,"readTime":17,"views":18,"isPinned":19,"publishedAt":135,"updatedAt":21,"category":136,"categories":137,"featuredImage":29,"bgImage":30,"posterImage":139,"relatedSolution":29},229,"checklist-docker-compose-en-produccion",{"fr":130,"en":131,"ar":132,"es":128},"docker-compose-production-checklist","docker-compose-in-production-10-point-checklist","docker-compose-في-الإنتاج-قائمة-التحقق-من-10-نقاط","Docker Compose en producción: checklist de 10 puntos","Checklist de Docker Compose en producción: 10 ajustes esenciales, gestión de secretos sin downtime, copias de volúmenes sin corrupción, CVE-2026-17106.","2026-08-06T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},[138],{"id":23,"name":24,"slug":25,"color":26,"icon":25},"\u002Fblog\u002Fcovers\u002Fdocker-compose-production-checklist-poster.svg",{"id":141,"slug":142,"slugs":143,"title":147,"excerpt":148,"readTime":149,"views":18,"isPinned":19,"publishedAt":135,"updatedAt":21,"category":150,"categories":155,"featuredImage":29,"bgImage":30,"posterImage":157,"relatedSolution":29},227,"elegir-reverse-proxy-vps-caddy-traefik-nginx",{"fr":144,"en":145,"ar":146,"es":142},"choisir-reverse-proxy-vps-caddy-traefik-nginx","caddy-traefik-or-nginx-proxy-manager-which-to-choose","caddy-أم-traefik-أم-nginx-proxy-manager-أيهما-تختار","Caddy, Traefik o Nginx Proxy Manager: ¿cuál elegir?","Guía para elegir entre Caddy, Traefik y Nginx Proxy Manager en un VPS con Docker: criterios, tabla comparativa y recomendaciones según su perfil.",10,{"id":151,"name":152,"slug":153,"color":154,"icon":153},5,"Comparativas","comparatif","bg-info\u002F10 text-info",[156],{"id":151,"name":152,"slug":153,"color":154,"icon":153},"\u002Fblog\u002Fcovers\u002Fchoisir-reverse-proxy-vps-caddy-traefik-nginx-poster.svg",{"id":159,"slug":160,"slugs":161,"title":165,"excerpt":166,"readTime":149,"views":167,"isPinned":19,"publishedAt":168,"updatedAt":21,"category":169,"categories":174,"featuredImage":29,"bgImage":30,"posterImage":176,"relatedSolution":29},236,"automatizar-servidores-vps-con-ansible",{"fr":162,"en":163,"ar":164,"es":160},"ansible-automatiser-serveurs-vps","automating-vps-server-management-with-ansible","أتمتة-إدارة-خوادم-vps-باستخدام-ansible","Automatizar la gestión de servidores VPS con Ansible","Automatice la gestión de una flota de servidores VPS con Ansible: inventario, playbooks, roles y Vault para una infraestructura reproducible.",1,"2026-08-08T00:00:00+00:00",{"id":170,"name":171,"slug":172,"color":173,"icon":172},2,"Automatización","automatisation","bg-brand-action\u002F10 text-brand-action",[175],{"id":170,"name":171,"slug":172,"color":173,"icon":172},"\u002Fblog\u002Fcovers\u002Fansible-automatiser-serveurs-vps-poster.svg",1789665019916]