Rotación de logs en Docker: evite que los registros llenen el disco de su VPS
El driver de logs predeterminado de Docker almacena datos sin límite. Un solo contenedor activo puede llenar un disco de 50GB en días. Este tutorial configura la rotación global de logs, ajustes por servicio en Compose, limpieza automatizada y monitoreo de uso de disco.
La configuración de logging predeterminada de Docker no tiene límite de tamaño. Cada línea que sus contenedores escriben en stdout o stderr se almacena en disco indefinidamente. En un VPS con 25GB o 50GB de almacenamiento, un solo contenedor con mucha actividad puede consumir todo el espacio disponible en días.
Este tutorial soluciona ese problema. Configurará la rotación global de logs, establecerá límites por servicio en Docker Compose, automatizará la limpieza de disco y configurará monitoreo para que el problema nunca lo tome por sorpresa.
Todos los comandos están probados en Debian 12 y Ubuntu 24.04 con Docker Engine 28.x/29.x.
Requisitos previos:
- Un VPS con Debian 12 o Ubuntu 24.04 con Docker instalado
- Acceso SSH con un usuario sudo
- Familiaridad básica con Docker y Docker Compose
¿Por qué los logs de contenedores Docker llenan el disco?
El driver de logs predeterminado de Docker es json-file. Captura todo lo que un contenedor escribe en stdout y stderr, y lo almacena como JSON en /var/lib/docker/containers/<container-id>/<container-id>-json.log. Por defecto, no hay tamaño máximo ni rotación. El archivo crece hasta que el disco se llena.
Una aplicación Node.js con nivel de logging INFO produce aproximadamente 50MB por día. Un reverse proxy (proxy inverso) bajo tráfico moderado puede generar 200MB o más. En un VPS de 50GB, eso significa que un solo contenedor sin gestión puede consumir todo el espacio libre en semanas.
Cuando el disco se llena, todo falla a la vez: los contenedores no pueden escribir, las bases de datos se corrompen, las sesiones SSH se congelan y ni siquiera puede iniciar sesión para solucionarlo.
Cómo verificar el uso actual de disco
Antes de cambiar algo, mida el daño:
df -h /var/lib/docker
Esto muestra cuánto espacio usa el directorio de datos de Docker. Luego obtenga un desglose específico de Docker:
docker system df
Salida esperada:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 5 3 1.2GB 450MB (37%)
Containers 8 4 3.8GB 3.1GB (81%)
Local Volumes 3 2 500MB 120MB (24%)
Build Cache 12 0 800MB 800MB (100%)
La fila "Containers" muestra el tamaño de los archivos de log. Si ese número es desproporcionadamente grande, los logs son el problema.
Para un desglose por contenedor:
docker system df -v
Esto lista cada contenedor con el tamaño de sus logs. Encuentre a los responsables.
Cómo encontrar los archivos de log más grandes directamente
Si docker system df confirma el problema, encuentre exactamente qué logs están consumiendo espacio:
sudo find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + | sort -rh | head -10
Esto lista los 10 archivos de log de contenedores más grandes con sus tamaños.
Triaje de emergencia: recupere espacio en disco ahora
Si su disco ya está lleno o casi lleno, solucione el problema inmediato antes de configurar la rotación.
Truncar el archivo de log de un contenedor específico (sin detener el contenedor):
sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
Reemplace <container-id> con el ID real del contenedor obtenido de docker ps --no-trunc -q.
Para truncar todos los archivos de log de Docker a la vez:
sudo sh -c 'truncate -s 0 /var/lib/docker/containers/*/*-json.log'
Verifique que el espacio fue recuperado:
df -h /var/lib/docker
Esta es una solución temporal. Los logs volverán a crecer. Las siguientes secciones hacen permanente la solución.
¿Cómo se configura la rotación de logs en daemon.json de Docker?
El archivo daemon.json establece las opciones de logging predeterminadas para todos los contenedores nuevos. El driver json-file de Docker soporta max-size (tamaño máximo por archivo de log antes de la rotación) y max-file (número de archivos rotados a conservar). Todos los valores en log-opts deben ser cadenas de texto, incluso los números.
Cree o edite la configuración del daemon:
sudo nano /etc/docker/daemon.json
Si el archivo no existe, créelo. Si ya contiene configuración (como registros personalizados o DNS), agregue las claves log-driver y log-opts junto a las existentes.
Recomendaciones de tamaño por disco de VPS
Elija valores basados en el tamaño de disco de su VPS:
| Tamaño de disco del VPS | max-size |
max-file |
Máximo de log por contenedor | Justificación |
|---|---|---|---|---|
| 25 GB | 5m |
3 |
15 MB | Disco limitado; logs mínimos |
| 50 GB | 10m |
5 |
50 MB | VPS estándar; retención equilibrada |
| 100 GB | 25m |
5 |
125 MB | Disco generoso; retención más larga |
| 200 GB+ | 50m |
5 |
250 MB | Disco grande; depuración extendida |
Para un VPS de 50GB (la opción más común), use esta configuración:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
Guarde el archivo y reinicie Docker:
sudo systemctl restart docker
Verifique que los nuevos valores predeterminados están activos:
docker info --format '{{.LoggingDriver}}'
Salida esperada:
json-file
Confirme que las opciones de log se aplican a los nuevos contenedores ejecutando uno e inspeccionándolo:
docker run -d --name log-test alpine echo "test" && docker inspect --format '{{.HostConfig.LogConfig}}' log-test && docker rm log-test
Salida esperada:
{json-file map[max-file:5 max-size:10m]}
¿Qué ocurre con los contenedores en ejecución después de cambiar daemon.json?
Los contenedores existentes conservan su configuración de log original. Los nuevos ajustes solo se aplican a contenedores creados después del reinicio. Los contenedores en ejecución deben recrearse para adoptar los nuevos valores predeterminados.
Si usa Docker Compose:
docker compose down && docker compose up -d
Para contenedores independientes, deténgalos y elimínelos, luego inícielos de nuevo. El flag --force-recreate también funciona:
docker compose up -d --force-recreate
Verifique que un contenedor específico usa la nueva configuración:
docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>
Salida esperada:
{json-file map[max-file:5 max-size:10m]}
¿Cómo se establecen límites de log en Docker Compose?
La configuración de log por servicio en Docker Compose anula los valores predeterminados de daemon.json. Esto permite dar límites más estrictos a servicios con mucha actividad y más margen a los silenciosos.
Agregue el bloque logging bajo cualquier servicio:
services:
web:
image: nginx:alpine
logging:
driver: json-file
options:
max-size: "5m"
max-file: "3"
ports:
- "80:80"
api:
image: node:22-alpine
logging:
driver: json-file
options:
max-size: "20m"
max-file: "5"
ports:
- "3000:3000"
Para evitar repetir el bloque de logging en cada servicio, use un ancla YAML (YAML anchor):
x-logging: &default-logging
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
services:
web:
image: nginx:alpine
<<: *default-logging
ports:
- "80:80"
api:
image: node:22-alpine
<<: *default-logging
ports:
- "3000:3000"
worker:
image: myapp/worker:latest
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
El servicio worker anula el ancla con sus propios límites. Todos los demás servicios reciben la configuración compartida.
Aplique la configuración:
docker compose up -d --force-recreate
Verifique:
docker inspect --format '{{.HostConfig.LogConfig}}' web
¿Cuál es la diferencia entre los drivers de log json-file, local y journald?
Docker incluye tres drivers de log que almacenan registros en el host. Cada uno tiene diferentes compromisos. El driver json-file es el predeterminado. El driver local es la alternativa recomendada por Docker para eficiencia de disco. El driver journald se integra con el journal de systemd.
| Característica | json-file | local | journald |
|---|---|---|---|
| Rotación predeterminada | Ninguna | Sí (100MB total) | Gestionada por journald |
| Compresión | Opcional (compress: "true") |
Activada por defecto | Gestionada por journald |
Soporte de docker logs |
Sí | Sí | Sí |
| Formato de log | JSON (legible) | Binario (interno) | Binario (journald) |
| Acceso con herramientas externas | Fácil (archivos de texto plano) | No soportado | Vía journalctl |
| max-size predeterminado | Ilimitado | 20MB por archivo | Configurado en journald.conf |
| max-file predeterminado | 1 (sin rotación) | 5 archivos | N/A |
Cuándo usar cada driver
json-file es el valor predeterminado seguro. Funciona en todas partes, soporta docker logs y los archivos de log son JSON plano que cualquier herramienta puede analizar. Agregue max-size y max-file y funciona bien para la mayoría de configuraciones en VPS.
local es mejor para eficiencia de disco. La compresión está activada por defecto, la rotación viene integrada (5 archivos de 20MB = 100MB por contenedor) y no necesita configurar nada. La contrapartida: los archivos de log usan un formato binario interno. Los recolectores de logs externos que leen archivos directamente (como Filebeat en modo archivo) no pueden analizarlos. Si solo lee logs a través de docker logs o los envía mediante un plugin de logging de Docker, cambie a local.
journald es la opción correcta si ya usa el journal de systemd para todos sus otros servicios y quiere los logs de contenedores en el mismo lugar. La rotación la gestiona la propia configuración de journald (/etc/systemd/journald.conf). Los logs se leen con journalctl en lugar de docker logs (aunque docker logs sigue funcionando).
Cómo cambiar al driver local
Edite /etc/docker/daemon.json:
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
Reinicie Docker:
sudo systemctl restart docker
Verifique:
docker info --format '{{.LoggingDriver}}'
Salida esperada:
local
Recree los contenedores para aplicar el nuevo driver:
docker compose up -d --force-recreate
Cómo usar el driver journald
Edite /etc/docker/daemon.json:
{
"log-driver": "journald"
}
Reinicie Docker:
sudo systemctl restart docker
Lea los logs de un contenedor específico:
sudo journalctl CONTAINER_NAME=mycontainer --no-pager -n 50
Siga los logs en tiempo real:
sudo journalctl CONTAINER_NAME=mycontainer -f
La rotación de journald se controla en /etc/systemd/journald.conf. Configuraciones clave:
[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=7day
Después de editar, reinicie journald:
sudo systemctl restart systemd-journald
¿Cómo se automatiza la limpieza de Docker con cron o timers de systemd?
La rotación de logs evita que los contenedores individuales crezcan sin límite. Pero Docker también acumula contenedores detenidos, imágenes sin usar, build cache huérfano y redes sin utilizar. docker system prune limpia todo esto.
¿Qué elimina exactamente docker system prune?
Por defecto, docker system prune elimina:
- Todos los contenedores detenidos
- Todas las redes no utilizadas por ningún contenedor en ejecución
- Todas las imágenes colgantes (dangling images, imágenes sin etiqueta no referenciadas por ningún contenedor)
- Todo el build cache no utilizado
No elimina:
- Contenedores en ejecución
- Volúmenes nombrados (los datos de su base de datos están seguros)
- Imágenes etiquetadas que aún están referenciadas
- Imágenes usadas por contenedores en ejecución
El flag --all adicionalmente elimina todas las imágenes no utilizadas (no solo las colgantes). El flag --volumes agrega los volúmenes anónimos a la limpieza. Use --volumes con precaución: destruye datos en volúmenes anónimos.
Opción 1: tarea cron
Cree una tarea de limpieza semanal:
sudo crontab -e
Agregue:
0 3 * * 0 /usr/bin/docker system prune -f >> /var/log/docker-prune.log 2>&1
Esto se ejecuta cada domingo a las 03:00. El flag -f omite la confirmación. La salida se guarda en un archivo de log para auditoría.
Verifique que el crontab se guardó:
sudo crontab -l
Opción 2: timer de systemd (recomendado)
Los timers de systemd son más confiables que cron. Registran en el journal, gestionan ejecuciones perdidas (si el servidor estaba apagado) y son más fáciles de monitorear.
Cree la unidad de servicio:
sudo nano /etc/systemd/system/docker-prune.service
[Unit]
Description=Docker system prune
Wants=docker.service
After=docker.service
[Service]
Type=oneshot
ExecStart=/usr/bin/docker system prune -f --filter "until=168h"
El flag --filter "until=168h" solo limpia objetos con más de 7 días de antigüedad. Esto protege los contenedores detenidos recientemente que podría querer inspeccionar.
Cree el timer:
sudo nano /etc/systemd/system/docker-prune.timer
[Unit]
Description=Run Docker prune weekly
[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
Persistent=true significa que si el servidor estaba apagado durante la hora programada, la tarea se ejecuta en el siguiente arranque. RandomizedDelaySec distribuye la carga si tiene múltiples servidores.
Active e inicie el timer:
sudo systemctl daemon-reload
sudo systemctl enable --now docker-prune.timer
enable hace que sobreviva a los reinicios. --now lo inicia inmediatamente.
Verifique que el timer está activo:
sudo systemctl status docker-prune.timer
Compruebe cuándo se ejecutará:
sudo systemctl list-timers docker-prune.timer
Pruébelo manualmente:
sudo systemctl start docker-prune.service
Revise el resultado:
sudo journalctl -u docker-prune.service --no-pager -n 20
¿Cómo se limpian los volúmenes de Docker de forma segura?
Los volúmenes almacenan datos persistentes: bases de datos, archivos subidos, configuración. Tenga cuidado aquí.
Liste todos los volúmenes y su uso:
docker volume ls
Muestre solo los volúmenes no adjuntos a ningún contenedor:
docker volume ls -f dangling=true
Elimine los volúmenes huérfanos:
docker volume prune -f
Esto solo elimina volúmenes que no están en uso por ningún contenedor (en ejecución o detenido). Los volúmenes nombrados adjuntos a contenedores detenidos están seguros.
Verifique lo que queda:
docker volume ls
Nunca ejecute docker volume prune inmediatamente después de docker system prune --volumes. El system prune con --volumes ya gestiona la limpieza de volúmenes. Ejecutar ambos es redundante en el mejor de los casos.
Para eliminar un volumen específico que haya identificado como innecesario:
docker volume rm <volume-name>
Siempre verifique qué datos contiene un volumen antes de eliminarlo:
docker volume inspect <volume-name>
El campo Mountpoint muestra dónde se encuentran los datos en disco. Puede inspeccionar su contenido:
sudo ls -la $(docker volume inspect --format '{{.Mountpoint}}' <volume-name>)
¿Cómo se monitorea el uso de disco de Docker en un VPS?
El monitoreo automatizado previene sorpresas. Esta sección configura una alerta de umbral que verifica el uso de disco de Docker y envía una advertencia cuando supera un límite.
Verificación manual rápida
Ejecute estos dos comandos cuando quiera una instantánea:
df -h /var/lib/docker
docker system df
Para un desglose detallado por contenedor e imagen:
docker system df -v
Script de alerta automatizada
Cree un script de monitoreo:
sudo nano /usr/local/bin/docker-disk-alert.sh
#!/bin/bash
# Alert when Docker's partition exceeds a usage threshold
THRESHOLD=80
MAILTO="admin@example.com"
USAGE=$(df /var/lib/docker | awk 'NR==2 {gsub(/%/,""); print $5}')
if [ "$USAGE" -ge "$THRESHOLD" ]; then
DOCKER_DF=$(docker system df 2>&1)
DISK_DF=$(df -h /var/lib/docker 2>&1)
TOP_LOGS=$(find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + 2>/dev/null | sort -rh | head -5)
BODY="Docker disk usage on $(hostname) is at ${USAGE}%.
Disk usage:
${DISK_DF}
Docker breakdown:
${DOCKER_DF}
Largest log files:
${TOP_LOGS}"
echo "$BODY" | mail -s "ALERT: Docker disk at ${USAGE}% on $(hostname)" "$MAILTO"
logger -t docker-disk-alert "Docker disk usage at ${USAGE}% - alert sent"
fi
Establezca los permisos:
sudo chmod 750 /usr/local/bin/docker-disk-alert.sh
Verifique los permisos:
ls -la /usr/local/bin/docker-disk-alert.sh
Salida esperada:
-rwxr-x--- 1 root root 612 Mar 19 12:00 /usr/local/bin/docker-disk-alert.sh
El script requiere mailutils (o mailx) para el envío de correos. Instálelo si no está presente:
sudo apt install -y mailutils
Pruebe el script:
sudo /usr/local/bin/docker-disk-alert.sh
Si el uso de disco está por debajo del umbral, no ocurre nada. Para probar la ruta de alerta, establezca temporalmente THRESHOLD=1 en el script, ejecútelo y luego restáurelo.
Programar la alerta con un timer de systemd
Cree el servicio:
sudo nano /etc/systemd/system/docker-disk-alert.service
[Unit]
Description=Check Docker disk usage
[Service]
Type=oneshot
ExecStart=/usr/local/bin/docker-disk-alert.sh
Cree el timer:
sudo nano /etc/systemd/system/docker-disk-alert.timer
[Unit]
Description=Check Docker disk usage every 6 hours
[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true
[Install]
WantedBy=timers.target
Actívelo:
sudo systemctl daemon-reload
sudo systemctl enable --now docker-disk-alert.timer
Verifique:
sudo systemctl list-timers docker-disk-alert.timer
Solución de problemas
El disco está lleno y Docker no arranca
Si Docker se niega a arrancar porque el disco está completamente lleno:
sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log
sudo systemctl start docker
Luego configure inmediatamente la rotación de logs como se describe arriba.
Un error de sintaxis en daemon.json impide que Docker arranque
Docker no arranca si daemon.json contiene JSON inválido. Valide el archivo:
sudo python3 -m json.tool /etc/docker/daemon.json
Si muestra el JSON formateado, la sintaxis es válida. Si muestra un error, corrija la línea indicada.
Revise el mensaje de error de Docker:
sudo journalctl -u docker.service -n 20 --no-pager
Los logs siguen creciendo después de configurar la rotación
La rotación solo se aplica a contenedores nuevos. Los contenedores existentes conservan su configuración original. Recréelos:
docker compose up -d --force-recreate
Verifique que la nueva configuración tomó efecto:
docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>
docker system prune no liberó mucho espacio
docker system prune no toca los contenedores en ejecución, sus logs ni los volúmenes nombrados. Si el problema de espacio son específicamente los logs, trunque los archivos de log o configure la rotación. Si son los volúmenes, use docker volume prune después de verificar que no se perderán datos necesarios.
Revise qué está consumiendo espacio:
sudo du -sh /var/lib/docker/*
Esto desglosa el uso por subsistema de Docker: containers (logs), overlay2 (imágenes/capas), volumes y otros.
Resumen
Una configuración completa de gestión de logs de Docker en un VPS tiene cuatro capas:
- Rotación global en
/etc/docker/daemon.jsonconmax-sizeymax-filepreviene el crecimiento ilimitado de logs para todos los contenedores. - Ajustes por servicio en Docker Compose dan límites más estrictos a los servicios con mucha actividad.
- Limpieza automatizada con
docker system pruneen un timer de systemd elimina contenedores muertos, imágenes sin usar y build cache. - Monitoreo de disco con un script de alerta detecta problemas antes de que se conviertan en caídas de servicio.
Después de configurar esto, verifique que funciona: revise docker inspect en sus contenedores, ejecute docker system df para confirmar el uso actual y espere a que el timer de limpieza se ejecute al menos una vez. Revise journalctl -u docker-prune.service para confirmar que se ejecutó.
Copyright 2026 Virtua.Cloud. Todos los derechos reservados. Este contenido es una obra original del equipo de Virtua.Cloud. La reproducción, republicación o redistribución sin permiso escrito está prohibida.