Compraste “20 TB raw”, montaste un pool RAIDZ y te sentiste satisfecho. Luego, alrededor del 70% de uso, las cosas empezaron a tambalearse:
las escrituras se ralentizaron, los scrub tardaron más, y tu monitorización gritó “pool lleno” mientras df afirmaba
que aún quedaban terabytes. Si esto te suena familiar, no estás de mala suerte: estás usando ZFS tal como fue diseñado.
ZFS no trata el “espacio libre” como un balde estúpido. Lo trata como un almacén con pasillos, montacargas y palés que no pueden
reorganizarse sin trabajo. Cuando los pasillos se estrechan, tus montacargas dejan de ser simpáticos.
El problema del 70%: qué significa realmente “lleno” en ZFS
Los pools ZFS a menudo se sienten llenos mucho antes de alcanzar el 100% de uso. El “número mágico” que la gente cita—70%, 80%,
a veces 85%—no es superstición. Es una regla práctica para cuando el comportamiento del asignador pasa de “mayoritariamente contiguo,
rápido y predecible” a “fragmentado, con mucha metadata y ocasionalmente dramático”.
Dos cosas son simultáneamente ciertas:
- ZFS puede seguir aceptando escrituras por encima del 70% del uso del pool, y muchos pools funcionan bien al 90%—hasta que dejan de hacerlo.
- El rendimiento y la fiabilidad de ZFS están fuertemente correlacionados con el espacio libre, especialmente para escrituras aleatorias, snapshots, RAIDZ y cargas con mucha metadata.
“Lleno” en producción tiene menos que ver con la capacidad bruta y más con si el asignador puede encontrar regiones contiguas lo bastante grandes
(y hacerlo sin consumir CPU) para satisfacer las escrituras entrantes mientras mantiene redundancia y rendimiento.
Aquí está el modelo mental que te mantendrá empleado: el espacio libre no es fungible.
Diez terabytes libres esparcidos en migas de 128 KB no son lo mismo que diez terabytes en trozos grandes y limpios.
ZFS es copy-on-write. No sobrescribe bloques en su sitio; escribe bloques nuevos y actualiza punteros.
Eso significa que necesita constantemente nuevo espacio para los cambios—incluso para los pequeños.
Un chiste corto, porque el universo exige equilibrio: ZFS no se queda sin espacio. Se queda sin espacio bueno,
como una nevera llena de condimentos cuando tienes hambre.
Entonces, ¿por qué 70%?
70% no es un límite rígido; es una luz de advertencia. Por encima de eso, especialmente en pools con vdevs RAIDZ, el asignador tiene
menos extensiones grandes con que trabajar. La fragmentación sube, la cantidad de churn de metadata aumenta, y cada escritura
se convierte en una pequeña negociación con la física.
El umbral “correcto” depende de:
- Diseño RAID (mirror vs RAIDZ1/2/3)
- Ancho de vdev
- Elección de ashift
- recordsize/volblocksize frente al tamaño de IO del workload
- Frecuencia y retención de snapshots
- Uso de special vdev (metadata/pequeños bloques)
- Con qué frecuencia se reescriben los datos (bases de datos vs archivos)
Si quieres una política que no te deje mal frente a una junta de revisión de cambios: planifica para
~70–80% como máximo en pools RAIDZ pesados que manejan escrituras aleatorias o muchos snapshots, y permite
~80–90% solo cuando entiendes tu carga y tienes una ruta de crecimiento probada.
Hechos e historia que explican el comportamiento
A los ingenieros de almacenamiento les encanta el folclore. Aquí hay hechos concretos—útiles—que explican por qué ZFS se comporta distinto
a los sistemas de archivos y pilas RAID tradicionales.
-
ZFS nació en Sun Microsystems a principios de los 2000 como un gestor de volúmenes y sistema de archivos combinados,
diseñado para evitar el juego de culpas “controlador RAID + sistema de archivos”. - Copy-on-write no lo inventó ZFS, pero ZFS lo popularizó en almacenamiento empresarial: permitió snapshots baratos y fuerte consistencia en disco sin rituales de fsck.
-
ZFS usa un modelo de almacenamiento en pool: los datasets se tallán lógicamente, no con particiones estáticas.
Esa flexibilidad también significa que cuotas, reservas y snapshots pueden crear escenarios sorprendentes de “¿dónde se fue mi espacio?”. - RAIDZ no es “RAID5 en software”. Tiene escrituras en franjas variables y un comportamiento de asignación distinto. Puede ser eficiente, pero también es más sensible a la fragmentación que los mirrors.
- El asignador trabaja en metaslabs (pedazos de espacio por vdev), y tiene múltiples estrategias dependiendo de la fragmentación y las extensiones disponibles. A medida que las metaslabs se llenan, la asignación se vuelve más costosa.
- Existe el “slop space” a propósito: ZFS reserva un trozo del pool para mantener el sistema operativo incluso cuando los usuarios intentan llenarlo. Eso no es espacio desperdiciado; es un mecanismo anti-pánico.
- El recordsize por defecto de 128 KB tiene una historia: es un compromiso orientado al rendimiento para cargas secuenciales, no una verdad universal para bases de datos o discos de VM.
-
Los discos con sectores de 4K cambiaron el mundo: elegir
ashift=9vsashift=12
afecta la eficiencia de espacio y el rendimiento. Si lo eliges mal, lo pagarás para siempre. -
Los special vdev son más recientes en la evolución de OpenZFS: son poderosos para metadata/pequeños bloques, pero
también crean un nuevo modo de fallo “puedes llenar lo incorrecto primero”.
Nada de esto es trivia. Es la razón por la que tu pool puede estar técnicamente “no lleno” y prácticamente inutilizable.
Dónde se va el espacio: la mecánica detrás del precipicio
1) Copy-on-write convierte actualizaciones en asignaciones
En un sistema de archivos tradicional que sobrescribe en el lugar, actualizar un bloque puede reutilizar el mismo sitio. En ZFS, actualizar un bloque
normalmente significa escribir el bloque nuevo en otro lugar y luego actualizar atómicamente la cadena de punteros hacia arriba en el árbol.
Eso es fantástico para la consistencia. También es la razón por la que ZFS necesita espacio para respirar.
Cuando el pool tiene amplitud, ZFS puede elegir asignaciones agradables y contiguas. Cuando el pool está apretado, el asignador
empieza a tomar lo que puede. Tu “pequeña actualización” se convierte en varias escrituras fragmentadas más actualizaciones de metadata.
Multiplica eso por una base de datos que hace escrituras de 8K y tendrás la sensación.
2) Amplificación en RAIDZ: la paridad no es gratis, ni lo son las escrituras pequeñas
Los mirrors son simples: escribe dos copias y listo. RAIDZ necesita paridad, y para escrituras parciales de franja puede necesitar leer
datos/paridad antiguos para calcular la nueva paridad (read-modify-write) a menos que pueda hacer escrituras de franja completa.
A medida que el espacio libre disminuye y la fragmentación aumenta, a ZFS le cuesta más ensamblar asignaciones de franja completa.
El resultado: más franjas parciales, más RMW, más operaciones IO por escritura lógica, más latencia.
3) La fragmentación no es un número; es el nivel de dolor del asignador
ZFS informa la fragmentación a nivel de pool, pero el asignador experimenta fragmentación por metaslab.
Un pool puede mostrar “solo” 30% de fragmentación y aun así tener varias metaslabs que son esencialmente hostiles a la asignación.
El punto clave: la fragmentación aumenta naturalmente en sistemas COW cuando los datos se reescriben y existen snapshots.
Es manejable con espacio libre. Es feo sin él.
4) Metadata y bloques indirectos crecen con snapshots y churn
Los snapshots no copian datos inmediatamente; preservan versiones de bloques. Eso significa que borrar archivos no libera espacio si un snapshot aún referencia los bloques antiguos.
Churn intenso + retención larga de snapshots es una fábrica de fragmentación. Cada reescritura deja atrás bloques históricos
anclados por snapshots. Tu “espacio libre” se vuelve un museo de datos de ayer.
5) La reserva de “slop space”: tu pool no miente, simplemente no estás invitado
ZFS mantiene algo de espacio en reserva para que el sistema pueda seguir funcionando, borrar datos y completar transacciones.
Esto previene la clásica cascada “disco lleno → el sistema no puede escribir logs → el sistema empeora”.
Prácticamente, significa que cuando estás muy lleno, puedes ver diferencias confusas entre:
zpool list, zfs list y df. No todos reportan lo mismo,
y parte de ese espacio “libre” no está pensado para tu aplicación.
6) Reservas, refreservations y cuotas: los bloqueadores silenciosos de espacio
ZFS te permite reservar espacio para datasets (reservation), y para volúmenes tiene
refreservation (espacio garantizado para ese dataset en sí, no para descendientes).
Es una buena herramienta—hasta que alguien configura una reserva “por si acaso” y se olvida.
Las cuotas también pueden hacer que “pool libre” sea irrelevante para un dataset. El pool puede tener espacio; tu dataset aún puede estar
detenido en seco.
7) Special vdevs y bloques pequeños: puedes llenar metadata antes que datos
Los special vdevs (a menudo SSD) almacenan metadata y opcionalmente bloques pequeños. Si los subdimensionas, tu pool puede entrar
en un mal estado: el special vdev se llena y la asignación se vuelve limitada incluso si los vdevs de datos principales aún tienen espacio.
Esta es una de esas características que es poderosa en manos entrenadas y una oportunidad de desarrollo profesional en manos no entrenadas.
8) ashift y sobrecarga por padding: muerte por alineación
Si eliges ashift demasiado pequeño en relación con el tamaño físico de sector del disco, puedes generar amplificación de escritura y problemas de rendimiento. Si lo eliges demasiado grande, desperdicias espacio en bloques pequeños debido al padding.
Con muchos archivos pequeños o metadata, ese desperdicio se hace visible pronto.
Guía rápida de diagnóstico
Cuando alguien dice “ZFS está lleno” o “ZFS está lento” y necesitas triage rápido, no divagues. Haz esto en orden.
El objetivo es identificar si tratas con (a) verdadera falta de capacidad, (b) bloqueo por snapshots, (c)
restricciones de cuota/reserva, (d) saturación de special vdev, o (e) fragmentación del asignador.
Primero: confirma qué significa “lleno”
- Revisa uso y salud del pool:
zpool list,zpool status - Revisa uso del dataset vs disponible:
zfs list - Revisa si una cuota/reserva está bloqueando escrituras:
zfs get quota,reservation,refquota,refreservation
Segundo: encuentra qué está anclando espacio
- Revisa snapshots y su espacio “used”:
zfs list -t snapshot - Revisa si los borrados no liberan espacio por snapshots: compara
usedvsreferdel dataset
Tercero: diagnostica el dolor del asignador
- Revisa fragmentación:
zpool list -o fragmentation - Revisa saturación de metaslabs/special vdev:
zpool list -v,zpool status -v - Revisa latencia IO y colas:
zpool iostat -v 1
Cuarto: decide si necesitas alivio inmediato o cambios estructurales
- Alivio inmediato: borrar snapshots, reducir retención, quitar reservas, añadir espacio, mover datasets calientes.
- Estructural: rediseñar ancho de vdev, mover BD/VM a mirrors, añadir special vdev correctamente, ajustar recordsize/volblocksize, arreglar política de snapshots.
Tareas prácticas: comandos, salidas y decisiones (12+)
Estos son los movimientos reales de operaciones. Cada tarea incluye: comando, salida de ejemplo, lo que significa y qué decisión tomar.
Los comandos asumen OpenZFS en Linux, pero los conceptos aplican ampliamente.
Tarea 1: Revisar capacidad del pool y salud básica
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
Significado: El pool está al 74% usado y 41% fragmentado. “ONLINE” es bueno; el dolor del asignador no es extremo.
Decisión: A ~74% con 41% de frag, trátalo como “acercándose al precipicio” para RAIDZ + workloads con churn.
Empieza a planear alivio (espacio, limpieza de snapshots, mover workloads).
Tarea 2: Revisar balance de asignación por vdev (y atrapar un cuello de botella oculto)
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
raidz2-0 18.1T 14.9T 3.22T - - 48% 82% - ONLINE
raidz2-1 9.06T 5.23T 3.83T - - 19% 57% - ONLINE
Significado: Un vdev está al 82% y muy fragmentado mientras el otro está al 57%. El desequilibrio de asignación ocurre;
duele porque el vdev más lleno se convierte en el limitador.
Decisión: Planifica la capacidad a nivel de vdev, no solo de pool. Considera añadir vdevs de igual tamaño,
o migrar datos para reequilibrar (send/receive), o repensar el layout.
Tarea 3: Confirmar vista a nivel de dataset (lo que experimentan las apps)
cr0x@server:~$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
tank 20.1T 5.92T 160K /tank
tank/prod 18.4T 5.92T 12.2T /tank/prod
tank/prod/vm 9.80T 5.92T 9.80T /tank/prod/vm
tank/prod/db 6.10T 5.92T 6.10T /tank/prod/db
tank/backups 1.62T 5.92T 1.62T /tank/backups
Significado: Los datasets muestran el mismo AVAIL porque comparten espacio libre del pool a menos que estén restringidos por cuotas.
Decisión: Si la app dice “no hay espacio” pero AVAIL parece bien, sospecha de cuotas/reservas o slop space,
no de capacidad bruta.
Tarea 4: Atrapar cuotas y reservas que te asfixian en silencio
cr0x@server:~$ zfs get -r quota,refquota,reservation,refreservation tank/prod
NAME PROPERTY VALUE SOURCE
tank/prod quota none default
tank/prod refquota none default
tank/prod reservation none default
tank/prod refreservation none default
tank/prod/vm quota 10T local
tank/prod/vm refquota none default
tank/prod/vm reservation none default
tank/prod/vm refreservation 9T local
Significado: tank/prod/vm tiene garantizados 9T independientemente de los descendientes, y está limitado a 10T.
Esto puede dejar sin espacio a otros datasets o hacer que el “espacio libre” sea engañoso.
Decisión: Si el pool está apretado, elimina o ajusta refreservations salvo que protejan algo crítico. La mayoría de entornos las sobreutilizan.
Tarea 5: Identificar bloqueo por snapshots (el clásico “borré archivos y no pasó nada”)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation | tail -n 6
tank/prod/db@auto-2026-02-03_0100 0B 6.10T Mon Feb 3 01:00 2026
tank/prod/db@auto-2026-02-03_0200 18G 6.10T Mon Feb 3 02:00 2026
tank/prod/db@auto-2026-02-03_0300 22G 6.10T Mon Feb 3 03:00 2026
tank/prod/db@auto-2026-02-03_0400 25G 6.10T Mon Feb 3 04:00 2026
tank/prod/db@auto-2026-02-03_0500 27G 6.10T Mon Feb 3 05:00 2026
tank/prod/db@auto-2026-02-03_0600 31G 6.10T Mon Feb 3 06:00 2026
Significado: Los snapshots están acumulando “USED” (bloques cambiados). Ese espacio no puede liberarse hasta que los snapshots expiren.
Decisión: Si necesitas espacio de emergencia, borra los snapshots más grandes/antiguos que puedas eliminar de forma segura (con cuidado), luego corrige la retención.
Si “USED” es pequeño, los snapshots no son tu villano principal hoy.
Tarea 6: Encontrar datasets con divergencia sospechosa entre USED y REFER
cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | head
NAME USED REFER COMPRESSRATIO
tank/prod 18.4T 12.2T 1.12x
tank/prod/vm 9.80T 9.80T 1.01x
tank/prod/db 6.10T 6.10T 1.37x
Significado: tank/prod USED es mucho mayor que REFER porque snapshots/hijos están anclando espacio.
Decisión: Investiga la política de snapshots y los datasets con churn. Mueve datasets de VM y BD a pools o tipos de vdev separados
si dominan el tráfico de reescritura.
Tarea 7: Revisar fragmentación y capacidad del pool de un vistazo
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag
NAME SIZE ALLOC FREE CAP FRAG
tank 27.2T 20.1T 7.08T 74% 41%
Significado: FRAG es una señal tosca. Más allá del ~50% de fragmentación en un pool ocupado es cuando “existe espacio libre” pero “no hay escrituras rápidas”.
Decisión: Si FRAG sube junto con CAP, prioriza añadir espacio o reducir churn. La fragmentación rara vez “se cura” sola.
Tarea 8: Observar presión IO en tiempo real y qué vdev está sufriendo
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 20.1T 7.08T 220 980 18.4M 96.7M
raidz2-0 14.9T 3.22T 170 770 14.1M 76.2M
raidz2-1 5.23T 3.83T 50 210 4.3M 20.5M
-------------------------- ----- ----- ----- ----- ----- -----
Significado: raidz2-0 está realizando la mayoría de las escrituras y también es el vdev más lleno y fragmentado. Eso es una trampa de rendimiento.
Decisión: Si un vdev está caliente y lleno, añade capacidad de forma que no empeore el desequilibrio (añade otro vdev similar,
o migra datos fuera del vdev caliente mediante replicación a un pool nuevo).
Tarea 9: Confirmar ashift (sobre-costo de espacio/rendimiento que no puedes “ajustar después”)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head -n 8
38: vdev_tree:
49: ashift: 12
73: ashift: 12
Significado: ashift: 12 significa sectores de 4K. Bueno para discos modernos; evita dolores de desalineación.
Decisión: Si descubres ashift=9 en discos de 4K, no “esperes que esté bien.” Planea una migración/reconstrucción.
Es uno de los pocos errores de ZFS que pega como chicle.
Tarea 10: Revisar presencia de special vdev y si está casi lleno
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
Significado: Existe un special vdev (NVMEs en mirror). Ahora debes tratarlo como almacenamiento crítico de metadata.
Decisión: Revisa inmediatamente la asignación del special vdev y asegúrate de alertas de monitorización específicas. Si se llena,
el pool puede volverse limitado en escrituras de formas desagradables.
Tarea 11: Ver la asignación del special vdev a nivel superior
cr0x@server:~$ zpool list -v tank | sed -n '1,8p'
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
raidz2-0 18.1T 14.9T 3.22T - - 48% 82% - ONLINE
special 1.81T 1.55T 260G - - 12% 85% - ONLINE
Significado: El special vdev está al 85% de uso. Eso no es “está bien.” Es un simulacro de incendio en cámara lenta.
Decisión: Deja de asignar pequeños bloques/metadata allí si está subdimensionado (special_small_blocks),
añade capacidad special si tu plataforma lo soporta de forma segura, o migra a un diseño de pool corregido.
Tarea 12: Revisar recordsize y volblocksize (desalineación con el workload causa amplificación)
cr0x@server:~$ zfs get recordsize,compression tank/prod/db
NAME PROPERTY VALUE SOURCE
tank/prod/db recordsize 128K default
tank/prod/db compression zstd local
Significado: El dataset de BD usa recordsize de 128K. Para muchas bases de datos que hacen IO de 8K/16K, esto puede aumentar read/modify/write y
fragmentación bajo churn.
Decisión: Considera fijar recordsize=16K (o alinearlo con el tamaño de página de tu BD) para datos nuevos,
y valida con un benchmark. No hagas cargo-cult; prueba.
Tarea 13: Mostrar ratio de compresión y decidir si la compresión es una palanca de capacidad
cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | grep -E 'db|vm'
tank/prod/vm 9.80T 9.80T 1.01x
tank/prod/db 6.10T 6.10T 1.37x
Significado: Los datos de VM son apenas compresibles; los datos de BD son moderadamente compresibles.
Decisión: La compresión no salvará pools de VM. Para BD, puede ganar tiempo, pero no la conviertas en un plan de capacidad.
Tarea 14: Identificar snapshots grandes rápidamente (quién acapara el ayer)
cr0x@server:~$ zfs list -t snapshot -o name,used -s used | tail -n 5
tank/prod/vm@weekly-2026-01-05 412G
tank/prod/vm@weekly-2026-01-12 438G
tank/prod/vm@weekly-2026-01-19 451G
tank/prod/vm@weekly-2026-01-26 466G
tank/prod/vm@weekly-2026-02-02 489G
Significado: Los snapshots semanales de VM están anclando cientos de GB cada uno. Eso es esperado con churn, pero también explica por qué tu pool “se llena temprano.”
Decisión: Ajusta la retención de snapshots para datasets de VM o mueve snapshots de VM a un destino de backup dedicado vía replicación.
Tarea 15: Validar que los borrados pueden realmente reclamar espacio (no bloqueados por holds)
cr0x@server:~$ zfs holds tank/prod/vm@weekly-2026-02-02
NAME TAG TIMESTAMP
tank/prod/vm@weekly-2026-02-02 keep Mon Feb 3 09:12 2026
Significado: Existe un hold en el snapshot. Alguien lo “protegió”. El espacio no se liberará hasta que el hold se libere.
Decisión: Audita los holds. Si el hold no está justificado, libéralo y borra el snapshot. Si es requerido por cumplimiento, deja de discutir con la física y añade almacenamiento.
Tarea 16: Estimar uso lógico vs físico (y detectar sobrecarga oculta)
cr0x@server:~$ zfs get -o name,property,value -s local,default logicalused,logicalreferenced,used,refer tank/prod
NAME PROPERTY VALUE
tank/prod logicalused 19.9T
tank/prod logicalreferenced 12.2T
tank/prod used 18.4T
tank/prod refer 12.2T
Significado: El uso lógico es mayor que el uso físico debido a la compresión, pero la brecha entre USED y REFER indica efectos de snapshots/hijos.
Decisión: Usa métricas lógicas para “cuánto cree la aplicación que tiene” y uso físico para “qué tan lleno está realmente el pool”.
Tres microhistorias corporativas desde las trincheras del almacenamiento
1) El incidente causado por una suposición errónea: “El espacio libre es espacio libre”
Una empresa mediana ejecutaba una plataforma analítica interna en un pool ZFS RAIDZ2. No era sofisticado: unos pocos datasets grandes, snapshots nocturnos y un flujo constante de trabajos ETL. Alguien notó que el pool rondaba el 78% durante meses y decidió que era “seguro” porque nunca llegaba al 90%.
Entonces añadieron una carga nueva: un trabajo intensivo en escrituras que reescribía unos pocos terabytes diarios. En papel, encajaba. En
la práctica, el pool empezó a hacer timeouts. Los logs de la aplicación mostraban mensajes esporádicos de “no hay espacio”; el pool aún
tenía varios terabytes libres. La gente culpó a la base de datos. La gente culpa a la base de datos como los marineros culpan al mar.
El verdadero problema fue el comportamiento del asignador bajo churn con snapshots. El trabajo ETL no solo escribía datos nuevos; reescribía bloques existentes.
Copy-on-write más snapshots significó que las versiones antiguas permanecían ancladas. El espacio libre se fragmentó, las metaslabs se volvieron más difíciles
de asignar y RAIDZ tuvo problemas para ensamblar franjas eficientes. La latencia subió hasta que la lógica de reintentos del trabajo hizo el resto.
La solución no fue glamorosa. Pausaron los snapshots nuevos para el dataset con churn, recortaron la retención y movieron el espacio de scratch del ETL a un pool en mirror diseñado para escrituras aleatorias. La lección que quedó: “espacio libre del pool” no es lo mismo que “pool sano”, y %CAP no es una garantía de rendimiento.
2) La optimización que salió mal: “Hagamos dedup; el almacenamiento es caro”
Otra organización quiso reducir gasto en almacenamiento para imágenes de VM y backups. Activaron deduplicación en un dataset con muchos datos parecidos. La primera semana fue genial—hasta que apareció presión de memoria,
y las cajas de almacenamiento empezaron a hacer swap bajo carga.
Dedup en ZFS no es un interruptor que activas porque suena eficiente. Es un compromiso de diseño: crea una tabla de dedup que debe consultarse para escrituras, y prospera con RAM y acceso de baja latencia a metadata.
No tenían suficiente RAM, y su metadata vivía en los mismos discos giratorios que todo lo demás.
A medida que el pool se llenó, las tablas de dedup y la actividad de metadata empeoraron. La latencia subió, el rendimiento se volvió espigado, y la eliminación de snapshots—normalmente una tarea de mantenimiento—se convirtió en un evento nocturno. No solo
hicieron el pool más pequeño; lo hicieron más difícil de manejar cuando el espacio apretó.
Finalmente deshabilitaron dedup (tras migrar datos, porque no puedes simplemente “apagarlo” y recuperar todo), se apoyaron en la compresión y rediseñaron los backups para evitar almacenar múltiples copias completas. La optimización que salió mal no fue la dedup en sí—fue la suposición de que la eficiencia de capacidad es siempre la meta correcta.
3) La práctica aburrida pero correcta que salvó el día: “Siempre mantener margen y tener un plan de crecimiento”
Una empresa relacionada con finanzas ejecutaba APIs al cliente respaldadas por un pool ZFS, con SLOs de latencia estrictos. Su ingeniero de almacenamiento no era popular en presupuesto porque insistía en objetivos de 25–30% de espacio libre y un plan de crecimiento escrito. Sonaba conservador. Lo era.
Un trimestre, un cambio de producto aumentó la amplificación de escritura: más índices, más actualizaciones pequeñas, más churn de snapshots.
Vieron las señales tempranas—fragmentación subiendo, special vdev tendiendo a llenarse, tiempos de scrub alargándose—y lo trataron como un evento planeado más que como una emergencia.
Ejecutaron un playbook preaprobado: añadieron un nuevo conjunto de vdevs, reequilibraron migrando el dataset más ruidoso mediante replicación a un pool nuevo, luego ajustaron la retención de snapshots en los datasets con churn. Los usuarios no notaron nada.
La dirección tampoco lo notó, y ese es el mayor elogio que puede recibir operaciones.
La práctica aburrida no fue solo “mantener margen.” Fue instrumentación más una regla de decisión: si CAP > 75% y
frag > 35% en un pool caliente, inicia el ticket de expansión. Sin heroísmos. Sin matemáticas nocturnas.
Errores comunes: síntoma → causa raíz → solución
1) “Borré un montón de datos pero el espacio no volvió”
Síntoma: El dataset muestra menos datos vivos, pero ALLOC del pool apenas cambia.
Causa raíz: Los snapshots aún referencian los bloques; los borrados solo eliminan las referencias actuales.
Solución: Identifica snapshots con alto USED (zfs list -t snapshot -o name,used -s used), borra o reduce retención, y elimina holds si procede.
2) “El pool dice 10% libre, pero las escrituras fallan con ENOSPC”
Síntoma: Las aplicaciones fallan; zpool list todavía muestra algo de FREE.
Causa raíz: Slop space en reserva, cuota/refquota, o agotamiento de metaslab en un vdev (el libre a nivel de pool no ayuda si un vdev es el limitador).
Solución: Revisa cuotas/reservas; revisa CAP por vdev; añade capacidad o migra datos. No intentes “aguantar”.
3) “El rendimiento colapsó después de cruzar ~80%”
Síntoma: Picos de latencia, caída de throughput, CPU subida en rutas IO del kernel.
Causa raíz: Fragmentación + escrituras parciales en RAIDZ + churn de metadata.
Solución: Crea margen (borra snapshots, añade vdevs, mueve workloads con churn). A largo plazo, usa mirrors para datasets con muchas escrituras aleatorias.
4) “Un vdev está casi lleno, pero el pool no”
Síntoma: CAP del pool parece OK, pero un vdev top-level muestra CAP/FRAG muy alto.
Causa raíz: Desequilibrio de asignación por añadir vdevs de tamaños distintos o decisiones históricas de asignación.
Solución: Añade vdevs que coincidan con el tamaño de los existentes; considera migrar datos a un pool nuevo para reestructurar el layout.
5) “Se llenó el special vdev y ahora todo está raro”
Síntoma: Escrituras lentas o fallidas; operaciones de metadata atascadas; el pool aún tiene espacio en vdevs HDD.
Causa raíz: Special vdev subdimensionado para metadata/pequeños bloques; es una dependencia fuerte.
Solución: Deja de descargar agresivamente pequeños bloques, amplía añadiendo capacidad special si es seguro, o reconstruye el pool. Monitorea CAP del special vdev como si fuera oxígeno.
6) “Pusimos reservas para estar seguros y ahora nos faltó espacio”
Síntoma: Algunos datasets tienen espacio, otros no pueden crecer; el pool libre parece incorrecto.
Causa raíz: Reservations/refreservations acumulan espacio independientemente del uso real.
Solución: Elimina o ajusta reservas; mantenlas solo para datasets realmente críticos con garantías duras.
7) “Ajustamos recordsize y empeoró”
Síntoma: Más IO, más fragmentación, peor latencia tras un “cambio de rendimiento”.
Causa raíz: Desajuste de recordsize/volblocksize con la carga, además de datos existentes no reescritos al nuevo tamaño.
Solución: Haz benchmarks con patrones de IO representativos; aplica por dataset; reescribe datos si necesitas que la nueva geometría de bloques importe.
Listas de verificación / plan paso a paso
Política de planificación de espacio (qué hacer, no qué debatir)
- Fija un objetivo de CAP por tipo de pool:
- RAIDZ + snapshots + escrituras aleatorias: objetivo ≤ 75–80% usado.
- Mirrors + mayormente secuencial + bajo churn: objetivo ≤ 85–90% usado (con monitorización).
- Planifica a nivel de vdev: un único vdev “caliente” puede definir tu umbral de fallo.
- Asume que los snapshots crecerán: la retención consume capacidad, no es un checkbox.
- Documenta acciones de crecimiento: qué añades, cuánto tarda y quién lo aprueba.
- Monitorea special vdev por separado: trátalo como un pool dentro del pool.
Plan de margen de emergencia (cuando necesitas espacio hoy)
- Confirma que no estás bloqueado por cuotas/reservas; elimina las accidentales primero.
- Borra los snapshots con mayor USED que puedas eliminar con seguridad; cuidado con los holds.
- Deja de crear nuevos snapshots temporalmente para los datasets con churn.
- Mueve workloads temporales/scratch fuera del pool restringido.
- Añade capacidad (nuevos vdevs) si puedes hacerlo de forma segura y rápida; de lo contrario, replica a un pool nuevo.
Plan de corrección estructural (para que esto no vuelva a pasar)
- Clasifica workloads: discos de VM, bases de datos, logs, backups, archivos multimedia.
- Ajusta el layout al workload:
- Pesado en escrituras aleatorias: mirrors (y suficientes vdevs para IOPS).
- Mayormente secuencial, orientado a capacidad: RAIDZ2/3 con margen planificado.
- Fija recordsize/volblocksize por dataset según el perfil de IO, no por intuición.
- Snapshots con intención: retención más corta para datasets con churn, más larga para datos mayormente append.
- Automatiza alertas “CAP + FRAG” con una política operativa rígida: cuando se crucen los umbrales, empieza la expansión.
Una idea de fiabilidad que vale la pena robar
Idea parafraseada, atribuida: “How Complex Systems Fail” de Richard Cook argumenta que la seguridad es un problema de control, no un problema de componentes.
La planificación de espacio es eso en pequeño: no obtienes fiabilidad esperando que los pools se mantengan ordenados; la obtienes controlando el margen y la tasa de cambios.
Preguntas frecuentes
1) ¿La regla “mantener ZFS por debajo del 80%” siempre es correcta?
No. Es un control de política. Para pools RAIDZ con churn y snapshots, 70–80% es un valor por defecto sólido.
Para pools mirror con escrituras mayormente secuenciales y bajo churn, puedes operar más alto—si monitorizas fragmentación y latencia y tienes una ruta de expansión probada.
2) ¿Por qué ZFS se ralentiza más que ext4 cuando el espacio se estrecha?
Copy-on-write implica que las actualizaciones requieren nuevas asignaciones, y RAIDZ quiere escrituras grandes y bien alineadas para ser eficiente.
Cuando el espacio libre está fragmentado, ZFS gasta más trabajo encontrando sitio, las escrituras son menos contiguas y la paridad aumenta el trabajo.
3) ¿Por qué df y zfs list discrepan sobre espacio libre?
Reportan capas distintas. df informa lo que el sistema de archivos montado anuncia, mientras zfs list informa contabilidad de datasets dentro del pool,
influenciada por cuotas, reservas y el comportamiento de slop space.
4) ¿Los snapshots consumen espacio incluso si no cambia nada?
Un snapshot en sí es barato, pero los cambios después del snapshot son los que cuestan espacio, porque deben preservarse versiones antiguas de bloques.
Si un dataset es mayormente append-only, los snapshots crecen despacio. Si reescribe datos, el “USED” del snapshot crece rápido.
5) ¿Puedo “desfragmentar” un pool ZFS?
No en el lugar como las antiguas herramientas de desfragmentación. La desfragmentación práctica es la migración: zfs send | zfs receive a un pool o dataset nuevo,
o reescribir datos a bloques nuevos. El verdadero anti-fragmentación es el margen de espacio.
6) ¿Añadir un vdev arregla la fragmentación?
A menudo mejora el comportamiento de asignación porque las escrituras nuevas van a los vdevs más vacíos, pero no reescribe mágicamente las distribuciones de bloques fragmentados antiguos.
Es alivio, no viajar en el tiempo.
7) ¿Cuál es el mayor error de planificación de espacio con special vdevs?
Subdimensionarlos y luego olvidarse de que ahora son críticos. Si el special vdev se llena, puedes sufrir graves problemas de rendimiento o fallos de asignación.
Dimensiona con margen, ponlos en mirror y monitorízalos explícitamente.
8) ¿Debo usar compresión como estrategia de capacidad?
Usa compresión porque suele ser un beneficio neto (especialmente zstd) y puede reducir IO.
Pero no construyas un plan que dependa de un ratio de compresión específico para no quedarte sin recursos; los datos cambian y “compressible” no es una propiedad contractual.
9) ¿Por qué mis snapshots semanales de VM se vuelven enormes?
Los discos de VM churnean: actualizaciones del SO, archivos de log, swap, bases de datos dentro de las VMs y escrituras aleatorias. Los snapshots preservan bloques antiguos, así que churn = crecimiento de snapshots.
La solución es la política de retención, la estrategia de replicación y, a veces, mover almacenamiento de VM a un layout que tolere mejor el churn.
10) ¿Cuál es la acción inmediata más segura cuando el pool está cerca de lleno?
Crea margen: reduce retención de snapshots y borra snapshots con alto USED que puedas eliminar de forma segura, luego añade capacidad.
No esperes al “100% usado” como disparador; ZFS puede quedarse operacionalmente atascado antes de eso.
Próximos pasos que puedes hacer esta semana
- Fija una política de cap explícita por pool (según workload y tipo de vdev). Si no puedes explicar por qué es 85% en lugar de 75%, que sea 75%.
-
Implementa una alerta “CAP + FRAG” usando
zpool list, y alerta a humanos antes de que los usuarios te llamen. - Audita la retención de snapshots en datasets con churn (VMs, BDs). Si los snapshots son tu estrategia de backup, vale—haz que el destino de backup pague por ello, no producción.
- Audita cuotas/reservas. Elimina lo que exista “por si acaso.” “Por si acaso” es cómo el almacenamiento muere silenciosamente.
- Revisa la utilización del special vdev. Si supera 70–80%, trátalo como trabajo de ingeniería urgente.
- Escribe un playbook de crecimiento: quién aprueba la expansión, cómo añades vdevs y cuándo eliges migración sobre expansión.
Segundo chiste corto, porque lo necesitarás en la próxima revisión de capacidad: Lo único más optimista que las previsiones de ventas es un pool de almacenamiento planificado al 99%.
La disciplina central es simple: ZFS quiere margen de la misma manera que las bases de datos quieren índices—porque la alternativa es el caos,
y el caos siempre encuentra una ventana de mantenimiento que no programaste.