La alerta dice “filesystem full”, la aplicación empieza a devolver 500s y tu base de datos —antes confiada— ahora actúa como si hubiese olvidado cómo escribir.
Liberaste “algo de espacio” y reiniciaste todo, y ahora la BD o bien se niega a arrancar, está re-aplicando WAL/redo indefinidamente, o peor: arranca y miente.
Esta es una guía de recuperación para Debian 13 cuando ENOSPC (no queda espacio en el dispositivo) impacta tu base de datos en producción. No teoría. No “simplemente borra logs”.
Un runbook real: cómo encontrar qué está realmente lleno, cómo liberar espacio sin empeorar la consistencia y cómo llegar a un estado seguro y verificado.
Qué hace realmente “filesystem full” a las bases de datos
“Disco lleno” no es un único modo de fallo. Es una familia de fallos que se ven igual en un panel de control y que se comportan de forma muy distinta a las 03:00.
En Linux puedes estar “lleno” porque se agotaron bloques, porque se agotaron inodos, porque un proceso no puede asignar por cuotas,
o porque tu sistema de ficheros tiene espacio pero la base de datos necesita espacio más o menos contiguo para sus mecanismos de seguridad (piensa en WAL, redo logs, archivos temporales).
El momento más peligroso son los primeros minutos después de ENOSPC. Las bases de datos responden:
- Fallo de escrituras a mitad de transacción, dejando un estado parcial que debe revertirse o re-aplicarse.
- Fallo de fsync. Ahí es cuando entras en territorio “yo pensé que era durable”.
- Rotación de logs deficiente. Algunos motores siguen escribiendo en el mismo descriptor y no “ven” que liberaste espacio hasta reiniciar.
- Bloqueo en la recuperación porque la propia recuperación necesita espacio temporal.
- Ruptura de replicación porque WAL/binlogs no pueden archivarse o transmitirse.
Tu trabajo es restaurar el invariante que la BD espera: suficiente espacio libre para completar la recuperación tras un fallo, aplicar/revertir, hacer checkpoint y reanudar los patrones normales de escritura.
“Suficiente” no es “unos cientos de MB”. En producción, apunto a al menos 10–20% libre en el volumen de la BD como margen mínimo operativo.
Si no puedes llegar ahí, tratas esto como una emergencia de capacidad, no como limpieza.
Una cita que vale la pena recordar es una idea parafraseada de Werner Vogels (CTO de Amazon): todo falla, así que diseña para detectar fallos rápidamente y recuperarte automáticamente
.
Los eventos de disco lleno son la forma más aburrida de fallo —y la más humillante, porque también son las más previsibles.
Protocolo de diagnóstico rápido (primero/segundo/tercero)
Estás bajo presión. Necesitas una secuencia corta que identifique el cuello de botella rápidamente y evite “limpiezas aleatorias” que borren evidencia o dificulten la recuperación.
Haz esto en orden. No te inventes soluciones hasta haber hecho lo básico.
Primero: confirma qué tipo de “lleno” tienes
- ¿Bloques llenos? Comprueba
df -hen el montaje de la BD. - ¿Inodos llenos? Comprueba
df -i. El agotamiento de inodos se siente como “lleno” incluso sidf -hparece bien. - ¿Ficheros borrados pero abiertos? Comprueba
lsof +L1. Puedes “borrar” logs y no recuperar nada.
Segundo: identifica a los que más escriben, no los archivos más grandes
- Revisa el crecimiento reciente en logs/journals:
journalctl --disk-usage,duen/var/log. - Revisa directorios temporales y uso temporal de la BD:
/tmp,/var/tmp, equivalentes abase/pgsql_tmpde Postgres. - Revisa capas e imágenes de contenedores si aplica:
docker system df(o tu runtime).
Tercero: decide tu estrategia de recuperación
- Si la BD no arranca: libera espacio primero, luego arranca la BD y verifica la consistencia.
- Si la BD arranca pero da errores al escribir: mantenla en marcha solo el tiempo suficiente para capturar estado y drenar tráfico; luego arregla la capacidad.
- Si existe replicación: considera conmutar a un réplica saludable en lugar de “arreglos heroicos” en el primario.
Chiste #1: Los incidentes por disco lleno son como la gravedad—todo el mundo “no cree en ellos” hasta que se caen del tejado.
Datos interesantes y contexto histórico (puntos rápidos)
- Bloques reservados en sistemas ext: ext2/3/4 tradicionalmente reservan ~5% de bloques para root para evitar brick total; genial para servidores, confuso para humanos.
- ENOSPC no es solo bloques: el mismo error se devuelve comúnmente por agotamiento de inodos y por cuotas, por eso “df dice 40% libre” puede seguir siendo una crisis.
- El journaling no es magia: el journaling de ext4 protege la consistencia de metadata, no la corrección lógica de tu base de datos. Tu BD tiene su propio journal por una razón.
- Lección de Unix antiguo: borrar un archivo no libera espacio hasta que ningún proceso lo mantiene abierto—esto ha sido verdad por décadas y sigue causando outages modernos.
- La amplificación de escritura es real: las bases de datos pueden convertir una escritura lógica en múltiples escrituras físicas (WAL/redo + datos + índice + checkpoint). “Solo insertamos 1 GB/día” es cómo pierdes fines de semana.
- Los inodos se dimensionaron para cargas de los años 80: los valores por defecto aún pueden perjudicarte con millones de archivos pequeños (piensa caches), incluso en discos de varios TB.
- La recuperación tras fallo necesita espacio: Postgres puede necesitar espacio para replay de WAL y archivos temporales; InnoDB puede necesitar expandir logs; no puedes recuperar a base de aire.
- Las semánticas de sistemas de ficheros difieren: XFS se comporta distinto a ext4 bajo presión (y maneja deletes/ficheros abiertos de modo similar); ZFS tiene su propia cultura de “no llenar el pool más allá ~80%” por rendimiento.
Estabilizar primero: detener la hemorragia de forma segura
Cuando un sistema de ficheros está lleno, el movimiento equivocado son reinicios frenéticos. Los reinicios pueden convertir “bloqueo temporal pero consistente” en “bucle de recuperación que necesita más disco”.
Estabilizar significa: reducir escrituras, conservar evidencia y evitar que la BD haga trabajo extra hasta que tengas margen.
Tarea 1: confirma qué está fallando en la capa de servicio
cr0x@server:~$ systemctl --failed
UNIT LOAD ACTIVE SUB DESCRIPTION
● postgresql@16-main.service loaded failed failed PostgreSQL Cluster 16-main
Qué significa: systemd coincide en que tu BD está fallando, no solo tu app.
Decisión: no envíes reinicios en masa. Pasa a logs y estado del disco.
Tarea 2: captura los últimos errores relevantes antes de que roten
cr0x@server:~$ journalctl -u postgresql@16-main.service -n 200 --no-pager
Dec 30 02:11:44 db1 postgres[9123]: FATAL: could not write to log file: No space left on device
Dec 30 02:11:44 db1 postgres[9123]: PANIC: could not write to file "pg_wal/00000001000000A9000000FF": No space left on device
Dec 30 02:11:44 db1 systemd[1]: postgresql@16-main.service: Main process exited, code=exited, status=1/FAILURE
Qué significa: fallaron las escrituras de WAL. Eso no es “agradable”; es durabilidad central.
Decisión: tu primer objetivo es restaurar suficiente espacio para WAL y recuperación tras fallo.
Tarea 3: detener el tráfico o poner la BD detrás de una puerta de mantenimiento
cr0x@server:~$ systemctl stop myapp.service
cr0x@server:~$ systemctl stop nginx.service
Qué significa: estás reduciendo la presión de escritura mientras recuperas.
Decisión: si tienes una alternativa de solo-lectura, úsala; de lo contrario acepta tiempo de inactividad antes que corrupción.
Tarea 4: congelar el estado del proceso de BD (si está en bucle) en vez de matarlo de inmediato
cr0x@server:~$ systemctl kill -s SIGSTOP postgresql@16-main.service
cr0x@server:~$ systemctl status postgresql@16-main.service | sed -n '1,12p'
● postgresql@16-main.service - PostgreSQL Cluster 16-main
Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled)
Active: activating (start) since Tue 2025-12-30 02:12:01 UTC; 3min ago
Process: 10455 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect 16-main start (code=exited, status=1/FAILURE)
Main PID: 10501 (code=killed, signal=STOP)
Qué significa: el proceso está pausado, no consumiendo disco.
Decisión: haz esto si intenta recuperación repetidamente y consume el espacio que liberas; reanuda con SIGCONT cuando tengas margen.
Una regla práctica: si no sabes qué borrar, detén primero a los escritores. Siempre puedes reiniciar servicios; no puedes des-borrar archivos erróneos en medio de un incidente de consistencia.
Encontrar espacio como si importara (comandos + decisiones)
Necesitas responder cuatro preguntas rápidamente:
¿Qué sistema de ficheros está lleno? ¿Son bloques, inodos, cuotas o “borrado pero abierto”?
¿Quién está escribiendo? ¿Puedo crear espacio libre estable que permanezca libre?
Tarea 5: identifica el/los sistema(s) de ficheros llenos
cr0x@server:~$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 80G 79G 0 100% /
/dev/nvme1n1p1 ext4 1.8T 1.2T 520G 70% /var/lib/postgresql
Qué significa: el filesystem root está completamente lleno; el montaje de la BD está bien. Eso aún rompe la BD si escribe en /var/log o usa /tmp en /.
Decisión: céntrate en /, no en el volumen de BD. “El disco de la BD está al 70%” no te salva.
Tarea 6: comprobar agotamiento de inodos
cr0x@server:~$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2 5242880 5242880 0 100% /
/dev/nvme1n1p1 61054976 712345 60342631 2% /var/lib/postgresql
Qué significa: te quedaste sin inodos en /. Esto suele venir de caches, ficheros pequeños descontrolados o buzones de correo.
Decisión: borrar un archivo gigante no ayudará. Necesitas borrar muchos archivos pequeños o mover la carga.
Tarea 7: detectar ficheros borrados pero abiertos (el clásico “lo borré, ¿por qué sigue lleno?”)
cr0x@server:~$ sudo lsof +L1 | head -n 15
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
rsyslogd 721 syslog 6w REG 259,2 2147483648 0 1049883 /var/log/syslog.1 (deleted)
java 2041 app 12w REG 259,2 1073741824 0 1051122 /var/log/myapp/app.log (deleted)
Qué significa: el espacio sigue retenido por procesos en ejecución. El nombre del archivo desapareció, pero el inode sigue asignado.
Decisión: reinicia o envía señal a esos procesos para que cierren/reabran logs (por ejemplo, systemctl restart rsyslog), o trunca vía /proc/<pid>/fd/<fd> si es imprescindible.
Tarea 8: encuentra qué directorio en el filesystem lleno es el culpable
cr0x@server:~$ sudo du -xhd1 / | sort -h
0 /boot
1.2G /etc
2.8G /home
4.5G /opt
9.9G /usr
12G /var
49G /
Qué significa: /var es grande en root; eso suele ser logs, caches, spool o basura de contenedores.
Decisión: profundiza en /var a continuación, permaneciendo en el mismo sistema de ficheros (-x importa).
Tarea 9: identifica los mayores consumidores dentro de /var
cr0x@server:~$ sudo du -xhd1 /var | sort -h
120M /var/cache
260M /var/tmp
1.1G /var/lib
1.7G /var/spool
8.4G /var/log
12G /var
Qué significa: los logs son masivos. Eso es común, pero no asumas que es seguro borrarlo todo.
Decisión: inspecciona /var/log, especialmente journals y logs de aplicaciones.
Tarea 10: comprueba el uso y límites de systemd-journald
cr0x@server:~$ journalctl --disk-usage
Archived and active journals take up 6.8G in the file system.
Qué significa: journald ocupa una porción significativa. En particiones root pequeñas, puede ganar silenciosamente la competición de “quién se comió mi disco”.
Decisión: vacía los journals a un tamaño seguro y luego establece límites persistentes para que esto no se repita.
Tarea 11: encuentra archivos grandes rápidamente (triage por tamaño)
cr0x@server:~$ sudo find /var/log -xdev -type f -size +200M -printf '%s %p\n' | sort -n | tail -n 10
268435456 /var/log/journal/3b2b1a.../system@0000000000000000-0000000000000000.journal
536870912 /var/log/myapp/app.log
2147483648 /var/log/syslog.1
Qué significa: tienes algunos ficheros muy grandes. Ese es el tipo de limpieza más fácil—siempre que no estén abiertos.
Decisión: si los logs están abiertos, reinicia el logger/app después de truncar/rotar correctamente.
Tarea 12: comprueba caches de paquetes (relativamente seguro de eliminar)
cr0x@server:~$ sudo du -sh /var/cache/apt/archives
1.1G /var/cache/apt/archives
Qué significa: la caché de apt es notoria.
Decisión: limpiarla es en general seguro y rápido; no cambia el estado de la BD.
Tarea 13: comprueba si el volumen de la BD tiene bloat oculto (WAL, temp, backups)
cr0x@server:~$ sudo du -sh /var/lib/postgresql /var/lib/postgresql/* 2>/dev/null | sort -h | tail -n 8
48G /var/lib/postgresql/16
48G /var/lib/postgresql/16/main
7.9G /var/lib/postgresql/16/main/pg_wal
Qué significa: WAL es grande pero no necesariamente está mal—podría ser que una réplica esté caída, que falle el archivado o transacciones largas.
Decisión: no borres archivos WAL manualmente. Arregla la causa (replicación/archivado) y deja que Postgres gestione la retención.
Tarea 14: comprueba presión de memoria + swap (porque la recuperación necesita RAM y tmp)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 32Gi 26Gi 1.1Gi 214Mi 4.9Gi 3.8Gi
Swap: 0B 0B 0B
Qué significa: vas justo de RAM disponible, sin swap. Algunas tareas de recuperación de BD desbordan a archivos temporales; poca RAM puede amplificar uso de disco y tiempo.
Decisión: evita ejecutar mantenimiento pesado (VACUUM FULL, OPTIMIZE) durante la recuperación; estabiliza primero.
Liberar espacio de forma segura (qué borrar y qué no)
La prioridad es crear espacio libre estable—espacio que permanezca libre después de que los servicios se reanuden.
Borrar archivos al azar puede ralentizar el arranque del sistema, perder trazas de auditoría o romper el estado del paquete. Borrar los archivos incorrectos de la BD puede acabar tu semana.
Ganas de bajo riesgo (haz esto primero)
Tarea 15: vaciar systemd journal a un tamaño acotado
cr0x@server:~$ sudo journalctl --vacuum-size=500M
Vacuuming done, freed 6.3G of archived journals from /var/log/journal.
Qué significa: recuperaste espacio real. Esto suele ser seguro durante incidentes.
Decisión: si esto libera poco, journald no es el culpable principal; sigue buscando.
Tarea 16: establecer límites de journald para no repetirlo mañana
cr0x@server:~$ sudo sed -i 's/^#SystemMaxUse=.*/SystemMaxUse=500M/' /etc/systemd/journald.conf
cr0x@server:~$ sudo systemctl restart systemd-journald
Qué significa: has limitado el crecimiento futuro de logs.
Decisión: elige un valor que encaje con tu disco y requisitos de retención; el número correcto depende de tus necesidades de respuesta a incidentes.
Tarea 17: limpiar la caché de apt
cr0x@server:~$ sudo apt-get clean
cr0x@server:~$ sudo du -sh /var/cache/apt/archives
4.0K /var/cache/apt/archives
Qué significa: recuperaste un trozo de espacio sin tocar estado de las aplicaciones.
Decisión: hazlo si necesitas alivio rápido; no es una solución al problema de raíz.
Tarea 18: rotar/truncar logs de aplicaciones desbocadas correctamente
cr0x@server:~$ sudo truncate -s 0 /var/log/myapp/app.log
cr0x@server:~$ sudo systemctl restart myapp.service
Qué significa: la truncación libera espacio inmediatamente (a menos que el archivo sea reemplazado por patrones de logrotate).
Decisión: trunca solo logs que puedas permitirte perder. Prefiere arreglos de logrotate después del incidente.
Tarea 19: resolver logs borrados pero abiertos reiniciando el demonio adecuado
cr0x@server:~$ sudo systemctl restart rsyslog.service
cr0x@server:~$ sudo lsof +L1 | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
Qué significa: ya no aparecen (o aparecen menos) ficheros borrados pero abiertos.
Decisión: si el uso de espacio no baja, tu problema no son ficheros abiertos; vuelve a comprobar df y los inodos.
Movimientos de riesgo medio (hazlos con intención)
Estos pueden ser seguros, pero no están exentos de consecuencias.
Tarea 20: limpiar caches grandes con propietario conocido (ejemplo: directorio de cache de la app)
cr0x@server:~$ sudo du -sh /var/cache/myapp
3.4G /var/cache/myapp
cr0x@server:~$ sudo rm -rf /var/cache/myapp/*
cr0x@server:~$ sudo du -sh /var/cache/myapp
12K /var/cache/myapp
Qué significa: recuperaste espacio, pero puede aumentar la carga cuando la caché se regenere.
Decisión: aceptable durante incidente, pero coordina con los responsables de la app; vigila CPU y latencia después del reinicio.
Tarea 21: limpiar core dumps (a menudo enormes y olvidados)
cr0x@server:~$ sudo coredumpctl list | head
TIME PID UID GID SIG COREFILE EXE
Tue 2025-12-30 00:13:19 UTC 8123 1001 1001 11 present /usr/bin/myapp
cr0x@server:~$ sudo du -sh /var/lib/systemd/coredump
5.2G /var/lib/systemd/coredump
cr0x@server:~$ sudo rm -f /var/lib/systemd/coredump/*
cr0x@server:~$ sudo du -sh /var/lib/systemd/coredump
0 /var/lib/systemd/coredump
Qué significa: eliminaste artefactos de diagnóstico.
Decisión: haz esto solo si ya capturaste lo necesario para depuración, o si la disponibilidad supera el detalle post-mortem.
Movimientos de alto riesgo (evitar durante la recuperación)
- Borrar archivos de base de datos bajo
/var/lib/postgresqlo/var/lib/mysqlporque “se ven grandes”. Así creas un segundo incidente. - Ejecutar mantenimiento agresivo de BD (VACUUM FULL, REINDEX DATABASE, OPTIMIZE TABLE) mientras tienes poco disco. Estas operaciones suelen necesitar más disco para terminar.
- Mover el directorio de la base de datos al vuelo sin un plan probado. Si debes mover almacenamiento, usa una parada controlada, copy/rsync, verifica, actualiza la configuración del servicio y luego arranca.
Chiste #2: La forma más rápida de liberar disco es borrar /var/lib; la forma más rápida de actualizar tu currículum es hacerlo en producción.
Pasos de recuperación de base de datos que realmente funcionan
“Realmente funcionan” aquí significa: acabas con una base de datos que arranca, acepta escrituras y es lo suficientemente consistente para confiar—respaldado por verificaciones, no por sensaciones.
Los pasos exactos dependen de tu motor, pero la forma es consistente:
crear espacio → arrancar de forma segura → confirmar integridad → arreglar la causa del crecimiento → restaurar márgenes.
Paso cero: consigue margen real
Antes de arrancar la BD otra vez, apunta a al menos:
- PostgreSQL: espacio libre ≥ WAL necesario para replay + algo de temp; apunto a 10–20% del volumen del clúster, o al menos varios GB para sistemas pequeños.
- MySQL/InnoDB: suficiente para actividad de redo/undo y tablas temporales; de nuevo, 10–20% en el filesystem que aloja datadir y tmpdir.
Tarea 22: verifica el espacio libre tras la limpieza (no des por hecho)
cr0x@server:~$ df -hT /
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 80G 62G 14G 82% /
Qué significa: ahora tienes 14G libres en root. Es un margen usable para logs, temp y recuperación.
Decisión: procede con el reinicio de la BD. Si aún estás >95%, sigue liberando espacio o expande el filesystem primero.
PostgreSQL en Debian 13 (servicio en clúster)
El empaquetado de PostgreSQL en Debian usa clústeres (ej., postgresql@16-main), y suele comportarse bien durante la recuperación—si le das espacio.
Las dos mayores trampas de Postgres por disco lleno son: borrar WAL manualmente y reinicios repetidos que nunca dejan terminar la recuperación.
Tarea 23: reanudar la BD pausada (si usaste SIGSTOP), luego iniciar limpiamente
cr0x@server:~$ sudo systemctl kill -s SIGCONT postgresql@16-main.service
cr0x@server:~$ sudo systemctl start postgresql@16-main.service
cr0x@server:~$ sudo systemctl status postgresql@16-main.service | sed -n '1,12p'
● postgresql@16-main.service - PostgreSQL Cluster 16-main
Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled)
Active: active (running) since Tue 2025-12-30 02:24:09 UTC; 3s ago
Qué significa: el servicio está activo. Aún no probado como sano, pero respira.
Decisión: revisa logs para la finalización de la recuperación y asegúrate de que acepta conexiones.
Tarea 24: confirmar en logs la finalización de la recuperación tras fallo
cr0x@server:~$ sudo journalctl -u postgresql@16-main.service -n 80 --no-pager
Dec 30 02:24:08 db1 postgres[11001]: LOG: database system was interrupted; last known up at 2025-12-30 02:10:21 UTC
Dec 30 02:24:08 db1 postgres[11001]: LOG: redo starts at A9/FF000028
Dec 30 02:24:09 db1 postgres[11001]: LOG: redo done at A9/FF9A2B30
Dec 30 02:24:09 db1 postgres[11001]: LOG: database system is ready to accept connections
Qué significa: el replay de WAL terminó y Postgres declaró disponibilidad.
Decisión: procede a comprobaciones de integridad y reintroducción gradual de la carga.
Tarea 25: comprobar conectividad y lectura/escritura básica
cr0x@server:~$ sudo -u postgres psql -d postgres -c "select now();"
now
-------------------------------
2025-12-30 02:24:31.12345+00
(1 row)
cr0x@server:~$ sudo -u postgres psql -d postgres -c "create table if not exists diskfull_probe(x int); insert into diskfull_probe values (1);"
CREATE TABLE
INSERT 0 1
Qué significa: puedes conectar y hacer commit.
Decisión: conserva esta tabla de prueba o elimínala después; la idea es verificar que las escrituras funcionan.
Tarea 26: comprobar errores persistentes de “sin espacio” a nivel SQL
cr0x@server:~$ sudo -u postgres psql -d postgres -c "select datname, temp_bytes, deadlocks from pg_stat_database;"
datname | temp_bytes | deadlocks
-----------+------------+-----------
postgres | 0 | 0
template1 | 0 | 0
(2 rows)
Qué significa: el uso de temp es actualmente mínimo; no hay artefactos obvios de contención.
Decisión: si temp_bytes está explotando, tu carga está desbordando a disco; asegúrate de que temp_tablespaces y el margen del filesystem sean adecuados.
Tarea 27: ejecutar una comprobación de integridad dirigida (no tirar de pánico con tablas enteras)
cr0x@server:~$ sudo -u postgres psql -d postgres -c "select * from pg_stat_wal;"
wal_records | wal_fpi | wal_bytes | wal_buffers_full | wal_write | wal_sync
-------------+---------+-----------+------------------+----------+----------
18234 | 112 | 12345678 | 0 | 214 | 93
(1 row)
Qué significa: el subsistema WAL está operando. Esto no garantiza ausencia de corrupción, pero es una señal de “no está quemándose ahora”.
Decisión: si sospechas corrupción, programa amcheck o restaura desde backup; no improvises en la ventana del incidente.
MySQL / MariaDB (InnoDB) en Debian 13
InnoDB maneja la recuperación tras fallo re-aplicando redo logs. Un disco lleno puede interrumpir eso y dejarte con un servicio que no arranca o que arranca en solo-lectura.
El peor movimiento es borrar ib_logfile o ibdata para “forzar” un arranque. Eso no repara; es pérdida de datos con pasos adicionales.
Tarea 28: leer los últimos mensajes de recuperación de InnoDB
cr0x@server:~$ journalctl -u mariadb.service -n 120 --no-pager
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Error: Write to file ./ib_logfile0 failed at offset 1048576.
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Error: 28 No space left on device
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Plugin initialization aborted with error Generic error
Dec 30 02:13:02 db1 systemd[1]: mariadb.service: Main process exited, code=exited, status=1/FAILURE
Qué significa: falló la escritura del redo log. La recuperación no puede avanzar sin espacio.
Decisión: libera espacio en el filesystem que contiene datadir (a menudo /var/lib/mysql) y cualquier tmpdir configurado.
Tarea 29: confirmar ubicaciones de datadir y tmpdir antes de perseguir el montaje equivocado
cr0x@server:~$ sudo my_print_defaults --mysqld | egrep -i 'datadir|tmpdir'
--datadir=/var/lib/mysql
--tmpdir=/tmp
Qué significa: tmpdir está en /. Si root está lleno, las consultas que crean tablas temporales fallarán aunque /var/lib/mysql esté bien.
Decisión: considera mover tmpdir a un filesystem más grande después de la recuperación (y pruébalo).
Tarea 30: arrancar MariaDB/MySQL y vigilar el progreso de recuperación
cr0x@server:~$ sudo systemctl start mariadb.service
cr0x@server:~$ sudo systemctl status mariadb.service | sed -n '1,14p'
● mariadb.service - MariaDB 10.11.6 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled)
Active: active (running) since Tue 2025-12-30 02:27:40 UTC; 2s ago
Qué significa: arrancó. Ahora confirma que no está silenciosamente en problemas.
Decisión: comprueba el log de errores y ejecuta consultas básicas.
Tarea 31: validar lectura/escritura básica y salud de InnoDB
cr0x@server:~$ sudo mariadb -e "select @@version, now();"
@@version now()
10.11.6-MariaDB-0+deb13u1 2025-12-30 02:28:03
cr0x@server:~$ sudo mariadb -e "create database if not exists diskfull_probe; use diskfull_probe; create table if not exists t(x int); insert into t values (1);"
cr0x@server:~$ sudo mariadb -e "show engine innodb status\G" | sed -n '1,35p'
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2025-12-30 02:28:11 0x7f9c2c1fe6c0 INNODB MONITOR OUTPUT
...
Log sequence number 123456789
Log flushed up to 123456789
Qué significa: el flush del redo log alcanzó al resto; commits básicos funcionan.
Decisión: reintroduce la carga gradualmente, monitoriza tasas de error y uso de disco de cerca.
Si la BD aún no arranca: qué hacer (y qué no)
Si has creado margen y la BD aún falla, no recurras inmediatamente a modos de “recuperación forzada” a menos que estés preparado para restaurar desde backup.
La recuperación forzada puede ser una herramienta de extracción de datos, no una estrategia para volver al servicio normal.
Tarea 32: verificar permisos y señales de salud del filesystem (sanity rápida)
cr0x@server:~$ sudo dmesg -T | tail -n 20
[Thu Dec 30 02:22:11 2025] EXT4-fs warning (device nvme0n1p2): ext4_dx_add_entry: Directory index full!
[Thu Dec 30 02:22:12 2025] EXT4-fs (nvme0n1p2): Delayed block allocation failed for inode 3932211 at logical offset 0 with max blocks 2 with error 28
Qué significa: el kernel vio fallos de asignación coherentes con ENOSPC. No hay señal inmediata de fallo de medio, pero estás leyendo la verdad del terreno.
Decisión: si ves errores de I/O, remounts en solo-lectura o advertencias de corrupción, para y planifica una restauración/migración.
Tarea 33: si estás en LVM, expande el filesystem en vez de jugar al whack-a-mole con limpieza
cr0x@server:~$ sudo vgs
VG #PV #LV #SN Attr VSize VFree
vg0 1 2 0 wz--n- 200.00g 40.00g
cr0x@server:~$ sudo lvextend -L +20G /dev/vg0/root
Size of logical volume vg0/root changed from 80.00 GiB (20480 extents) to 100.00 GiB (25600 extents).
Logical volume vg0/root successfully resized.
cr0x@server:~$ sudo resize2fs /dev/vg0/root
resize2fs 1.47.0 (5-Feb-2023)
Filesystem at /dev/vg0/root is mounted on /; on-line resizing required
old_desc_blocks = 10, new_desc_blocks = 13
The filesystem on /dev/vg0/root is now 26214400 (4k) blocks long.
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg0-root 99G 62G 33G 66% /
Qué significa: convertiste un incidente recurrente en capacidad.
Decisión: si puedes extender, hazlo. La limpieza es un parche; la capacidad es una solución.
Listas de verificación / plan paso a paso (mentalidad imprimible)
Lista A: respuesta a incidentes para “filesystem full rompió BD”
- Detén servicios con muchas escrituras (workers de la app, ingestión, cron jobs). Mantén la BD fuera hasta que tengas espacio.
- Captura logs de systemd y logs de BD antes de rotar/truncar.
- Confirma el modo de fallo: bloques (
df -h), inodos (df -i), borrado-abierto (lsof +L1), cuotas. - Libera espacio de bajo riesgo: vacuum de journald, caché de apt, caches conocidos seguros, rota logs.
- Vuelve a comprobar espacio libre y asegura margen (no te conformes con 200MB).
- Arranca la BD una vez, y deja que recupere. No hagas reinicios a lo loco.
- Verifica disponibilidad: BD dice “ready”, conectividad, prueba básica de escritura.
- Reintegra tráfico gradualmente: primero workers canary, luego escala.
- Arregla la causa del crecimiento: rotación de logs, archivado atascado, operaciones temporales largas, contenedores, backups.
- Establece guardarraíles: monitorización, cuotas, límites de journald, umbrales de alerta, plan de capacidad.
Lista B: endurecimiento post-incidente (qué previene el caso #60)
- Alerta sobre bloques e inodos. Muchas organizaciones alertan solo por bloques y luego se sorprenden.
- Pon rutas temporales de BD en un volumen con margen. Para Postgres, considera un tablespace dedicado para temp si procede.
- Aplica límites de logs (journald + logs de apps) y prueba logrotate bajo carga.
- Mantén al menos una ruta de restauración probada (restauración desde backup o promoción de réplica) que no requiera heroicidades.
- Realiza periódicamente “juegos de disco lleno” en staging: simula ENOSPC y valida tiempo y pasos de recuperación.
Tres mini-historias corporativas desde el frente
1) El incidente causado por una suposición errónea: “La BD tiene su propio disco, así que root puede llenarse”
Una compañía SaaS mediana separó almacenamiento “correctamente”: datos de la base en un gran montaje separado, root en una partición NVMe pequeña.
El equipo se sentía virtuoso. El volumen de BD tenía espacio; los gráficos estaban verdes. El on-call se volvió cómodo.
Entonces un cambio inocuo se desplegó: una bandera de depuración muy verbosa se activó accidentalmente en un servicio de autenticación.
Los logs explotaron en /var/log en root. En horas, root llegó al 100%. El volumen de BD todavía tenía cientos de GB libres,
así que el incidente commander inicialmente descartó almacenamiento como causa y persiguió falsos positivos de red y CPU.
PostgreSQL empezó a fallar escrituras—no porque /var/lib/postgresql estuviera lleno, sino porque no podía escribir en sus logs y no podía crear archivos temporales.
Comenzaron bucles de recuperación: systemd intentaba reinicios; cada reinicio intentaba loguear; cada escritura de log fallaba; el servicio hacía flapping.
Mientras tanto la app, al ver fallos de conexión, reintentó agresivamente, amplificando el problema.
La solución fue dolorosamente simple: vacuum de journald, rotar el log de la app desbocada, detener la tormenta de reintentos y solo entonces reiniciar la BD.
La acción de postmortem que importó no fue “añadir más disco”. Fue “tratar root como una dependencia de la BD” y alertarlo en consecuencia.
2) La optimización que salió mal: “Apagar la compresión de logrotate, desperdicia CPU”
Un gran equipo de plataforma interna quiso reducir picos de CPU en una flota de proxies de base de datos Debian.
Alguien notó que la compresión de logrotate consumía CPU en horas punta. Pensaron razonablemente: desactivar compresión y mantener rotación.
La CPU se aplanó. Todos se felicitaron y siguieron.
Dos semanas después, múltiples nodos empezaron a alcanzar disco lleno casi al mismo tiempo. Las bases proxied estaban bien; los proxies no.
Los logs rotados sin comprimir ahora eran enormes, y la retención estaba ajustada para “tamaño comprimido”, no crudo. Las particiones root eran pequeñas porque “no hacen mucho”.
El incidente se manifestó como churn de conexiones y reintentos en cascada—comportamiento clásico de sistemas distribuidos: una pequeña falla se convierte en problema de todos.
La recuperación se enredó porque los ingenieros seguían borrando logs antiguos mientras rsyslog aún retenía descriptores abiertos.
El espacio no regresaba, lo que llevó a más borrados, y a menos logs forenses. Finalmente lo arreglaron reiniciando rsyslog, luego aplicando políticas sensatas de logrotate,
y moviendo logs de alto volumen a un filesystem dedicado.
Lección: las optimizaciones que eliminan salvaguardas rara vez son gratis. La CPU suele ser más fácil de comprar que tiempo de recuperación limpio.
Si desactivas compresión, debes volver a afinar la retención y las alertas, o el disco se convertirá en tu nuevo programador.
3) La práctica aburrida pero correcta que salvó el día: “Siempre mantener una réplica con runbooks reales de promoción”
Un equipo de servicios financieros ejecutaba PostgreSQL con una réplica por streaming en otro rack. Nada fancy.
Lo aburrido: una vez por trimestre practicaban promover la réplica, actualizar configuraciones de la app y luego degradar/reconstruir el primario.
Lo trataban como simulacros—molestos, programados y no opcionales.
Durante una ejecución de batch de fin de mes, el primario llenó su filesystem debido a una mala configuración de archivado que hizo que la retención de WAL se disparara.
Las escrituras se pararon. Los intentos de recuperación peleaban por disco. El equipo no intentó cirugías complicadas en el host mientras los stakeholders de negocio observaban.
Confirmaron que la réplica estaba lo bastante al día, la promovieron y restauraron el servicio con un drama mínimo.
Tras mover el tráfico, se tomaron su tiempo: ampliaron almacenamiento, corrigieron el archivado, validaron backups y reconstruyeron replicación limpiamente.
El postmortem leyó como una lista de la compra, no una novela de suspense. Ese es el objetivo. Lo aburrido es una característica en operaciones.
Errores comunes: síntoma → causa raíz → arreglo
1) “df muestra 0 bytes liberados después de borrar logs”
- Síntoma: borras archivos grandes, pero
df -hsigue al 100%. - Causa raíz: los archivos fueron borrados pero siguen abiertos por procesos.
- Arreglo:
lsof +L1; reinicia el servicio propietario o trunca el FD abierto vía/proc/<pid>/fd/<fd>. Luego vuelve a comprobardf.
2) “df -h parece bien, pero todo falla ‘No space left on device’”
- Síntoma: hay gigabytes disponibles, pero creaciones/escrituras fallan.
- Causa raíz: agotamiento de inodos (
df -i) o límites de cuota/proyecto. - Arreglo: borra un gran número de archivos pequeños en el directorio afectado; para cuotas, inspecciona y aumenta límites; para inodos, detén la carga que crea ficheros y rediseña el layout de almacenamiento.
3) “La BD arranca, pero la aplicación tiene fallos intermitentes de escritura”
- Síntoma: el servicio está en marcha, pero algunas escrituras fallan, operaciones con tablas temporales fallan o los sorts se caen.
- Causa raíz: tmpdir o directorio de logs está en un filesystem aún lleno (a menudo root), mientras que datadir está bien.
- Arreglo: confirma rutas tmpdir (la ubicación de temp de Postgres varía; MySQL tiene tmpdir configurable). Libera/expande el filesystem correcto; mueve tmpdir a un montaje mayor con cambios de configuración probados.
4) “El directorio WAL de Postgres es enorme; vamos a borrar WAL viejos”
- Síntoma:
pg_walconsume decenas de GB. - Causa raíz: slot de replicación reteniendo WAL, réplica caída, archivado fallando o transacciones largas impidiendo limpieza.
- Arreglo: identifica replication slots y lag; arregla archivado; elimina slots no usados; resuelve transacciones largas. No borres archivos WAL a mano.
5) “MySQL no arranca; alguien sugiere borrar ib_logfile0”
- Síntoma: errores de inicialización InnoDB tras ENOSPC.
- Causa raíz: escrituras incompletas de redo logs por disco lleno y espacio insuficiente para la recuperación.
- Arreglo: restaura espacio en disco, arranca limpio, verifica estado de InnoDB. Si persiste la corrupción, usa backups/replicas; la recuperación forzada es para extracción, no para restauración normal.
6) “Liberamos 2GB; ¿por qué sigue fallando la recuperación?”
- Síntoma: la recuperación arranca y vuelve a fallar con ENOSPC.
- Causa raíz: la recuperación en sí genera escrituras (replay de WAL, temp, checkpoints). 2GB no es una estrategia.
- Arreglo: libera/expande hasta tener un margen significativo (10–20% o varios GB según tamaño y carga de la BD), luego reintenta una vez.
7) “Después de la limpieza, el disco se rellena al instante”
- Síntoma: liberas espacio, reinicias servicios y vuelve al 100% en minutos.
- Causa raíz: spam de logs desbocado, tormenta de reintentos, cola atascada o un job por lotes que se reanuda.
- Arreglo: mantén los escritores detenidos; identifica al escritor principal; añade límites de tasa; corrige niveles de log; drena colas con cuidado; solo entonces reintroduce tráfico.
Preguntas frecuentes
1) ¿“filesystem full” es lo mismo que “disk full”?
No siempre. Los sistemas de ficheros pueden estar “llenos” porque se agotaron bloques, se agotaron inodos o por espacio reservado/limitado por cuotas.
Comprueba siempre df -h y df -i, y busca ficheros borrados pero abiertos con lsof +L1.
2) ¿Puedo simplemente borrar logs antiguos para arreglarlo?
A veces, pero hazlo con intención. Si los logs están abiertos, borrarlos no recuperará espacio hasta que el proceso los cierre.
Además: borrar logs puede eliminar la única evidencia de lo ocurrido, así que captura lo que necesites primero.
3) ¿Por qué se rompió mi BD si la partición de datos no estaba llena?
Porque las bases de datos dependen de otras rutas: directorios de logs, tmp dirs, sockets, ficheros PID y a veces artefactos de recuperación.
Root lleno puede romper una BD cuyo datadir esté en un montaje separado.
4) ¿Debo reiniciar la BD repetidamente hasta que vuelva?
No. Reinicios repetidos pueden thrashear la recuperación y generar más escrituras (y más logs) mientras tienes poco espacio.
Libera espacio primero, luego arranca una vez y deja que la recuperación termine.
5) ¿Cuánto espacio libre es “suficiente” antes de reiniciar?
Suficiente para terminar la recuperación y aguantar un pico de escrituras normales. En producción apunto a 10–20% libre en los filesystem relevantes.
Si no puedes llegar, extiende almacenamiento o conmute a una réplica en vez de apostar.
6) ¿Cuál es la forma más segura de recuperar espacio inmediatamente?
Vacía systemd journals, limpia cachés de paquetes, elimina caches conocidos seguros y rota/trunca logs de aplicaciones desbocadas.
Evita tocar archivos de la base de datos a menos que sigas un procedimiento probado específico del motor.
7) ¿Y si me quedo sin inodos?
Borrar algunos archivos grandes no ayudará. Debes borrar muchos archivos pequeños (a menudo caches) o mover esa carga fuera del filesystem.
A largo plazo, rediseña: separa caches de root y elige parámetros de filesystem que encajen con el patrón de conteo de ficheros.
8) ¿Los “bloques reservados” de ext4 significan que puedo reclamar espacio de emergencia?
Los bloques reservados existen para que root todavía pueda funcionar cuando los usuarios llenan el disco. Puedes ajustarlos con tune2fs,
pero cambiarlos durante un incidente rara vez es la mejor primera acción. Libera espacio real o expande el filesystem.
9) ¿Un disco lleno puede causar corrupción silenciosa de datos?
El disco lleno típicamente provoca fallos ruidosos (ENOSPC), pero puede poner tu base de datos en un estado que requiera recuperación y verificación de consistencia.
Si sospechas corrupción—especialmente con errores de I/O en dmesg—prefiere restaurar/promover réplica en vez de “seguir reiniciando y esperar”.
10) ¿Cómo prevengo que esto vuelva a pasar?
Alerta sobre bloques e inodos, aplica límites de logs (journald y app), pon rutas temporales sobre almacenamiento con margen y practica failover/restauración.
La prevención es mayormente configuración aburrida—y lo aburrido es más barato que el downtime.
Conclusión: próximos pasos que puedes hacer hoy
Los incidentes por disco lleno no son glamorosos, pero son honestos: revelan si tu sistema se opera con márgenes, guardarraíles y recuperación ensayada.
Si esto te acaba de ocurrir en Debian 13, tu prioridad es terminar en un estado verificado-bueno, no simplemente “servicio en marcha”.
Haz esto a continuación, en este orden:
- Establece límites estrictos para logs (journald + logrotate) y confirma que funcionan bajo carga.
- Añade alertas para inodos y ficheros borrados-abiertos (o al menos haz que
lsof +L1forme parte de tu memoria muscular on-call). - Mueve dependencias de la BD fuera de root donde tenga sentido: tmp dirs, logs de alto volumen, capas de contenedores.
- Decide tu “política de espacio libre mínimo” para volúmenes de BD (10–20% es un inicio sensato) y hazla cumplir con alertas y planificación de capacidad.
- Practica la ruta de recuperación (restauración o promoción de réplica). Si solo lo haces durante un outage, no es un plan—es una apuesta.
El mejor resultado de un incidente por disco lleno no es que recuperaste. Es que ahora tienes la disciplina suficiente en el sistema para que el siguiente nunca llegue a ser un incidente.