Docker en Up pero la app caída: guía para diagnosticar
En pocas palabras: Tu contenedor figura en Up porque Docker solo confirma que el proceso principal corre, no que la aplicación funcione. Para encontrar la falla, leé primero los logs con docker logs y después revisá inspect, recursos, puertos, red y el healthcheck, que da sano con exit code 0.
Para diagnosticar un contenedor Docker que figura en Up pero cuya aplicación no responde, hay que leer primero los logs y después revisar estado, puertos, recursos, red y healthcheck. Según una guía de dev.to del 14 de octubre de 2026, Docker solo confirma que el proceso principal corre.
Un contenedor Docker en estado running (Up en docker ps) es un contenedor cuyo proceso principal sigue ejecutándose. Un contenedor healthy es uno cuyo HEALTHCHECK, una prueba que vos definís en el Dockerfile o en Compose, devuelve éxito de forma periódica. Son dos señales distintas: la primera la da Docker solo, la segunda depende de que alguien haya escrito la prueba y de qué verifique esa prueba.
En este artículo:
- En 30 segundos
- ¿Qué diferencia hay entre un contenedor running y una aplicación healthy?
- ¿Cómo diagnosticar un contenedor Docker empezando por los logs?
- ¿Cómo revisar puertos, recursos y red de un contenedor que no responde?
- ¿Cómo configurar un HEALTHCHECK en un Dockerfile?
- ¿Por qué depends_on no garantiza que la base de datos esté lista?
- ¿Cómo armar un stack de monitoreo de Docker con Prometheus, Grafana y cAdvisor?
- ¿Cómo verificar que el arreglo funcionó de verdad?
- Errores comunes al diagnosticar contenedores
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Running solo indica que el proceso principal está vivo; la aplicación puede estar caída igual.
- El orden de la guía: estado, logs, inspect, recursos, puertos, red, prueba del endpoint, arreglo y verificación.
- Un HEALTHCHECK usa exit code 0 para sano y 1 para un chequeo fallido, según la guía de Netdata.
- depends_on ordena el arranque pero no espera a que la base esté lista; para eso va condition: service_healthy.
¿Qué diferencia hay entre un contenedor running y una aplicación healthy?
Running quiere decir que el proceso principal del contenedor no terminó. Healthy quiere decir que una prueba definida por vos pasa. Entre una cosa y la otra caben varios fallos que Docker no ve solo, y la guía de dev.to los enumera.
- Base de datos inalcanzable. La app arranca pero no logra conectarse.
- HTTP 500. La API responde, pero con errores.
- Puerto equivocado. La app escucha en uno y vos publicaste otro.
- Variable de entorno faltante. El proceso vive y la configuración está incompleta.
- Lentitud o dependencia caída. Todo “anda”, pero nadie puede usarlo.
Eso sí: la guía describe un escenario ilustrativo. Reiniciar y reconstruir la imagen no arregla nada porque el problema no estaba en el contenedor sino en lo que la app necesita alrededor.
¿Cómo diagnosticar un contenedor Docker empezando por los logs?
Con docker logs sobre el contenedor, antes de tocar nada. La guía recomienda leerlos primero porque suelen mostrar la causa raíz más rápido que cualquier otro paso. Más contexto en proteger tus contenedores con un WAF como SafeLine.
docker logs my-app
docker logs -f my-app
docker logs --tail 100 my-app
docker logs -t my-appEn orden: logs completos, seguimiento en vivo, últimas 100 líneas y con marcas de tiempo. Los mensajes que la fuente manda a buscar son Connection refused, Database connection failed, Address already in use, Permission denied y variable de entorno faltante.
Si los logs no dicen nada, el siguiente paso es docker inspect. Con docker inspect -f '{.State.Status}' my-app ves el estado, con .State.ExitCode el código de salida y con .State.Health.Status la salud. Ojo con este último: si no configuraste un healthcheck, devuelve vacío.
¿Cómo revisar puertos, recursos y red de un contenedor que no responde?
Con cuatro comandos: docker port para los puertos publicados, docker stats para CPU y memoria, y docker network ls más docker network inspect para la red. El mapeo es -p HOST_PORT:CONTAINER_PORT, y publicar un puerto no hace que la app escuche en él.
| Síntoma | Comando | Qué revela |
|---|---|---|
| No sabés si el contenedor existe o se detuvo | docker ps -a | Estado de todos los contenedores, también los parados |
| Se cayó y no sabés por qué | docker inspect -f '{.State.ExitCode}' my-app | Código de salida del proceso |
| Querés saber si Docker lo ve sano | docker inspect -f '{.State.Health.Status}' my-app | starting, healthy o unhealthy (vacío sin healthcheck) |
| El navegador no llega a la app | docker port my-app | Puertos publicados en el host |
| Lentitud o cuelgues | docker stats | CPU y memoria contra el límite |
| La app no encuentra la base | docker network inspect my-network | Qué contenedores comparten red |

La guía muestra un ejemplo hipotético: my-app con 95% de CPU y 480MiB sobre un límite de 512MiB. Su consejo es investigar antes de subir límites, porque el origen puede ser una pérdida de memoria, tráfico excesivo o código ineficiente. Y una advertencia que comparto: docker system prune -a no es un paso de diagnóstico, puede borrar imágenes que todavía necesitás. Ya lo cubrimos antes en instalación de Docker Desktop en Windows.
Dato complementario que no viene de las fuentes principales y conviene validar en la documentación de Docker: el campo State.OOMKilled y el exit code 137 apuntan a que el kernel mató el contenedor por falta de memoria.
¿Cómo configurar un HEALTHCHECK en un Dockerfile?
Se agrega la instrucción HEALTHCHECK con un comando que Docker ejecuta cada tanto dentro del contenedor. La referencia oficial del Dockerfile la lista como la instrucción que chequea la salud del contenedor. Este es el ejemplo de la guía, con nginx:alpine:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
HEALTHCHECK --interval=30s \
--timeout=5s \
--start-period=10s \
--retries=3 \
CMD wget -q -O /dev/null http://127.0.0.1/ || exit 1Los estados posibles son starting, healthy y unhealthy. El exit code 0 es sano y el 1 indica un chequeo fallido, según la guía de Netdata.
Los detalles de temporización los explica la guía de Netdata: el primer chequeo se dispara después de --interval segundos y no al arrancar, el default de --retries es 3, un comando que excede --timeout recibe SIGKILL y cuenta como fallo, y durante --start-period los fallos no suman. También avisa que --start-interval exige Compose v2.20.2+ y Engine 25.0+.
El límite que importa: un healthcheck vale lo que valga la prueba. Un 200 en la raíz de nginx no demuestra que el login, el checkout o la conexión a la base funcionen (esto es una opinión mía, no de la fuente).
¿Por qué depends_on no garantiza que la base de datos esté lista?
Porque Compose espera a que el contenedor de la base esté corriendo, no a que acepte conexiones. La documentación de Compose lo dice así: “Compose does not wait until a container is ‘ready’, only until it’s running”. Postgres puede estar en Up y todavía inicializándose, y tu app, que ya arrancó, falla al conectar.
La solución oficial combina un healthcheck en la base con condition: service_healthy en el servicio que depende. Armé este ejemplo de aprendizaje a partir de la guía de dev.to (app y postgres:17, conexión por nombre de servicio) y del patrón de Compose: Sobre eso hablamos en pipelines ETL con Airflow y BigQuery.
services:
app:
build: .
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://app:password@db:5432/appdb
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
retries: 5
start_period: 30s
timeout: 10sEsto no es una configuración de producción. La propia guía pide secrets en lugar de contraseñas escritas a mano, almacenamiento persistente para los datos y lógica de reintentos en la aplicación. Sobre ese último punto coincido: ponele que la base se reinicia a las tres de la mañana, el orden de arranque de Compose no te ayuda y lo único que te salva es una app que sepa reintentar.
¿Cómo armar un stack de monitoreo de Docker con Prometheus, Grafana y cAdvisor?
La guía propone esta cadena: contenedores, cAdvisor (expone métricas de recursos), Prometheus (las recolecta), Grafana (las grafica) y dashboards con alertas. Todo se levanta con Docker Compose. La idea es enterarte de la caída por una alerta y no por un mensaje de un usuario.
Las métricas que sugiere seguir son CPU, memoria, reinicios del contenedor y disponibilidad de la aplicación. Netdata marca como señales de alerta un uso de memoria superior al 80% del límite y un RestartCount mayor a 0 y creciente. Las fuentes no traen configuraciones de Prometheus ni de Grafana, así que no las invento: el proyecto de laboratorio que plantea la guía consiste en simular una falla y documentar la investigación, la causa raíz y el arreglo.
¿Cómo verificar que el arreglo funcionó de verdad?
Propuesta editorial, no atribuida a la fuente ni probada por nosotros: antes de dar el incidente por cerrado, comprobá tres cosas por separado. Lo explicamos a fondo en usar Caddy como proxy en producción.
- Salud.
docker inspectdebería mostrar healthy, no solo Up. - Respuesta real. Un
curldesde el host al puerto publicado devuelve lo que esperás. - Logs limpios. Los errores que originaron el problema no reaparecen.
Si reproducís la falla a propósito en un entorno local (el desafío que plantea la guía), vas a ver si tu diagnóstico era el correcto o solo una corazonada.
Errores comunes al diagnosticar contenedores
- Reiniciar o reconstruir sin leer los logs. Corrección: empezá por
docker logs --tail 100 -ty recién después tocá algo. - Creer que depends_on espera a la base. Corrección: healthcheck en la base y
condition: service_healthy, más reintentos en la app. - Escribir el healthcheck en forma exec con operadores de shell. Netdata advierte que
["curl", "...", "||", "exit", "1"]falla porque los operadores se pasan como argumentos; usáCMD-SHELL. - Usar una imagen mínima sin curl ni wget. El healthcheck falla con “executable file not found” y parece un problema de la app.
- Un start-period más corto que el arranque real. El ejemplo de Netdata: si la app tarda 45 segundos y el período es de 30, queda unhealthy apenas cierra la ventana de gracia.
Preguntas Frecuentes
¿Por qué mi contenedor Docker está en Up pero la aplicación no funciona?
Porque Up solo indica que el proceso principal sigue ejecutándose. La aplicación puede estar sin conexión a la base, escuchando en otro puerto o con una variable de entorno faltante, y Docker no lo detecta sin un healthcheck.
¿Cómo ver los logs de un contenedor Docker?
Con docker logs nombre. Sumá -f para seguirlos en vivo, --tail 100 para las últimas 100 líneas y -t para ver marcas de tiempo.
¿Cómo configurar un HEALTHCHECK en un Dockerfile?
Agregá la instrucción HEALTHCHECK con las opciones –interval, –timeout, –start-period y –retries, y un comando que termine en 0 si todo está bien y en 1 si no. Después consultás el estado con docker ps o docker inspect.
¿Por qué mi app no se conecta a la base de datos en Docker Compose aunque use depends_on?
Porque depends_on solo espera a que el contenedor de la base esté corriendo, no a que acepte conexiones. Definí un healthcheck en la base (por ejemplo con pg_isready en Postgres) y usá condition: service_healthy en la app.
¿Cómo ver cuánta memoria y CPU consume un contenedor Docker?
Ejecutá docker stats: muestra CPU y uso de memoria contra el límite de cada contenedor. Para una foto única, Netdata usa docker stats --no-stream con el ID del contenedor.
Conclusión
Un contenedor en Up no prueba nada sobre la aplicación. Lo útil es tener un orden fijo (estado, logs, inspect, recursos, puertos, red, endpoint) y un healthcheck que verifique algo que le importe a tu negocio, no solo que nginx conteste.
Lo que sigue es concreto: agregá un healthcheck a los contenedores que hoy dependen de que alguien avise, cambiá los depends_on a service_healthy donde haya bases de datos y probá en local una falla provocada. Las fuentes no traen mediciones propias, así que tomá los ejemplos como guía de método y validá los comandos en tu versión de Docker.






