Guía9 min

Logging json-file de Docker para un agente IA: max-size o el host muere

Resumen

Cómo no llenar /var/lib/docker con stdout del bot: logging json-file max-size y max-file en Compose. Distinto de wrangler tail. El driver no rota solo. Techos oficiales Docker json-file, local y none, verificados con curl el 3 de septiembre de 2026.

Docker
Rotacion de logs json-file de Docker junto a un agente que escribe stdout

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

El agente loguea cada tool. json-file (default) crece sin tope en el host. Disco del volumen es /data. Esto es /var/lib/docker/containers/*/json.log. Fuentes oficiales consultadas el 3 de septiembre de 2026.

Logs runtime cubre Vercel/wrangler/railway CLI. Aquí: el driver del daemon.

La regla

logging:
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

30 MB por servicio, no 30 GB. docker logs sigue funcionando.

No none en prod: no hay postmortem. No journald “porque systemd” si no sabes leer journalctl -u docker.

Qué no va al json.log

Secretos. Bodies de Telegram. API keys. El driver no filtra. Recorta en la app (NODE_ENV).

Adjuntos / dumps: no console.log(buffer).

Fly / Railway / Vercel

Ellos retegan. No uses json-file ahí. Esta guía es VPS/Compose. docker logs --tail en el VPS; en Fly fly logs.

Tabla

Driver json-file con tope versus sin tope

DriverTopedocker logs
json-file defaultnosí, hasta llenar disco
json-file + max-size
localsí (docs)
nonen/ano

Errores comunes

Host lleno por json.log del contenedor

SíntomaCausaFix
Host 100%, volumen okjson.logmax-size
docker logs vacíodriver nonejson-file
Leak de token en logappno loguear env
max-size ignoradomal indent yamlbajo logging.options
Fly “json-file”no aplicafly logs

Relación con el resto

Checklist

  • max-size + max-file en Compose
  • No secrets en stdout
  • du de json.log en el host
  • Ensayo: llena 11 MB de log, el archivo rota
  • No driver none en prod
  • Documenta docker logs -f

FAQ

¿local vs json-file? local tiene rotación por default (docs). json-file + options es lo que más tutoriales usan. Elige uno y pon tope.

¿syslog? Otro hop. Empieza por max-size.

¿K8s? El kubelet retega; no es json-file del daemon en el node igual. Distinto runbook.

Dónde está el archivo

/var/lib/docker/containers/<id>/<id>-json.log. docker inspect --format '{{.LogPath}}'. du -h ese path. docker logs --since 1h no borra el archivo.

Un docker compose down no siempre borra logs si el contenedor queda. docker rm sí. Restart policy unless-stopped deja el mismo id y el mismo log (con rotación).

El ensayo: yes | head al stdout del contenedor (Preview) hasta pasar max-size; ls -lh *json.log*.

Un bot verbose a las 3 AM es un ataque de disco accidental. 10m × 3 es barato.

docker system df no sustituye mirar el json.log. Mira el path. docker compose logs lee el mismo driver: si rotó, el historial corto es esperado, no un bug de la app.

json-file: techos oficiales

Driver json-file (hoy): max-size es entero positivo más unidad k / m / g (sin importar mayúsculas). Default -1 (unlimited). Sin esa opción no hay rotación. max-file es entero positivo; default 1. Solo aplica si también hay max-size. Si pones solo max-file: "3", el daemon no rota.

compress de archivos ya rotados: default false. No esperes gzip gratis.

Ejemplo documentado: --log-opt max-size=10m y --log-opt max-file=3 — tres archivos, 10 MB cada uno. En daemon.json las opciones van como strings: "max-file": "3" con comillas. Un número JSON crudo es el bug.

Cambiar daemon.json no mueve contenedores ya creados. Hay que recrearlos. docker compose up -d --force-recreate del servicio, no un reload del daemon.

Default del daemon: json-file. Compruébalo con docker info --format '{{.LoggingDriver}}'. Por contenedor: docker inspect -f '{{.HostConfig.LogConfig.Type}}'.

local vs json-file

Driver local: formato interno, no JSON línea a línea. Default 20m por archivo y 5 archivos = 100 MB por contenedor, con compresión encendida. json-file default es ilimitado y sin compress. Por eso “cambiar a local y olvidarte” funciona; json-file hay que escribir el tope.

docker logs lee ambos. No edites a mano los archivos del driver: la doc avisa interferencia.

En bash, local es palabra reservada. En scripts, cita el driver: --log-driver local.

blocking vs non-blocking

Default: entrega blocking del stdout al driver. Un disco lento bloquea el proceso del bot. mode=non-blocking más max-buffer-size (default 1m, un millón de bytes) evita que el agente se cuelgue escribiendo logs. Buffer lleno: se tiran líneas nuevas. Un agente chatty en non-blocking pierde el postmortem. Para VPS con json-file en disco local, blocking más max-size es el default sano.

Ejemplo documentado: --log-opt mode=non-blocking --log-opt max-buffer-size=4m. No lo copies a prod del webhook sin saber qué estás tirando.

none

--log-driver none: docker logs vacío. No hay postmortem. No lo uses en prod del bot.

Compose logging.driver es por servicio. El ejemplo oficial usa syslog; para el VPS usa json-file o local con options. El default y los valores disponibles son específicos de la plataforma: no copies un driver: syslog de un gist a un VPS sin syslogd.

Recrear, no recargar

El daemon no aplica log-opts nuevos a contenedores vivos. unless-stopped (restart) deja el mismo id y el mismo json.log. Recrear el servicio. Un kill -HUP dockerd no rota el archivo actual.

labels y env del driver json-file se configuran en el daemon, no en el yaml del bot. Sirven para tags avanzados. No meten el token de Telegram en el log: eso lo hace console.log(process.env).

Unidades de max-size: k, m, g. 10mb no es el formato. 10m sí.

Fly, Railway y Vercel retegan ellos. No copies este yaml a wrangler. Esta guía es VPS/Compose. En el VPS, du del json.log; en Fly, fly logs.

Siguiente paso: si el driver ya rota y el volumen sqlite igual se llena, disco. Sin runtime: curso.