Un día tus contenedores funcionan bien. Al siguiente el host está “misteriosamente” sin disco, el SSH va lento y cada despliegue se convierte en una ruleta.
Revisas /var/log como buen ciudadano. No es el culpable. Entonces recuerdas el lugar oscuro: /var/lib/docker.
Este es el modo de fallo donde los logs de Docker crecen hasta que el sistema de archivos se queda sin espacio. Es aburrido, común y totalmente evitable.
Arréglalo correctamente y tu yo del futuro no tendrá que hacer una cirugía de emergencia en un nodo en vivo a las 2 a.m.
Guía rápida de diagnóstico
Cuando el disco se está llenando, no quieres una disertación filosófica. Quieres respuestas en minutos: qué es grande, qué está creciendo y qué cambiar para que
no vuelva a pasar.
Primero: ¿son realmente logs de Docker?
Revisa el uso de los sistemas de archivos y confirma qué montaje está en llamas. Si / está lleno pero Docker está en otra partición, no pierdas tiempo
en el lugar equivocado. Si Docker vive en root (común), estás a punto de aprender por qué eso es delicado.
Segundo: encuentra los mayores responsables rápido
Identifica qué archivos de log de contenedores son enormes. Si un solo contenedor genera gigabytes por hora, no estás “arreglando la rotación”, estás
tratando un DDoS de logs.
Tercero: confirma el driver de logging y sus límites
Si usas json-file sin max-size ni max-file, los logs crecerán hasta que el disco diga “no”.
Si usas journald, el volumen de logs se mueve a systemd-journald y tienes que limitarlo allí.
Cuarto: decide qué tipo de arreglo necesitas
- Contención inmediata: truncar logs enormes, liberar disco, detener la hemorragia.
- Arreglo de configuración: establecer defaults del daemon, imponer límites, reiniciar Docker de forma segura.
- Arreglo estructural: enviar logs a un sistema centralizado y dejar de tratar el disco local como un pozo sin fondo.
Qué está creciendo realmente (y por qué)
El comportamiento por defecto de Docker es engañosamente simple: todo lo que tu contenedor escribe en stdout y stderr lo captura el motor Docker y
lo escribe en algún lugar. El “algún lugar” por defecto en la mayoría de sistemas Linux es el driver de logging json-file.
Con json-file, cada contenedor obtiene un archivo de log (más los rotados si configuraste rotación) bajo
/var/lib/docker/containers/<container-id>/<container-id>-json.log. Ese archivo crece. Y crece. Y sigue creciendo hasta que el
sistema de archivos se llena, a menos que le digas a Docker que lo rote.
Aquí está la verdad incómoda: “tenemos monitorización” no ayuda si no tienes señales de protección. Las alertas de disco saltan cuando ya está mal.
La rotación es el cinturón de seguridad. El logging centralizado son los airbags. Quieres ambos.
Una frase que vale la pena pegar en un post-it:
La esperanza no es una estrategia
— atribuida a menudo a Gordon “Nick” Haskins (idea parafraseada).
Además: los contenedores no hacen que el logging sea mágicamente más fácil. Facilitan crear muchos logs, rápidamente, desde muchos puntos pequeños.
Si tu servicio es hablador, tu host se convierte en el diario que nunca pidió.
Chiste #1: Los logs de Docker son como las pegatinas gratis en una conferencia: tomas suficientes y de repente tu portátil no cierra.
Datos interesantes y contexto histórico
- Docker promovió originalmente “logs a stdout” como una separación limpia: las apps emiten logs; la plataforma decide a dónde van.
- El driver
json-filese convirtió en el predeterminado porque es simple, autocontenido y no requiere dependencias externas. - Las opciones de rotación de Docker son por driver: las banderas que funcionan para
json-fileno aplican necesariamente ajournald. - Kubernetes estandarizó logs de contenedores como archivos en el nodo (comúnmente bajo
/var/log/containers), lo que convirtió la rotación a nivel de nodo en un problema operativo principal. - Systemd-journald tiene sus propios controles de retención y puede configurarse para mantener logs en memoria, en disco o ambos—útil hasta que nadie lo limita.
- Los sistemas de ficheros overlay cambiaron cómo se siente el “uso de disco”: puedes tener espacio en una capa y aún quedarte sin espacio en el filesystem que lo respalda.
- Plataformas tempranas de contenedores se distribuían sin políticas de retención, porque la retención depende del negocio (cumplimiento, forense, coste).
- Muchos outages atribuidos a “almacenamiento Docker” son en realidad eventos de disco lleno desencadenados por logs sin límite o modos debug descontrolados.
Tareas prácticas: comandos, salidas, decisiones
A continuación hay tareas reales que puedes ejecutar en un host Linux con Docker. Cada una incluye: el comando, qué significa una salida típica y qué decisión tomar a continuación.
El objetivo es convertir “el disco está lleno” en una secuencia controlada de comprobaciones.
Tarea 1: Confirma qué sistema de archivos está lleno
cr0x@server:~$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 120G 116G 2.5G 98% /
/dev/nvme0n1p1 vfat 512M 8.0M 504M 2% /boot/efi
tmpfs tmpfs 32G 0 32G 0% /dev/shm
Qué significa: Root está al 98%. Si los datos de Docker están en /, estás a un despliegue de un mal día.
Decisión: Procede a localizar los consumidores de disco de Docker inmediatamente; no empieces a “limpiar” a ciegas.
Tarea 2: Mide el tamaño de /var/lib/docker rápidamente
cr0x@server:~$ sudo du -sh /var/lib/docker
87G /var/lib/docker
Qué significa: Docker está consumiendo la mayor parte del root. Eso no es automáticamente incorrecto, pero es una señal fuerte.
Decisión: Desglosa el almacenamiento de Docker en contenedores, imágenes, volúmenes y logs.
Tarea 3: Ve la contabilidad de almacenamiento de Docker
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 18 19.6GB 6.2GB (31%)
Containers 65 24 3.8GB 1.1GB (28%)
Local Volumes 14 12 9.4GB 0B (0%)
Build Cache 0 0 0B 0B
Qué significa: Imágenes/volúmenes no son lo bastante masivos para explicar 87G. Probablemente sean logs o capas modificables de contenedores.
Decisión: Ve a cazar archivos de log bajo los directorios de contenedores.
Tarea 4: Encuentra los archivos de log de contenedor más grandes
cr0x@server:~$ sudo find /var/lib/docker/containers -name "*-json.log" -printf "%s %p\n" | sort -nr | head -n 5
32213455120 /var/lib/docker/containers/9b2c.../9b2c...-json.log
11422577664 /var/lib/docker/containers/12ad.../12ad...-json.log
2213478400 /var/lib/docker/containers/7a11.../7a11...-json.log
845312000 /var/lib/docker/containers/fe88.../fe88...-json.log
331776000 /var/lib/docker/containers/0c19.../0c19...-json.log
Qué significa: Tienes al menos un archivo de log de 32GB. Ese es tu escape de disco.
Decisión: Mapea el ID del contenedor a un nombre/servicio, luego decide: truncar ahora y arreglar la rotación permanentemente.
Tarea 5: Mapea el ID del contenedor al nombre e imagen
cr0x@server:~$ docker ps -a --no-trunc --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}' | head
CONTAINER ID NAMES IMAGE STATUS
9b2c0f0e0d6c2f7d4c3d1b1e8c... api-prod registry/app:2.8.1 Up 3 days
12ad77c2b12d8b1f21b9e8f2aa... worker-prod registry/worker:5.1.0 Up 3 days
7a11aa90b9d9d7c0c1e0fda2bb... nginx-edge nginx:1.25 Up 10 days
Qué significa: El log enorme pertenece a api-prod. Ahora puedes hablar con el equipo adecuado, o al menos saber a quién mirar feo.
Decisión: Inspecciona la configuración de logging y la tasa de crecimiento; considera truncarlo inmediatamente si el disco está críticamente bajo.
Tarea 6: Comprueba el driver de logging por defecto del daemon
cr0x@server:~$ docker info --format '{{.LoggingDriver}}'
json-file
Qué significa: Estás en json-file. La rotación no es automática a menos que esté configurada.
Decisión: Verifica que el daemon tenga log-opts; si no, impleméntalos.
Tarea 7: Inspecciona la configuración actual del daemon
cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
Qué significa: No hay límites de logs configurados globalmente.
Decisión: Añade log-driver y log-opts por defecto, luego reinicia Docker en una ventana controlada.
Tarea 8: Comprueba la ruta efectiva del log de un contenedor (json-file)
cr0x@server:~$ docker inspect --format '{{.LogPath}}' api-prod
/var/lib/docker/containers/9b2c0f0e0d6c2f7d4c3d1b1e8c.../9b2c0f0e0d6c2f7d4c3d1b1e8c...-json.log
Qué significa: Este es el archivo exacto que está creciendo. Sin conjeturas.
Decisión: Si la presión de disco es alta, trunca ese archivo (de forma segura) como contención.
Tarea 9: Mide la tasa de crecimiento del log (¿está explotando ahora?)
cr0x@server:~$ sudo ls -lh /var/lib/docker/containers/9b2c.../*-json.log
-rw-r----- 1 root root 30G Jan 2 11:41 /var/lib/docker/containers/9b2c.../9b2c...-json.log
cr0x@server:~$ sleep 10; sudo ls -lh /var/lib/docker/containers/9b2c.../*-json.log
-rw-r----- 1 root root 30G Jan 2 11:41 /var/lib/docker/containers/9b2c.../9b2c...-json.log
Qué significa: Si el tamaño sube notablemente en segundos, el servicio está enviando logs a saco. Si está estable, la “explosión” pudo haber sido antes.
Decisión: Si está creciendo rápidamente, trátalo como un incidente: reduce el nivel de logging, limita la tasa o arregla el loop de fallos.
Tarea 10: Trunca un archivo de log enorme sin reiniciar el contenedor
cr0x@server:~$ sudo truncate -s 0 /var/lib/docker/containers/9b2c.../9b2c...-json.log
cr0x@server:~$ sudo ls -lh /var/lib/docker/containers/9b2c.../9b2c...-json.log
-rw-r----- 1 root root 0 Jan 2 11:44 /var/lib/docker/containers/9b2c.../9b2c...-json.log
Qué significa: Acabas de liberar espacio inmediatamente. El contenedor sigue en ejecución; Docker sigue escribiendo en el mismo inode.
Decisión: Esto es contención, no una solución. Implementa rotación después o volverás aquí pronto.
Tarea 11: Confirma que el espacio en disco volvió y el host puede respirar
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 120G 86G 29G 75% /
Qué significa: Compraste tiempo. Úsalo sabiamente.
Decisión: Aplica rotación a nivel de daemon y luego valida con un contenedor nuevo.
Tarea 12: Configura la rotación json-file a nivel de daemon (la solución real)
cr0x@server:~$ sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
},
"storage-driver": "overlay2"
}
EOF
Qué significa: Los contenedores nuevos rotarán a ~10MB y conservarán 5 archivos (aprox. 50MB por contenedor, más overhead).
Decisión: Reinicia Docker de forma controlada; ten en cuenta que los contenedores existentes pueden necesitar recreación para adoptar los defaults.
Tarea 13: Valida que la configuración sea sintácticamente correcta antes de reiniciar
cr0x@server:~$ sudo jq . /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
},
"storage-driver": "overlay2"
}
Qué significa: JSON válido. Docker no se negará a arrancar por una coma faltante.
Decisión: Reinicia Docker, pero comprende el radio de impacto: los contenedores pueden reiniciarse según tu init y orquestador.
Tarea 14: Reinicia Docker y confirma que volvió correctamente
cr0x@server:~$ sudo systemctl restart docker
cr0x@server:~$ systemctl is-active docker
active
Qué significa: Docker está arriba. Ahora verifica que los ajustes de logging se apliquen realmente a contenedores nuevos.
Decisión: Ejecuta un contenedor de prueba e inspecciona su configuración de logs.
Tarea 15: Verifica que un contenedor nuevo herede las opciones de log
cr0x@server:~$ docker run --rm --name logtest alpine:3.20 sh -c 'i=0; while [ $i -lt 20000 ]; do echo "line $i"; i=$((i+1)); done'
line 0
line 1
line 2
...
cr0x@server:~$ docker inspect --format '{{json .HostConfig.LogConfig}}' logtest
{"Type":"json-file","Config":{"max-file":"5","max-size":"10m"}}
Qué significa: Los límites de rotación están presentes en la configuración del contenedor.
Decisión: Planea cómo aplicar estos ajustes a contenedores existentes de larga vida (normalmente recreándolos).
Tarea 16: Identifica qué contenedores no tienen rotación (realidad de flotas mixtas)
cr0x@server:~$ for c in $(docker ps -q); do
name=$(docker inspect --format '{{.Name}}' "$c" | sed 's#^/##')
cfg=$(docker inspect --format '{{.HostConfig.LogConfig.Config}}' "$c")
echo "$name $cfg"
done | head
api-prod map[]
worker-prod map[max-file:3 max-size:50m]
nginx-edge map[]
Qué significa: map[] normalmente indica que no hay overrides por contenedor. Dependiendo de la versión de Docker, puede seguir heredando los defaults del daemon, o reflejar configuraciones antiguas. Trátalo como sospechoso.
Decisión: Para cualquier contenedor crítico y de larga vida con logs enormes, establece explícitamente opciones de logging o recréalo después de aplicar los defaults del daemon.
Arreglar la rotación de logs correctamente
La solución correcta depende de cómo ejecutes contenedores. Hosts Docker standalone son un mundo. Docker Compose es otro. Swarm existe (sí, aún).
Kubernetes es su propio universo. El principio es el mismo: necesitas logs locales con límite, y un lugar para los logs cuando realmente te importen.
Defaults del daemon: la línea base que todo host debe tener
Si no haces nada más, establece límites globales en /etc/docker/daemon.json. Esto evita “contenedor nuevo, archivo nuevo sin límite.”
También hace que los hosts sean predecibles entre entornos.
Un punto de partida sensato para muchos servicios es:
10–50MB max-size y 3–10 max-file.
Más pequeño para servicios de alta rotación, más grande si necesitas más historial local para el triage.
El compromiso es brutal: rotaciones más pequeñas implican que puedes perder logs antiguos localmente. Está bien si tienes logging centralizado.
Es imprudente si los logs locales son tus únicos registros.
Anulaciones por contenedor: úsalas con moderación, pero úsalas
Para ese contenedor que siempre es hablador (ingress, proxy de auth, servidor web con access logs), puede que desees overrides explícitos para que un futuro cambio de daemon
no te sorprenda.
Con la CLI de Docker puedes usar:
--log-opt max-size=... y --log-opt max-file=... en docker run. En Compose, puedes especificar opciones de logging por servicio.
El punto no es la sintaxis; el punto es la intención. Haz de “logs locales acotados” una propiedad del workload, no una esperanza atada al host.
No uses logrotate del sistema como solución principal para json logs de Docker
La gente prueba esto porque conoce logrotate. Se siente familiar. También es la capa equivocada para los logs de contenedores Docker.
Docker espera controlar esos archivos; si los rotas externamente renombrándolos, puedes acabar con Docker escribiendo aún al manejador de archivo antiguo.
La truncación puede funcionar en una emergencia. El renombrado es donde empieza la rareza. Si insistes en rotar a nivel de SO por alguna razón, debes entender descriptores de archivo
y el comportamiento copytruncate. La mayoría de equipos no quieren ese drama de relaciones a escala.
Chiste #2: La rotación de logs es como usar hilo dental: saltártela se siente bien hasta que se vuelve caro y personal.
Haz que la rotación sea exigible, no una sugerencia
El fallo organizativo más común: un host se arregla, el siguiente es “temporal” y el siguiente es un copo de nieve construido con la memoria de alguien.
Tu arreglo debe estar codificado:
- Gestión de configuración (Ansible, Puppet, Chef) o imágenes inmutables que incluyan
daemon.json. - Cheques en CI que rechacen Compose o specs sin límites de logging.
- Un runbook SRE base: “Cualquier contenedor debe tener logs locales acotados.”
Elige un driver de logging con intención
El driver de logging de Docker decide a dónde van stdout/stderr. No es un ajuste estético. Es un contrato operativo:
rendimiento, fiabilidad, retención y quién recibe la alarma cuando el disco se llena.
json-file: simple, suficientemente rápido, peligroso si no se acota
Pros: los archivos locales son fáciles de inspeccionar; no depende de systemd; funciona en todas partes; las herramientas lo esperan.
Contras: sin límites por defecto; duplica esfuerzo si ya envías logs; puede crear amplificación de escritura en servicios ocupados.
En la práctica: json-file está bien si lo acotas y tienes envío de logs. Si lo acotas y no envías, eliges pérdida de datos a cambio de la seguridad del host.
Eso puede ser la decisión correcta, pero sé honesto respecto a ello.
journald: centralizado en el nodo, pero igualmente finito
Pros: consistente con el logging del sistema; metadatos ricos; consultable con journalctl; soporta rate limiting y topes de tamaño vía configuración de journald.
Contras: si no limitas journald, simplemente moviste el problema del disco; el debug entre reinicios y las opciones de persistencia pueden sorprender.
Si usas journald, debes configurar la retención de journald. Si no, construiste un mejor acumulador de logs, no un sistema más seguro.
Drivers remotos (syslog, fluentd, gelf, awslogs, splunk, etc.): menos archivos locales, más dependencias de red
Pros: los logs salen del nodo, que es el objetivo; retención centralizada; puedes analizar sin SSH.
Contras: la contrapresión puede afectar; las caídas de red pueden perder logs o bloquear contenedores (según driver/configuración); la complejidad operativa se traslada a la canalización de logging.
Un enfoque amigable para producción suele ser: buffer local acotado + envío fiable. No apuestes tu respuesta a incidentes a un único salto de red.
Respuesta de emergencia: el host está sin disco, ¿y ahora qué?
Cuando el host está al 100%, los modos de fallo se multiplican: Docker no puede escribir logs, los contenedores fallan, el kernel no puede asignar espacio para lo básico y de repente
tu “problema simple de logging” se convierte en un incidente de disponibilidad.
Objetivos de contención (en orden)
- Liberar espacio rápido para restaurar la estabilidad del sistema (truncar los logs más grandes).
- Detener la fuente de logging de alto volumen si es un comportamiento anormal (modo debug, bucle de crash, tormenta de excepciones).
- Aplicar límites de rotación para que el mismo patrón no vuelva a llenar el disco de inmediato.
- Preservar suficiente evidencia para entender por qué ocurrió (captura logs de muestra antes de truncar si es posible).
Qué no hacer en un pánico por disco lleno
- No empieces a borrar directorios aleatorios bajo
/var/lib/docker. Así es como “arreglas logs” borrando tu runtime. - No ejecutes
docker system prune -acomo reflejo en un nodo de producción. Liberarás espacio y también eliminarás imágenes que necesitas para recuperar. - No reinicies Docker repetidamente con la esperanza de que “limpie algo”. Los reinicios pueden desencadenar reinicios en cascada de contenedores.
Si necesitas conservar evidencia
Si cumplimiento o depuración requieren preservar un fragmento de logs, captura un tail antes de truncar. Esto te da las últimas líneas sin transportar el archivo de 30GB entero.
cr0x@server:~$ sudo tail -n 5000 /var/lib/docker/containers/9b2c.../9b2c...-json.log > /root/api-prod-last-5000.jsonl
Qué significa: Conservaste eventos recientes. No es perfecto, pero suele ser suficiente para ver el patrón de la tormenta de errores.
Decisión: Trunca el archivo grande después de capturar lo que necesites y luego arregla la rotación.
Tres micro-historias de la realidad corporativa
Micro-historia 1: El incidente causado por una suposición errónea
Una empresa mediana ejecutaba una API para clientes en unos cuantos hosts Docker robustos. El equipo de servicio era disciplinado con métricas y trazas, y asumieron que los logs
eran “problema de otro” porque había un agente de logging instalado en los hosts. Todos creían que los logs se enviaban fuera y por tanto eran inofensivos.
La suposición fue errónea de una forma muy específica: el agente leía ficheros bajo /var/log y rutas específicas de apps, pero no ingestaba los json logs de contenedores de Docker bajo /var/lib/docker/containers. Los contenedores escribían a stdout. Docker escribía json logs. Nadie los limitó.
El agente de envío nunca se enteró. Los logs se quedaron locales. Para siempre.
El outage no comenzó con la API fallando. Comenzó con el sistema de archivos root del host alcanzando el 100%. A ese punto, todo se volvió mentira: servicios fallaban por razones que parecían no relacionadas—handshakes TLS con timeouts, health checks fluctuando y contenedores reiniciándose porque sus propias escrituras de log empezaron a fallar.
Ops vio una propagación de síntomas y persiguió fantasmas.
La solución fue poco glamorosa: limitar globalmente los logs json-file, enviar explícitamente los logs de contenedor y añadir monitorización que rastreara
el crecimiento de /var/lib/docker/containers. El cambio cultural importante fue aún más aburrido: “el logging existe en dos sitios—recolección y retención.”
Tenían recolección para algunos logs y retención para ninguno.
Micro-historia 2: La optimización que salió mal
Otra organización se cansó de los picos de uso de disco. Decidieron fijar max-size extremadamente bajo en toda la flota. Piensa en megabytes de un solo dígito.
La razón era clara: mantener los nodos seguros, enviar todo centralmente y dejar el disco local como un buffer de emergencia únicamente.
Dos semanas después, la respuesta a incidentes empezó a fallar de otra forma. Cuando la canalización de logging central tuvo problemas (mantenimiento, saturación de red o un indexador sobrecargado),
los logs locales eran demasiado pequeños para cubrir la brecha. Los ingenieros SSH-eaban a nodos durante un outage y encontraban que los últimos logs locales solo cubrían un minuto o dos. La canalización estaba caída y el buffer local estaba vacío. El triage se volvió conjetura.
Luego vino el efecto de segundo orden: rotaciones frecuentes crearon muchos archivos pequeños y más churn de metadatos. En algunos nodos, la combinación de alto volumen de logs y
umbrales de rotación muy bajos aumentó la sobrecarga. La “optimización” no fue catastrófica, pero hizo el sistema más ruidoso y difícil de razonar.
El compromiso eventual fue sensato: aumentar max-size para mantener una ventana local significativa, pero aún acotada; hacer el envío resiliente; y añadir alertas sobre
la salud del pipeline de logs. La rotación no sustituye a una recolección fiable. Es una red de seguridad, no una trapeista.
Micro-historia 3: La práctica aburrida pero correcta que salvó el día
Una compañía relacionada con finanzas ejecutaba workloads de contenedores en hosts que trataban como ganado, no como mascotas. El equipo SRE tenía una práctica que parecía tediosa en las revisiones de tickets:
cada imagen base incluía por defecto los ajustes del daemon para logging, y cada provisión de host incluía un pequeño paso de validación que lanzaba un contenedor de prueba e inspeccionaba su LogConfig efectivo. Nadie lo celebraba. Era solo otra casilla.
Una tarde, un release introdujo una tormenta de excepciones. El servicio empezó a volcar stack traces a alta velocidad. Hubiera sido el clásico incidente “los logs llenan el disco y todo muere”. Pero en esos nodos, cada contenedor tenía topes duros. El uso de disco subió y luego se estabilizó.
El incidente aún dolió: el servicio estaba degradado y necesitó rollback. Pero la flota de hosts se mantuvo estable. No hubo fallos en cascada. No hubo limpieza paniqueada que arriesgara borrar imágenes y romper la recuperación. El equipo pudo concentrarse en el bug real en lugar de hacer de conserje del filesystem.
El postmortem fue casi aburrido. Y eso es el cumplido. La práctica aburrida—defaults forzados más una pequeña prueba de validación—convirtió un posible incidente de plataforma en un incidente de aplicación sencillo. Tus mejores victorias operativas parecen “no pasó nada especial.”
Errores comunes (síntoma → causa raíz → solución)
1) Síntoma: El filesystem root se llena cada pocos días
Causa raíz: driver json-file sin max-size/max-file, más workloads habladores.
Solución: Establece defaults del daemon en /etc/docker/daemon.json, reinicia Docker con seguridad y recrea contenedores de larga vida si hace falta.
2) Síntoma: Configuraste rotación pero contenedores viejos aún tienen logs enormes
Causa raíz: Los contenedores existentes no adoptan los nuevos defaults del daemon; mantienen su LogConfig previo.
Solución: Para servicios críticos, establece opciones de logging por servicio (Compose/Swarm) o recréalos después de aplicar los defaults.
3) Síntoma: El disco está lleno pero docker system df parece normal
Causa raíz: La contabilidad de Docker no siempre refleja el bloat real de archivos de log o realidades del sistema de archivos.
Solución: Mide directamente con find y du bajo /var/lib/docker/containers; trata los logs como consumidores de disco de primera clase.
4) Síntoma: Cambiaste a journald y el disco aún se llena
Causa raíz: journald retiene demasiado; no hay topes o el almacenamiento persistente crece sin límites.
Solución: Configura la retención de journald (por ejemplo, uso máximo del sistema) y monitoriza el uso de disco del journal. No “configures journald” y te desentiendas.
5) Síntoma: Después de logrotate externo, Docker sigue escribiendo en un archivo “eliminado” y no se libera espacio
Causa raíz: El descriptor de archivo aún apunta al inode antiguo; renombrar/eliminar no reclama espacio hasta que el escritor cierra.
Solución: Prefiere la rotación incorporada de Docker. En emergencias, usa truncate en la ruta activa del log.
6) Síntoma: Los contenedores se quedan bloqueados cuando el backend de logging va lento
Causa raíz: Algunos drivers de logging pueden aplicar contrapresión; las escrituras a stdout pueden bloquear si el driver no puede seguir el ritmo.
Solución: Prueba el comportamiento del driver bajo carga, asegura buffers adecuados y evita logging remoto síncrono sin resiliencia.
7) Síntoma: Pusiste max-size muy pequeño y ahora no puedes depurar incidentes
Causa raíz: La ventana de retención local es demasiado corta; el logging central no es lo suficientemente fiable.
Solución: Incrementa los topes locales para preservar una ventana útil y fortalece la salud y alerta del pipeline de envío de logs.
8) Síntoma: Los archivos rotan pero el uso de disco sigue subiendo
Causa raíz: El verdadero culpable está en otra parte: capas writable de overlay2, volúmenes colgantes, build cache o logs fuera de Docker.
Solución: Desglosa el uso de disco con du y docker system df, luego prunea de forma dirigida (con control de cambios).
Listas de verificación / plan paso a paso
Paso a paso: estabilizar un host que se está llenando ahora mismo
- Confirma qué filesystem está lleno (
df -hT). - Encuentra los archivos de log más grandes bajo
/var/lib/docker/containers. - Mapea el ID del contenedor a un servicio y comprueba si está en crash loop o modo debug.
- Captura un tail pequeño si necesitas evidencia (
tail -n). - Trunca los infractores principales (
truncate -s 0). - Aplica defaults de logging al daemon y valida el JSON.
- Reinicia Docker de forma controlada (o prográmalo). Confirma que está activo.
- Recrea o redeploya los peores culpables para que adopten la nueva política de logging.
- Configura alertas sobre uso de disco y sobre la tasa de crecimiento de logs (deltas de tamaño de directorio), no solo “disco al 90%”.
Lista base: todo host Docker debería cumplir esto
- El daemon tiene explícito
log-driverylog-optsacotados (o la retención de journald está configurada). - La capacidad de
/var/lib/dockerestá dimensionada para imágenes + volúmenes + logs acotados, no “lo que quede en root”. - La monitorización incluye:
- Alertas de uso de filesystem con umbrales sensatos
- Alertas de uso de inodos (sí, la rotación puede trasladar el problema)
- Alertas de crecimiento en
/var/lib/docker/containers
- La salud del pipeline de logs se monitoriza si se envían logs fuera del host.
- El runbook incluye comandos seguros: inspeccionar rutas de logs, truncar, verificar configuración del daemon.
Lista de gestión de cambios: desplegar nuevos ajustes de logs con seguridad
- Decide los defaults (
max-size,max-file) según la ventana local de retención aceptable. - Actualiza
/etc/docker/daemon.jsoncon JSON válido; valida conjq. - Elige un método de despliegue:
- Recrear contenedores gradualmente (preferido)
- Reinicio host por host en mantenimiento
- Verifica: los contenedores nuevos tienen el
HostConfig.LogConfigesperado. - Confirma que el uso de disco se estabiliza en varios días y que la respuesta a incidentes aún cuenta con suficientes logs locales.
Preguntas frecuentes
1) ¿Por qué los logs de Docker son tan grandes si mi app “no registra tanto”?
Tu app puede no “loggear” intencionalmente, pero puede estar escribiendo salida stderr ruidosa, repitiendo stack traces, imprimiendo access logs a alto QPS o corriendo en modo debug.
Además, algunas librerías generan más logs de lo esperado en condiciones de error (las tormentas de reintentos son clásicas).
2) ¿Configurar max-size y max-file borra logs?
Rota y elimina trozos antiguos cuando se alcanza el límite de retención. Eso es eliminación por diseño. Si necesitas retención más larga, envía logs centralmente.
El disco local no es un archivo histórico.
3) ¿Los defaults en daemon.json se aplican a contenedores en ejecución?
No de forma fiable. Los contenedores en ejecución mantienen sus settings de logging configurados. Trata los defaults del daemon como “para contenedores nuevos” y planifica recrear workloads de larga vida.
4) ¿Es seguro truncar el json log?
Sí, para contención. truncate -s 0 mantiene el archivo y el inode; Docker sigue escribiendo. Pierdes el historial en ese archivo, así que captura un tail primero si lo necesitas.
5) ¿Debo cambiar de json-file a journald?
Si tu flota usa systemd y tu equipo está cómodo con journalctl, journald puede ser una buena opción. Pero limita journald. Si no, solo moviste el punto de desbordamiento.
6) ¿Y Kubernetes—esto sigue importando?
Sí. Kubernetes sigue dependiendo del manejo de logs a nivel de nodo. Los logs del runtime de contenedores, el comportamiento del kubelet y los ajustes de rotación del nodo influyen en la presión de disco.
Si lo ignoras, los nodos se evictan y los workloads flaquean. Diferente tooling, misma física.
7) ¿Por qué no simplemente pruneo Docker regularmente?
Prunear imágenes y caches puede ayudar, pero no soluciona el problema central: escrituras de log sin límite. Además, pruning agresivo puede ralentizar despliegues y romper paths de rollback.
Arregla la política de logs primero y luego prunea de forma intencional.
8) ¿Cómo elijo buenos valores de rotación?
Escoge una ventana local de retención con la que puedas vivir durante una caída del pipeline de logging. Para muchos equipos, 30 minutos a unas pocas horas de logs es un buffer útil.
Convierte eso en tamaño según tu tasa típica de logs, luego pon el tope. Empieza conservador, observa y ajusta.
9) Mi disco está lleno pero no encuentro json logs grandes—¿qué más debo revisar?
Mira volúmenes y capas writables (caches de aplicación, uploads, archivos temporales), build caches y cualquier otro directorio en el mismo filesystem.
También revisa agotamiento de inodos: muchos archivos rotados pequeños pueden causar problemas aún con espacio aparente.
10) ¿La rotación de logs puede afectar el rendimiento?
Rotaciones excesivamente frecuentes pueden añadir sobrecarga. Pero el coste en rendimiento de logs sin límite es peor: escrituras constantes en disco, alto I/O wait y eventual outage.
La meta correcta es “acotado y aburrido.”
Conclusión: próximos pasos que perduran
Si los logs de Docker se están desbordando, no es un caso extraño. Es una característica de seguridad faltante. El disco de tu host no es una caridad.
La solución es sencilla: identifica los archivos de log, acótalos correctamente y deja de tratar “stdout” como un sumidero infinito.
Haz esto a continuación, en este orden:
- Hoy: encuentra los archivos
*-json.logmás grandes; trunca los peores si la presión de disco es alta. - Esta semana: establece defaults de rotación a nivel de daemon; valida con un contenedor de prueba; recrea servicios críticos para que adopten la política.
- Este mes: haz la política de logs exigible en provisión y CI; asegúrate de que el logging central esté sano y monitorizado; alerta sobre crecimiento, no solo sobre llenado.
Una vez esto esté en marcha, los incidentes por disco lleno causados por logging desaparecerán de tu agenda. Tus futuras alertas deberían ser sobre fallos reales, no sobre la plataforma
ahogándose en su propia narración.