Guía9 min

Disco lleno en un agente IA: volúmenes, sqlite y logs

Resumen

Cómo no tumbar el bot porque el volumen de 1 GB se llenó de WAL, logs y adjuntos: tamaño de volúmenes en Fly y Railway, logs de Docker que no rotan, y qué hacer cuando sqlite crece más que el disco. Backup copia; esto evita el ENOSPC.

DockerRailway
Volumen de disco al límite junto a un agente que ya no puede escribir

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.

ENOSPC: no space left on device no es un bug del modelo. El volumen de 1 GB se llenó de -wal, jsonl de logs y PDFs que el agente “iba a borrar”. Backup copia el dato. Esta guía cubre no quedarte sin bloques. Fuentes oficiales consultadas el 3 de septiembre de 2026.

La regla

Tres cubos en el mismo disco:

  1. Estado (sqlite / uploads) — debe caber y tener margen.
  2. Logs — rotan o se van al proveedor (logs runtime).
  3. Basura temporal — /tmp del turno, no eterno.

Si los tres viven en el mismo volumen de 1 GB, el cubo 2 mata al 1.

Fly / Railway

El volumen tiene tamaño fijo. Fly documenta 1 GB por defecto al crear (fly volumes create / fly launch) y un techo de 500 GB. Se agranda con fly volumes extend <id> -s <GB>; no se puede encoger. Un volumen se ata a una Machine: 1:1, una Machine un volumen. Réplicas extra = volúmenes extra, o sqlite se pelea. Snapshots: retención por defecto 5 días; el ID de un volumen borrado solo se consulta 24 h. Precio documentado: USD 0,15/GB-mes de capacidad provisionada, prorrateado a la hora, cobrado aunque la Machine esté parada.

Railway: el volumen se dimensiona al crear. Live resize (Hobby y Pro) crece sin downtime; si ya está al 100%, el resize pasa a offline y reinicia el servicio. Precio de volumen: USD 0,15/GB-mes. Techo por plan (docs de plans, por servicio, incluye réplicas): Free/Trial 0,5 GB; Hobby 5 GB; Pro 1 TB self-serve. Variables inyectadas: RAILWAY_VOLUME_NAME y RAILWAY_VOLUME_MOUNT_PATH. El build y el pre-deploy no montan el volumen: un write en build a /data no persiste. Si la app escribe en ./data, el mount suele ser /app/data (Railway pone el código en /app).

Backups nativos de Railway: manuales limitados al 50% del tamaño del volumen. Si el sqlite ya ocupa 800 MB de 1 GB, el backup nativo falla hasta que crezcas el disco. Diarios se guardan 6 días; semanales 1 mes; mensuales 3 meses. Restaurar desmonta el volumen actual y monta uno nuevo con el stamp; borrar el volumen borra los backups. Un sqlite de 800 MB en 1 GB + WAL + un dump local = ENOSPC en el siguiente turno.

Mide df / el panel antes de que falle el health. Alertar a 70% es más barato que el incidente.

Docker en VPS

Sin volumen nombrado, los writes van al overlay del contenedor y se pierden al recrear — o llenan el disco del host. El driver por defecto json-file anota cada línea con origen y timestamp. max-size vale -1 (ilimitado) si no lo pones; max-file solo aplica si también hay max-size (default 1). Ejemplo documentado: max-size=10m y max-file=3. Esos archivos los toca el daemon; no los recortes con rm a mano. Pon rotación o manda logs a stdout y que el runtime los retenga.

No montes el repo entero como volumen.

sqlite

WAL: *-wal y *-shm junto al .db. Checkpoint automático al llegar a 1000 páginas (salvo que compiles otro umbral). Un VACUUM a veces necesita espacio extra (copia). Si el disco está al 95%, VACUUM falla. Deja ~2× el tamaño de la db o mueve a un volumen más grande antes. WAL no funciona en filesystem de red: todos los procesos tienen que compartir el host. No cambies page_size con WAL activo.

Adjuntos: no los metas en sqlite BLOB si pesan. Disco + retención (borrar a N días) o S3/R2.

Tabla

Estado, logs y temporales peleando el mismo volumen

CuboDóndeTecho verificado 2026-09-03
sqlitevolumen /dataFly default 1 GB, max 500 GB; Railway Hobby 5 GB
WAL / -shmjunto al .dbcheckpoint ~1000 páginas; no borrar en caliente
Logs appstdout / archivorotación o proveedor
Docker json-filehostmax-size default ilimitado
Backup Railwaymismo volumenmanual ≤ 50% del tamaño
Backup localotro discono en el mismo 1 GB

Errores comunes

Healthcheck verde con disco al 100%

SíntomaCausaFix
SQLITE_FULL / ENOSPCvolumen chico + WALvolumen más grande o VACUUM con margen
Host lleno, contenedor “ok”logs json-filemax-size
Backup llena proddump en el mismo volumenoffsite (backup)
Recrear contenedor borra estadooverlay, no volumevolumen nombrado
Health 200, writes fallanhealth no chequea dfalerta de disco, no solo HTTP

Relación con el resto

Checklist

  • Tamaño del volumen escrito (GB)
  • sqlite + WAL caben con margen ≥ 2×
  • Logs no van a archivo eterno en /data
  • Docker max-size o stdout
  • Backup fuera del volumen de prod
  • Alerta a ~70% de uso
  • Ensayo: df en el box del agente, no en tu laptop

FAQ

¿Borrar WAL a mano? No. Cierra conexiones; sqlite lo checkpoint. Borrar archivos -wal con el proceso vivo corrompe.

¿Más réplicas? Dos writers, un disco. Peor. Ver concurrencia.

¿R2/S3 para adjuntos? Sí, en cuanto pesen. El volumen es para el db chico.

El ensayo: llena Preview con basura hasta 80% (archivo dummy), confirma la alerta, bórralo. En prod no improvises.

Qué no hacer

No “limpies” prod a ciegas borrando /data/*.db-wal. No subas el volumen 10× “por si el RAG crece”: el RAG grande no debería vivir en sqlite local. No dejes docker logs sin tope en un VPS de 20 GB que también tiene imágenes.

Workers y Vercel Functions no te dan un volumen de 1 GB. Si tu agente necesita disco, no está en un Worker. El patrón serverless es: estado en D1/KV/Postgres gestionado, no ENOSPC en overlay.

Compose: tmpfs para /tmp del turno evita que un PDF de 400 MB se quede en el overlay. El estado sigue en el volumen nombrado.

Un volumen no “se estira solo” porque el agente sea listo. El listo eres tú midiendo df.

Siguiente paso: si el disco ya tiene margen y igual se pierde estado, backup. Sin runtime: curso.