Ubuntu 24.04: Disco «lleno» pero df muestra espacio — agotamiento de inodos explicado (y solucionado)

¿Te fue útil?

Estás conectado por SSH a un servidor Ubuntu 24.04 que «tiene mucho espacio» según df -h.
Sin embargo, cada despliegue falla, los logs no rotan, apt no puede desempaquetar y el kernel no para de lanzar
No space left on device como si le pagaran por cada error.

Este es uno de esos incidentes que hace dudar hasta a la gente más experimentada. El disco no está lleno.
Algo más lo está. Normalmente: los inodos. Y una vez que entiendes qué significa eso, la solución es casi aburrida.
Casi.

Qué estás viendo: «disco lleno» con GB libres

En Ubuntu, «disco lleno» suele significar una de tres cosas:

  • Agotamiento de espacio en bloques: el caso clásico; te quedaste sin bytes.
  • Agotamiento de inodos: te quedaste sin entradas de metadatos de archivos; los bytes pueden quedar libres.
  • Bloques reservados, cuotas o corrupción del sistema de archivos: tienes espacio pero no puedes usarlo.

El agotamiento de inodos es el más traicionero porque no aparece en el primer comando que todo el mundo ejecuta.
df sin opciones sólo informa bloques usados/disponibles. Puedes tener 200 GB libres y aún así
no poder crear un archivo de 0 bytes. El sistema de archivos no puede asignar un nuevo inodo, así que no puede crear un archivo nuevo.

Notarás efectos secundarios extraños:

  • No se pueden crear nuevos archivos de log, por lo que los servicios se caen o dejan de registrar justo cuando los necesitas.
  • apt falla a mitad de instalación porque no puede crear archivos temporales o desempaquetar archivos.
  • Las compilaciones de Docker empiezan a fallar en operaciones de «writing layer» aunque los volúmenes parezcan correctos.
  • Algunas aplicaciones informan «disco lleno» mientras otras siguen funcionando (porque no están creando archivos).

Hay una prueba simple: intenta crear un archivo en el sistema de archivos afectado. Si falla con «No space left»
mientras df -h muestra espacio libre, deja de discutir con df y revisa los inodos.

Inodos explicados como si gestionaras producción

Un inodo es el registro del sistema de archivos para un archivo o directorio. Es metadatos: propietario, permisos, marcas de tiempo,
tamaño, punteros a bloques de datos. En muchos sistemas de archivos, el nombre del archivo no se almacena en el inodo; vive en
las entradas del directorio que mapean nombres a números de inodo.

La verdad operativa importante: la mayoría de los sistemas Linux tienen dos «presupuestos» separados:
bloques (bytes) e inodos (cantidad de archivos). Si gastas cualquiera de los dos presupuestos, se acabó.

Por qué se agotan los inodos en sistemas reales

El agotamiento de inodos normalmente no es «muchos archivos grandes». Es «millones de archivos diminutos».
Piensa en caches, buzones de correo, artefactos de compilación, capas de contenedores, espacios de trabajo de CI, subidas temporales
y buffers de métricas que alguien olvidó expirar.

Un archivo de 1 KB todavía consume un inodo. Un archivo de 0 bytes todavía consume un inodo. Un directorio también consume un inodo.
Cuando tienes 12 millones de archivos pequeños, el disco puede estar mayormente vacío en bytes, pero la tabla de inodos está frita.

Qué sistemas de archivos tienden a darte problemas

  • ext4: común en Ubuntu; los inodos suelen crearse al formatear según una ratio de inodos. Si estimaste mal, puedes quedarte sin inodos.
  • XFS: los inodos son más dinámicos; el agotamiento es menos común pero no imposible.
  • btrfs: la asignación de metadatos es diferente; aún puedes tener problemas de espacio para metadatos, pero no es la historia de «conteo fijo de inodos».
  • overlayfs (Docker): no es un tipo de sistema de archivos por sí mismo, pero amplifica el comportamiento de «muchos archivos» en hosts con muchos contenedores.

Una cita que vale la pena mantener en tu runbook mental:

“La esperanza no es una estrategia.” — General Gordon R. Sullivan

Guía rápida de diagnóstico (primero/segundo/tercero)

Cuando la alerta dice «No space left on device» pero las gráficas indican que estás bien, no divagues.
Sigue la guía.

Primero: confirma qué «espacio» está realmente agotado

  1. Revisa bloques (df -h) e inodos (df -i) para el montaje afectado.
  2. Intenta crear un archivo en ese montaje; confirma la ruta de error.
  3. Revisa si hay un remonte en solo lectura o errores del sistema de archivos en dmesg.

Segundo: encuentra el montaje y los mayores consumidores de inodos

  1. Identifica qué ruta del sistema de archivos está fallando (logs, temp, directorio de datos).
  2. Busca directorios con alto conteo de archivos usando find y un par de conteos dirigidos.
  3. Si es Docker/Kubernetes, revisa overlay2, imágenes, contenedores y logs.

Tercero: libera inodos de forma segura

  1. Empieza con limpiezas obvias y seguras: logs antiguos, caches, archivos temporales, vacuum del journal, basura de contenedores.
  2. Elimina archivos, no directorios, si la aplicación espera una estructura de directorios.
  3. Confirma que el uso de inodos baja (df -i) y que los servicios se recuperan.

No necesitas heroísmos. Necesitas un plan de eliminación controlado y un postmortem que impida que vuelva a pasar.

Tareas prácticas: comandos, significado de salidas, decisiones

A continuación hay tareas reales que puedes ejecutar en Ubuntu 24.04. Cada una incluye qué observar y qué decisión tomar.
Hazlas en orden si estás de guardia; escoge si ya conoces el montaje afectado.

Tarea 1: Confirma uso de bloques (la comprobación obvia)

cr0x@server:~$ df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/sda2      ext4   200G   62G  128G  33% /
tmpfs          tmpfs  3.1G  1.2M  3.1G   1% /run
/dev/sdb1      ext4   1.8T  1.1T  648G  63% /data

Significado: El espacio en bloques está bien en / y /data.
Decisión: Si aún recibes «No space left», pasa a comprobar inodos. No pierdas tiempo buscando archivos grandes todavía.

Tarea 2: Comprueba uso de inodos (la mitad de las veces es el culpable)

cr0x@server:~$ df -iT
Filesystem     Type    Inodes  IUsed   IFree IUse% Mounted on
/dev/sda2      ext4   13107200 13107190     10  100% /
tmpfs          tmpfs    790000     420 789580    1% /run
/dev/sdb1      ext4  122142720  982134 121160586   1% /data

Significado: El sistema de archivos raíz tiene IUse% 100%. Sólo 10 inodos libres. Eso impide crear archivos.
Decisión: Debes eliminar archivos en / (o moverlos) para liberar inodos. Eliminar un archivo enorme no ayudará si sigue siendo un solo inodo de todos modos.

Tarea 3: Reproduce el fallo de forma controlada

cr0x@server:~$ touch /tmp/inode-test-file
touch: cannot touch '/tmp/inode-test-file': No space left on device

Significado: El sistema de archivos no puede asignar un inodo para un archivo pequeño.
Decisión: Trátalo como un incidente de disponibilidad. Cualquier cosa que necesite escribir (logs, sockets, archivos PID, temporales) puede fallar a continuación.

Tarea 4: Identifica qué ruta está en el sistema de archivos afectado

cr0x@server:~$ findmnt -T /var/log
TARGET SOURCE    FSTYPE OPTIONS
/      /dev/sda2 ext4   rw,relatime,errors=remount-ro

Significado: /var/log está en /. Si los logs explotaron en millones de archivos, ese es tu campo de batalla.
Decisión: Centra la búsqueda bajo /var, /tmp, /var/lib y cualquier directorio de aplicaciones en /.

Tarea 5: Detecta los sospechosos habituales por tamaño (bytes)

cr0x@server:~$ sudo du -xh --max-depth=1 /var | sort -h
12M	/var/cache
180M	/var/log
2.1G	/var/lib
2.4G	/var

Significado: El uso en bytes no es extremo. Esta es tu primera pista de que el problema es el conteo de archivos, no el tamaño.
Decisión: Deja de optimizar por GB. Empieza a optimizar por cantidad de archivos.

Tarea 6: Encuentra directorios con gran número de archivos (barrido de alto nivel)

cr0x@server:~$ sudo bash -lc 'for d in /var/* /tmp /home; do [ -d "$d" ] && printf "%s\t" "$d" && find "$d" -xdev -type f 2>/dev/null | wc -l; done | sort -n -k2 | tail -n 10'
/var/cache	1320
/var/log	5402
/var/lib	12877190
/tmp	120
/home	88

Significado: /var/lib tiene ~12.8 millones de archivos. Eso no es «un poco desordenado»; son tus inodos.
Decisión: Enfócate en /var/lib. Si este es un host de contenedores, espera encontrar /var/lib/docker o /var/lib/containerd.

Tarea 7: Filtra rápidamente dentro de /var/lib

cr0x@server:~$ sudo bash -lc 'for d in /var/lib/*; do [ -d "$d" ] && printf "%s\t" "$d" && find "$d" -xdev -type f 2>/dev/null | wc -l; done | sort -n -k2 | tail -n 10'
/var/lib/systemd	2200
/var/lib/dpkg	9800
/var/lib/docker	12866012

Significado: Docker se está comiendo tu presupuesto de inodos mediante capas, caché de compilación y logs de contenedores.
Decisión: Decide si puedes ejecutar prune de forma segura ahora mismo. Si este host es de propósito único, hacer prune suele ser correcto. Si es un servidor «pet» con cargas desconocidas, sé más cauto.

Tarea 8: Revisa la vista de Docker (bytes, no inodos, pero útil)

cr0x@server:~$ sudo docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          48        12        23.4GB    18.1GB (77%)
Containers      65        18        4.2GB     2.9GB (69%)
Local Volumes   26        20        11.8GB    1.1GB (9%)
Build Cache     152       0         6.4GB     6.4GB

Significado: Hay bastante para podar, pero esta salida no muestra el conteo de archivos.
Decisión: Procede con acciones de prune si entiendes el impacto: contenedores detenidos, imágenes huérfanas, caché de compilación.

Tarea 9: Prune de Docker de forma segura (empieza conservador)

cr0x@server:~$ sudo docker image prune -a -f
Deleted Images:
deleted: sha256:1a2b3c...
deleted: sha256:4d5e6f...
Total reclaimed space: 17.8GB

Significado: Imágenes no usadas eliminadas; el conteo de inodos también debería bajar porque las capas contienen muchos archivos pequeños.
Decisión: Vuelve a comprobar df -i. Si el uso de inodos no baja lo suficiente, elimina contenedores y caché de compilación a continuación.

Tarea 10: Podar la caché de compilación (a menudo una fábrica silenciosa de inodos)

cr0x@server:~$ sudo docker builder prune -a -f
Deleted build cache objects:
k2r1m3n4o5p6...
Total reclaimed space: 6.4GB

Significado: Los hosts de CI/compilación suelen dejar fragmentos de caché atrás.
Decisión: Si necesitas builds reproducibles y reconstrucciones rápidas, programa esta limpieza en lugar de ejecutarla constantemente. Por ahora, estás estabilizando el nodo.

Tarea 11: Confirma la recuperación de inodos (esta es tu métrica de éxito)

cr0x@server:~$ df -i /
Filesystem     Inodes   IUsed    IFree IUse% Mounted on
/dev/sda2     13107200 8123400  4983800   63% /

Significado: Has vuelto del borde del precipicio. Los servicios pueden crear archivos de nuevo.
Decisión: Reinicia los servicios que se cayeron por escrituras fallidas y coloca guardarraíles para que no vuelva a ocurrir.

Tarea 12: Si los logs son el culpable, límpialos y establéceles límites (systemd-journald)

cr0x@server:~$ sudo journalctl --disk-usage
Archived and active journals take up 1.7G in the file system.

Significado: Los journals no suelen ser especialmente intensivos en inodos (son archivos menos numerosos y más grandes), pero pueden presionar por bytes.
Decisión: Si importan los bytes también, haz vacuum. Si importan los inodos, céntrate en aplicaciones que crean muchos archivos de log separados, no en el journal en sí.

cr0x@server:~$ sudo journalctl --vacuum-time=7d
Vacuuming done, freed 1.2G of archived journals from /var/log/journal.

Significado: Bytes liberados. Inodos liberados sólo de forma modesta.
Decisión: Establece límites persistentes en la configuración de journald si este host es propenso a logs ruidosos.

Tarea 13: Si apt falla, limpia cachés de paquetes

cr0x@server:~$ sudo apt-get clean

Significado: Limpia los archivos de paquetes descargados bajo /var/cache/apt/archives.
Decisión: Buena higiene, pero normalmente no es una bala de plata para inodos. Ayuda cuando las caches contienen muchos archivos pequeños y parciales.

Tarea 14: Encuentra directorios con gran conteo de archivos usando du (estilo inodos)

cr0x@server:~$ sudo du -x --inodes --max-depth=2 /var/lib | sort -n | tail -n 10
1200	/var/lib/systemd
9800	/var/lib/dpkg
12866012	/var/lib/docker
12877190	/var/lib

Significado: Esta es la vista valiosa: consumo de inodos por directorio.
Decisión: Apunta al mayor consumidor. No «limpies un poco por todas partes». Perderás tiempo y seguirás al 100%.

Tarea 15: En caso de duda, inspecciona fan-out patológico

cr0x@server:~$ sudo find /var/lib/docker -xdev -type f -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -n | tail -n 5
  42000 /var/lib/docker/containers/8a7b.../mounts
  78000 /var/lib/docker/overlay2/3f2d.../diff/usr/lib
 120000 /var/lib/docker/overlay2/9c1e.../diff/var/cache
 250000 /var/lib/docker/overlay2/b7aa.../diff/usr/share
 980000 /var/lib/docker/overlay2/2d9b.../diff/node_modules

Significado: Una capa de contenedor con node_modules puede generar conteos de archivos absurdos.
Decisión: Arregla la compilación (multi-stage builds, eliminar dependencias de desarrollo, .dockerignore) y/o mueve el data root de Docker a un sistema de archivos diseñado para esta carga.

Tarea 16: Confirma el tipo de sistema de archivos y detalles de provisión de inodos

cr0x@server:~$ sudo tune2fs -l /dev/sda2 | egrep -i 'Filesystem features|Inode count|Inode size|Block count|Reserved block count'
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Inode count:              13107200
Inode size:               256
Block count:              52428800
Reserved block count:     2621440

Significado: ext4 tiene aquí un conteo de inodos fijo. No puedes «añadir más inodos» sin reconstruir el sistema de archivos.
Decisión: Si el trabajo de este host son «millones de archivos pequeños», planifica una migración: nuevo sistema de archivos con una ratio de inodos distinta, o una disposición de almacenamiento diferente.

Broma #1: Los inodos son como salas de reuniones: puedes tener un edificio vacío y aún estar «lleno» si cada sala está reservada con una nota pegajosa.

Tres mini-historias corporativas (anonimizadas, dolorosamente reales)

1) Incidente causado por una suposición errónea: «df dice que estamos bien»

Una compañía SaaS mediana gestionaba una flota de servidores Ubuntu que manejaban ingestión de webhooks. Los ingenieros monitorizaban el uso de disco en porcentaje,
alertaban al 80% y se sentían orgullosos: «Ya no llenamos discos». Un martes por la tarde, la canalización de ingestión empezó a devolver
500 intermitentes. Los reintentos se acumularon, las colas se llenaron, los dashboards se encendieron.

El de guardia siguió la rutina estándar: df -h parecía sano. La CPU estaba bien. La memoria no era ideal pero manejable.
Reiniciaron un servicio y murió inmediatamente porque no podía crear un archivo PID. Ese mensaje de error finalmente apareció:
No space left on device.

Alguien sugirió «quizá el disco mintió», que es una forma curiosamente humana de decir «no medimos lo correcto».
Ejecutaron df -i y encontraron el filesystem raíz al 100% de uso de inodos. El culpable no era una base de datos. Era
una «cola de reintentos temporal» implementada como un JSON por evento bajo /var/lib/app/retry/.
Cada archivo era minúsculo. Había millones.

La solución fue inmediata: borrar archivos más antiguos que un umbral y reiniciar. La solución real tomó un sprint:
migrar la cola de reintentos a un sistema de colas diseñado para eso, dejar de usar el sistema de archivos como una base de datos barata y añadir alertas de inodos.
El postmortem tuvo un título educado. El chat interno no.

2) Optimización que salió mal: «cachear todo en disco local»

Un equipo de ingeniería de datos aceleró trabajos ETL cacheando artefactos intermedios en SSD local.
Pasaron de «un artefacto por lote» a «un artefacto por partición», por paralelismo.
El rendimiento mejoró. Los costes parecieron mejores. Todos siguieron con su trabajo.

Semanas después, los nodos empezaron a fallar de forma escalonada. No todos a la vez, lo que lo hizo más difícil.
Algunos trabajos tenían éxito, otros fallaban al azar al intentar escribir salida. Los errores eran inconsistentes:
excepciones de Python, errores de E/S en Java, ocasional «sistema de archivos en solo lectura» después de que el kernel remontara un disco problemático.

La causa raíz fue vergonzosamente mecánica: el cacheado creó decenas de millones de archivos diminutos.
El sistema ext4 se había formateado con una ratio de inodos por defecto adecuada para uso general, no para «millones de shards».
Los nodos no se quedaron sin bytes; se quedaron sin identidades de archivo. La «optimización» fue efectivamente una prueba de estrés de inodos.

«Lo arreglaron» aumentando el tamaño del disco. Eso no ayudó, porque el conteo de inodos seguía fijo.
Luego reformatearon con una densidad de inodos más adecuada y cambiaron la estrategia de cache para empacar particiones en paquetes tipo tar.
El rendimiento regresó ligeramente. La fiabilidad mejoró dramáticamente. Es un intercambio razonable.

3) Práctica aburrida pero correcta que salvó el día: archivos separados y guardarraíles

Otra organización ejecutaba cargas mixtas en nodos Kubernetes: servicios del sistema, Docker/containerd y algo de espacio temporal local.
Tenían una regla: cualquier cosa que pueda explotar en cantidad de archivos tiene su propio sistema de archivos. Docker vivía en /var/lib/docker
montado desde un volumen dedicado. El espacio temporal estaba en un montaje separado con políticas agresivas de limpieza.

También tenían dos monitores aburridos: «uso de bloques» y «uso de inodos». Nada de ML sofisticado. Sólo dos series temporales y alertas que paginaban
antes del precipicio. Probaron las alertas trimestralmente creando una tormenta temporal de inodos en staging (sí, eso existe).

Un día una nueva pipeline de compilación empezó a producir capas patológicas con árboles de dependencias enormes.
Los inodos en el volumen de Docker subieron rápido. La alerta saltó temprano. El de guardia no tuvo que aprender nada nuevo bajo estrés.
Hicieron prune, revertieron la pipeline y elevaron los límites. El resto del nodo se mantuvo sano porque la raíz no se vio involucrada.

El informe del incidente fue corto. La solución fue aburrida. Todos durmieron tranquilos.
Esa es la esencia del SRE.

Datos interesantes y un poco de historia (porque explican los modos de fallo)

  • Los inodos vienen del Unix temprano: el concepto data de la concepción original del sistema de archivos Unix, donde metadatos y bloques de datos eran estructuras separadas.
  • Los sistemas ext tradicionales preasignan inodos: ext2/ext3/ext4 normalmente deciden el conteo de inodos en el momento de mkfs basándose en una ratio de bytes por inodo, no dinámicamente según la carga.
  • Las ratios de inodos por defecto son un compromiso: están pensadas para cargas de propósito general; no están adaptadas a capas de contenedores, caches de CI o explosiones de maildir.
  • Los directorios también consumen inodos: «Sólo creamos directorios» no es una defensa; cada directorio también consume un inodo.
  • «No space left on device» es un mensaje sobrecargado: la misma cadena de error puede significar falta de bloques, falta de inodos, cuota excedida o incluso un sistema de archivos remontado en solo lectura tras errores.
  • Existen bloques reservados por una razón: ext4 suele reservar un porcentaje de bloques para root, pensado para mantener el sistema usable bajo presión; no reserva inodos de la misma manera.
  • Las cargas de pequeños archivos son más difíciles de lo que parecen: las operaciones de metadatos dominan; la eficiencia de inodos y búsquedas en directorios puede importar más que el rendimiento secuencial.
  • Las imágenes de contenedores magnifican los patrones de archivos pequeños: ecosistemas de lenguajes con árboles de dependencias enormes (Node, Python, Ruby) pueden crear capas con un número masivo de archivos.
  • Algunos sistemas de archivos se movieron hacia metadatos dinámicos: XFS y btrfs manejan metadatos de forma distinta, lo que cambia la forma de los fallos «llenos», pero no los elimina.

Soluciones: desde limpieza rápida hasta prevención permanente

Estabilización inmediata (minutos): libera inodos sin empeorar la situación

Tu trabajo durante un incidente no es «hacerlo bonito». Es «hacerlo escribible otra vez» sin borrar lo incorrecto.
Aquí lo que suele ser seguro y efectivo, en orden descendente de sentido:

  • Eliminar caches efímeros conocidos (caché de compilación, caché de paquetes, archivos temporales) con comandos diseñados para ello.
  • Podar basura de contenedores si el host está cargado de contenedores y puedes tolerar eliminar artefactos no usados.
  • Expirar archivos generados por aplicaciones por antigüedad, no por suposiciones. Prefiere políticas «más antiguos que N días».
  • Mover directorios fuera del sistema de archivos si eliminar es arriesgado: archiva a otro montaje y luego borra localmente.

Cuando sea por logs: arregla el problema de cantidad de archivos, no solo la rotación

Logrotate resuelve «un archivo que crece sin parar». No resuelve automáticamente «creamos un archivo por petición».
Si tu aplicación crea archivos de log únicos por unidad de trabajo (ID de request, job, tenant), te estás causando un ataque de denegación de servicio distribuido
contra tu propia tabla de inodos.

Prefiere:

  • registro en una única secuencia con campos estructurados (JSON está bien, pero mantén la coherencia)
  • integración con journald cuando proceda
  • spooling local acotado con retención explícita

Cuando sea Docker: elige un data root que coincida con la carga

Docker sobre ext4 puede funcionar bien, hasta que no. Si sabes que un nodo va a construir imágenes, ejecutar muchos contenedores
y triturar capas, trata /var/lib/docker como un datastore de alta rotación y dale su propio sistema de archivos.

Opciones prácticas:

  • Montaje separado para /var/lib/docker con una densidad de inodos que coincida con el conteo esperado de archivos.
  • Limpieza de caché de compilación programada, no sólo manual durante incidentes.
  • Arreglar las builds de imágenes para reducir fan-out: multi-stage builds, recortar dependencias, usar .dockerignore.

Arreglo permanente: diseña el sistema de archivos para la carga

Si el agotamiento de inodos se repite, no tienes un «problema de limpieza». Tienes un problema de planificación de capacidad.
En ext4, el conteo de inodos se fija al crear el sistema de archivos. La única solución real es migrar a un sistema con más inodos
(o una disposición distinta), lo que implica:

  • crear un nuevo sistema de archivos con mayor densidad de inodos
  • mover datos
  • actualizar montajes y servicios
  • añadir monitorización y políticas de retención

Cómo crear ext4 con más inodos (migración planificada)

El conteo de inodos en ext4 se ve influido por -i (bytes por inodo) y -N (conteo explícito de inodos).
Menos bytes por inodo significa más inodos. Más inodos significa más sobrecarga de metadatos. Es un intercambio, no caramelos gratis.

cr0x@server:~$ sudo mkfs.ext4 -i 16384 /dev/sdc1
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 976754176 4k blocks and 61079552 inodes
Filesystem UUID: 9f1f4a1c-8b1d-4c1b-9d88-8d1aa14d4e1e
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done

Significado: Este ejemplo crea muchos más inodos que la ratio por defecto en el mismo tamaño de volumen.
Decisión: Usa esto sólo cuando sepas que necesitas muchos archivos. Para datos secuenciales grandes, no desperdicies metadatos.

Broma #2: Si tratas el sistema de archivos como una base de datos, eventualmente te cobrará en inodos.

Errores comunes: síntoma → causa raíz → arreglo

1) «df muestra 30% usado pero no puedo escribir archivos» → agotamiento de inodos → comprobar y eliminar puntos calientes de archivos pequeños

  • Síntoma: las escrituras fallan, touch falla, apt falla, servicios se caen al crear archivos temporales.
  • Causa raíz: df -i muestra 100% de uso de inodos en el montaje.
  • Arreglo: identifica directorios con mayor conteo de archivos con du --inodes / find ... | wc -l, elimina archivos efímeros seguros y evita recurrencias.

2) «Eliminé un archivo enorme, sigue roto» → liberaste bloques, no inodos → elimina muchos archivos en su lugar

  • Síntoma: los GB libres aumentan, pero «No space left» continúa.
  • Causa raíz: el uso de inodos no cambió.
  • Arreglo: libera inodos eliminando por cantidad de archivos, no por tamaño. Apunta a caches, spools y artefactos de compilación.

3) «Sólo root puede escribir; usuarios no» → bloques reservados o cuotas → verificar con tune2fs y herramientas de cuotas

  • Síntoma: root puede crear archivos, no-root no.
  • Causa raíz: porcentaje de bloques reservados en ext4, o cuotas de usuario alcanzadas.
  • Arreglo: revisa bloques reservados con tune2fs; revisa cuotas; ajusta con cuidado. No pongas 0% de bloques reservados en particiones del sistema a ciegas.

4) «Se volvió de solo lectura y ahora todo falla» → errores del sistema de archivos → investigar dmesg, ejecutar fsck (offline)

  • Síntoma: el kernel remontó con errors=remount-ro, las escrituras fallan con errores de solo lectura.
  • Causa raíz: errores de I/O o corrupción del sistema de archivos, no capacidad.
  • Arreglo: inspecciona dmesg; planifica un reinicio en modo recuperación y ejecuta fsck. La limpieza de capacidad no arreglará la corrupción.

5) «Nodo Kubernetes con DiskPressure pero df parece bien» → presión de inodos del runtime de contenedores → podar y separar montajes

  • Síntoma: pods expulsados; kubelet se queja; nodo inestable.
  • Causa raíz: directorios del runtime llenan el presupuesto de inodos (overlay2, logs).
  • Arreglo: podar almacenamiento del runtime, aplicar recolección de basura de imágenes, poner el runtime en un volumen dedicado con monitorización.

6) «Limpiamos /tmp; ayudó una hora» → la aplicación recrea la tormenta → arregla la retención en la fuente

  • Síntoma: incidentes de inodos repetidos tras la limpieza.
  • Causa raíz: bug de la aplicación, diseño malo (un archivo por evento) o falta de TTL/rotación.
  • Arreglo: añade política de retención, rediseña el almacenamiento (base de datos/cola/objeto), aplica límites y alertas.

Listas de verificación / plan paso a paso

Checklist de guardia (estabilizar en 15–30 minutos)

  1. Ejecuta df -hT y df -iT para el montaje afectado.
  2. Confirma con touch en la ruta afectada.
  3. Encuentra el mapeo de montaje con findmnt -T.
  4. Identifica los mayores consumidores de inodos con du -x --inodes --max-depth=2 y find ... | wc -l dirigidos.
  5. Elige una acción de limpieza que sea segura y de alto impacto (prune de Docker, limpieza de caches, eliminación por tiempo).
  6. Revisa df -i hasta que estés por debajo de ~90% en el montaje crítico.
  7. Reinicia servicios impactados (sólo después de que las escrituras funcionen).
  8. Captura evidencia: comandos ejecutados, conteos de inodos antes/después, directorios responsables.

Checklist de ingeniería (prevenir recurrencias)

  1. Añade monitorización y alertas de inodos por sistema de archivos (no sólo uso de disco en %).
  2. Pon directorios de alta rotación en montajes dedicados: /var/lib/docker, spool de aplicaciones, caché de compilación.
  3. Implementa retención en el productor: TTLs, límites, compactación periódica o un backend distinto.
  4. Revisa builds e imágenes: reduce el conteo de archivos por capa; evita vendorizar enormes árboles de dependencias en imagenes de runtime.
  5. Si usas ext4 para cargas de pequeños archivos, planifica la densidad de inodos al formatear y documenta la decisión.
  6. Realiza un game day en staging: simula presión de inodos y valida alertas y pasos de recuperación.

Checklist de migración (cuando el conteo de inodos de ext4 es fundamentalmente incorrecto)

  1. Mide el conteo actual de archivos y la tasa de crecimiento (nuevos archivos diarios, comportamiento de retención).
  2. Elige el objetivo: ext4 con mayor densidad de inodos, XFS u otra arquitectura (almacenamiento de objetos, base de datos, cola).
  3. Provisiona un nuevo volumen y sistema de archivos; móntalo en la ruta prevista.
  4. Detén la carga, copia datos (preservando dueño/permisos), valida y haz el corte.
  5. Rehabilita la carga con valores de retención por defecto habilitados desde el primer día.
  6. Deja guardarraíles: alertas, timers de limpieza y una política de límite duro.

Preguntas frecuentes

1) ¿Qué es un inodo en una frase?

Un inodo es un registro de metadatos del sistema de archivos que representa un archivo o directorio; necesitas un inodo libre para crear un nuevo archivo.

2) ¿Por qué Ubuntu dice «No space left on device» cuando df -h muestra espacio libre?

Porque «espacio» puede significar bytes (bloques) o metadatos de archivo (inodos). df -h muestra bloques; df -i muestra inodos.

3) ¿Cómo confirmo rápidamente el agotamiento de inodos?

Ejecuta df -i en el montaje y busca IUse% 100%, luego intenta touch para confirmar que la creación de archivos falla.

4) ¿Puedo aumentar el conteo de inodos en un ext4 existente?

No de forma práctica en caliente. El conteo de inodos de ext4 se define en la creación del sistema de archivos. La solución real es migrar a un sistema con más inodos.

5) ¿Por qué los contenedores hacen más probable el problema de inodos?

Las capas de imagen y los árboles de dependencias pueden contener un número enorme de archivos pequeños. El almacenamiento por overlay multiplica operaciones de metadatos, y las cachés de compilación se acumulan silenciosamente.

6) ¿Es seguro borrar un directorio grande?

A veces. Es más seguro eliminar archivos acotados por tiempo bajo una ruta efímera conocida que borrar un directorio que tu servicio espera. Prefiere eliminaciones dirigidas y verifica las configuraciones de servicio.

7) ¿Qué debo monitorizar para detectar esto temprano?

Monitoriza el uso de inodos por sistema de archivos (df -i) y alerta sobre crecimientos sostenidos y niveles críticos (por ejemplo, 85% y 95%), no sólo el % de disco usado.

8) Si me quedo sin inodos, ¿debo reiniciar?

Reiniciar no crea inodos. Puede limpiar temporalmente algunos archivos temporales, pero no es una solución y puede complicar la investigación. Libera inodos de forma deliberada.

9) ¿Por qué a veces afecta sólo a una aplicación?

Porque sólo las aplicaciones que necesitan crear archivos están bloqueadas. Los servicios de solo lectura pueden seguir funcionando hasta que intenten escribir logs, sockets o estado.

10) ¿Los logs de journald suelen causar agotamiento de inodos?

Menos comúnmente que los logs de «un archivo por evento». journald tiende a almacenar datos en archivos menos numerosos y más grandes, lo que presiona más por bloques que por inodos.

Siguientes pasos que deberías hacer

Si sólo adoptas un hábito: cuando veas «No space left on device», ejecuta df -i con la misma naturalidad que df -h.
El sistema de archivos tiene dos límites, y la producción no perdona cuál olvidaste monitorizar.

Pasos prácticos:

  1. Añade alertas de inodos en cada montaje persistente (especialmente / y el almacenamiento del runtime de contenedores).
  2. Mueve rutas de alta rotación a sistemas de archivos dedicados para que una carga defectuosa no deje inservible todo el nodo.
  3. Arregla el comportamiento del productor: retención, TTLs, menos archivos, mejor empaquetado de artefactos.
  4. Para ext4, planifica la densidad de inodos desde el inicio para cargas de archivos pequeños y documenta la razón.
  5. Realiza un game day pequeño: crea una tormenta controlada de inodos en staging, verifica alertas y el playbook de recuperación.

No necesitas más disco. Necesitas menos archivos, mejores controles de ciclo de vida y una disposición del sistema de archivos que coincida con la realidad en lugar de con suposiciones.

← Anterior
Seguridad futura de la CPU: ¿Se acabaron las sorpresas tipo Spectre?
Siguiente →
Ubuntu 24.04: DKMS falló tras actualizar el kernel — recuperar controladores sin tiempo de inactividad

Deja un comentario