Si has gestionado ZFS en serio—en un host de bases de datos, una granja de VM o un servidor de archivos que silenciosamente se convirtió en un lago de datos—lo has visto: du afirma que un dataset ocupa 2.1T, pero zfs list insiste en que usa 3.4T. Alguien pregunta: “Entonces… ¿de dónde salió el terabyte extra?” y de repente estás haciendo respuesta a incidentes con una calculadora.
Esto no es un bug de ZFS. Es contabilidad. ZFS reporta múltiples “verdades” según la pregunta que hagas. du es un recorrido por el sistema de archivos. zfs list es contabilidad de propiedad de bloques en snapshots, reservaciones, metadatos y políticas a nivel de pool como el slop space. Cuando alineas las preguntas, los números se alinean—en su mayor parte. Cuando no lo haces, obtienes sorpresas que parecen fugas.
El desacuerdo central: archivos vs bloques vs historial
du responde: “¿Cuántos bloques de disco están actualmente asignados a los archivos que puedo alcanzar recorriendo este árbol de directorios ahora mismo?”
zfs list responde: “¿Cuántos bloques en el pool son atribuibles a este dataset y sus snapshots, más algunas sutilezas como reservaciones?” Dependiendo de las columnas que solicites, puede responder “bloques únicos” o “bloques compartidos” o “bytes lógicos antes de compresión”.
Mismo sistema de almacenamiento, diferente pregunta. Esa diferencia se amplifica por las características definitorias de ZFS:
- Copy-on-write significa que los bloques antiguos permanecen mientras los snapshots los referencian.
- La compresión significa que “bytes en archivos” y “bytes en disco” son dos monedas distintas.
- Existen punteros de bloque, metadatos y sobrecarga del spacemap incluso cuando tu directorio parece “vacío”.
- El comportamiento a nivel de pool (slop space) reserva capacidad para mantener el sistema vivo.
Broma #1: Preguntar “por qué du y zfs list discrepan” es como preguntar “por qué mi saldo bancario no coincide con mi tracker de gastos”—uno incluye transacciones pendientes y el otro incluye tu optimismo.
Hechos y contexto histórico que realmente importan
El comportamiento del almacenamiento es política más física, pero ZFS también tiene historia. Unos puntos de contexto cortos que te ayudan a razonar sobre los números de hoy:
- ZFS popularizó snapshots copy-on-write en sistemas de archivos de propósito general; esa elección hace que el uso de espacio dependa inherentemente del tiempo.
- La línea original de ZFS proviene de Solaris, donde el objetivo operativo no era “un portátil”; era “un servidor de almacenamiento que no debe corromper datos cuando todo falla”.
- La reportabilidad de espacio en ZFS evolucionó: OpenZFS más reciente expone propiedades como
usedbydataset,usedbysnapshotsylogicalusedpara reducir la incertidumbre. - El comportamiento de “slop space 1/8” existe para reducir modos de fallo catastrófico cerca de pools llenos; no es una broma de interfaz.
- Históricamente, los admins llenaban filesystems al 99% porque las herramientas de la era ext enseñaban que podían; ZFS castiga ese hábito porque la asignación y el comportamiento de metaslabs empeoran bruscamente cuando está casi lleno.
- La compresión pasó de “truco de rendimiento de nicho” a “recomendación por defecto” en muchas implantaciones de ZFS porque las CPUs modernas son baratas y el disco no.
- Los vdevs especiales (metadatos/bloques pequeños en medios rápidos) cambiaron el perfil de rendimiento de “workloads de archivos pequeños”, pero también introdujeron nuevas formas de quedarse sin el tipo equivocado de espacio.
- Los snapshots son baratos en el sentido de “crear”, no en el sentido de “guardar para siempre”; la factura llega cuando los datos churnean.
- Las imágenes de VM y las capas de contenedores hicieron de la combinación “archivo sparse + churn + snapshots” un cóctel de fallo común; esto no existía a esta escala cuando nació ZFS.
Un modelo mental práctico del espacio en ZFS
Cuando leas estadísticas de espacio de ZFS, mantén tres cubetas mentales:
1) Bytes lógicos (lo que las aplicaciones creen que escribieron)
Esto es el tamaño de archivos, páginas de bases de datos, capacidad “virtual” de imágenes VM. Es con lo que los humanos quieren razonar. ZFS puede exponer esto vía logicalused y logicalreferenced.
2) Bytes físicos (lo que realmente ocupa el pool)
Esto determina si llegas al 80%, 90% y luego “todo está en llamas”. Compresión, copias, sobrecarga de paridad/RAIDZ y metadatos caen aquí. ZFS lo expone vía used, referenced y campos más detallados usedby*.
3) Propiedad/atribución (quién “posee” bloques a través del tiempo)
Con snapshots, los bloques pueden ser compartidos por múltiples “vistas” de datos. Tu filesystem en vivo puede ya no referenciar un bloque, pero un snapshot sí, así que el pool lo mantiene. du no puede ver eso porque recorre el árbol vivo, no el histórico.
Qué mide du (y qué ignora)
du recorre directorios y suma bloques asignados para archivos alcanzables. Eso es útil, pero tiene puntos ciegos en ZFS:
- Snapshots: Un snapshot no forma parte del árbol de directorio en vivo, así que
duignora el espacio anclado por snapshots a menos que recorras explícitamente.zfs/snapshot(si está expuesto). - Metadatos y sobrecarga del dataset:
dureporta asignaciones de archivos, no metadatos ZFS como punteros de bloque, sobrecarga del spacemap y bloques indirectos que escalan con la fragmentación y el churn. - Verdad de la compresión:
dutípicamente informa bloques asignados desde la vista del SO. En ZFS, puede estar más cerca de “físico” que de “lógico”, pero el comportamiento exacto depende de la plataforma y las opciones (--apparent-sizevs por defecto). - Reservas: Si un dataset tiene una reserva, el pool tiene capacidad apartada, pero
duno te lo va a decir. - Huecos/regiones sparse:
ducuenta bloques asignados; los ceros sparse no cuentan a menos que se hayan escrito.
Qué mide zfs list (y por qué parece “más grande”)
zfs list es contabilidad de bloques, no un recorrido de directorios. Sus columnas por defecto son engañosamente simples:
USED: espacio físico consumido por el dataset y descendientes (según el contexto), incluyendo espacio referenciado por snapshots.AVAIL: lo que ZFS te dejará asignar, teniendo en cuenta espacio libre del pool, cuotas, reservas y slop space.REFER: espacio físico accesible por este dataset (sin incluir descendientes).
Esas palabras son precisas pero incompletas. En producción, casi siempre quieres las columnas de desglose, porque USED es una cifra compuesta.
Snapshots: el sospechoso habitual
La mayoría de los desacuerdos entre du y zfs list son “los snapshots están reteniendo bloques eliminados/sobrescritos”. Copy-on-write significa que las modificaciones asignan nuevos bloques, y los snapshots conservan los antiguos. Si eliminas un directorio de 500G hoy, y existe el snapshot de anoche, esos bloques siguen en el pool.
Esto empeora con workloads de alto churn: bases de datos, imágenes VM, capas de contenedores y cualquier cosa que reescriba archivos grandes en su lugar. El dataset en vivo puede verse pequeño (du), pero su conjunto de snapshots está silenciosamente preservando historia como bloques físicos (zfs list).
Broma #2: Los snapshots son como ese canal “temporal” de Slack—barato crear, caro mantener, y nadie quiere ser quien lo borre.
Compresión, espacio lógico y por qué confunde a la gente
La compresión es donde la intuición muere. En un dataset comprimido:
ls -lmuestra el tamaño lógico del archivo.du(por defecto) suele mostrar espacio asignado, que puede seguir más de cerca el uso físico.zfs listUSEDes uso físico (con caveats de snapshot/reservation).zfs get logicalusedmuestra el uso lógico (lo que se escribió antes de la compresión).
Así que puedes ver du más pequeño que zfs list porque los snapshots fijan bloques antiguos, pero también puedes ver lo opuesto: du --apparent-size puede mostrar “más grande” que la realidad física debido a la compresión.
Operativamente: al planificar capacidad, te importan los bytes físicos en el pool. Cuando aparece facturación o expectativas de inquilinos, puede importarte los bytes lógicos. Elige uno, etiquétalo y no los mezcles en la misma hoja de cálculo.
Archivos sparse, zvols y “asignado” vs “escrito”
Los archivos sparse son un generador clásico de desajustes: una imagen de disco VM puede tener “1T” de tamaño lógico pero solo 80G asignados. du y las estadísticas físicas de ZFS tenderán a coincidir en “asignado”, mientras que los equipos de aplicaciones siguen citando el tamaño lógico y se preguntan por qué “no cabe”.
Los zvols añaden otra capa: son dispositivos de bloque dentro de ZFS. Pueden ser thick o thin según la configuración (volmode, comportamiento de aprovisionamiento y patrones de escritura del huésped). Hacer snapshots de zvols es común en pilas de VM; también es una gran forma de preservar una cantidad impresionante de churn.
Sobrecarga de metadatos, xattrs, ACLs y el impuesto de archivos pequeños
Algunos workloads son principalmente metadatos: millones de archivos pequeños, xattrs pesados, árboles ricos en ACL o patrones tipo maildir. La sobrecarga de metadatos de ZFS no es gratis y no aparece como “bytes de archivo”. du puede sub-representar la huella porque suma las asignaciones de archivos, no cuenta todos los bloques indirectos, dnodes y estructuras de metadatos necesarias para mantener el filesystem coherente y rápido.
Si alguna vez has visto un dataset de “archivos diminutos” comerse un pool, aprendes a dejar de confiar en promedios como “los archivos solo tienen 4K”. No es así. El archivo es 4K; el ecosistema alrededor del archivo es el resto de tu fin de semana.
Reservas, cuotas, refreservations y slop space
Las reservaciones crean “uso fantasma” desde la perspectiva de du. ZFS puede apartar espacio para un dataset (reservation o refreservation) incluso si los archivos no lo están usando. ZFS reporta ese espacio reservado como usado/no disponible porque está prometido a alguien.
Las cuotas (quota, refquota) restringen la asignación. No explican directamente las discrepancias entre du y zfs list, pero sí explican “por qué AVAIL es más pequeño de lo esperado”.
Luego está el slop space: ZFS típicamente retiene un trozo del espacio libre del pool para evitar que las asignaciones y actualizaciones de metadatos fallen catastróficamente cerca del 100%. Así que aunque “tengas” espacio libre, ZFS puede negarse a entregarlo.
copies=2, vdevs especiales y otras trampas
La propiedad copies es un multiplicador silencioso: copies=2 guarda dos copias de los datos de usuario (dentro del mismo pool). Genial para ciertas necesidades de fiabilidad; terrible si la olvidas activada y luego comparas con du.
Los vdevs especiales (metadatos/bloques pequeños en SSD/NVMe) pueden hacer que ZFS se sienta como un filesystem distinto. Pero también crean un segundo dominio de capacidad: puedes quedarte sin espacio en el vdev especial mientras el pool principal parece bien. Tu “problema de espacio” se convierte en un “vdev equivocado lleno”, y los síntomas son sutiles hasta que dejan de serlo.
Tres mini-historias del mundo corporativo
Mini-historia 1: Un incidente causado por una suposición equivocada
El ticket empezó como una queja de finanzas: “Storage nos está cobrando un 40% más de lo que almacenamos.” El equipo ejecutó du en el directorio del proyecto y se sintieron confiados. Los números eran pequeños. Los números de facturación eran grandes. Alguien concluyó que los reportes del equipo de almacenamiento estaban inflados y escaló.
Miramos el dataset y vimos el patrón habitual: un sistema CI generando artefactos, eliminándolos, generando más y haciendo eso todo el día. El dataset también tenía una política de “snapshots horarios, conservar 30 días” copiada de un workload más estable. El árbol en vivo era modesto; los snapshots eran básicamente un registro arqueológico de outputs de builds.
El error inmediato no fue “existencia de snapshots”. Fue asumir que du medía lo mismo que el consumo del pool. La salida de du era verdadera para “lo que existe ahora”. Era irrelevante para “qué bloques el pool debe retener”.
No borramos todos los snapshots; así te ganas enemigos. Cambiamos la retención para ese dataset para que coincidiera con su perfil de churn, luego introdujimos un dataset separado para artefactos con retención más corta y sin snapshots de larga duración. La facturación dejó de ser un debate porque los números ahora estaban alineados con la intención.
El ítem de postmortem que importó: cada dataset recibió una etiqueta explícita de clase de datos (artefactos efímeros de build, directorios home, bases de datos, imágenes VM) y las políticas de snapshot se asociaron a la clase, no a impresiones.
Mini-historia 2: Una optimización que salió mal
Un equipo orientado al rendimiento activó compresión agresiva y afinamiento de recordsize en un dataset de carga mixta. Su benchmark lucía genial: menos IO de disco, más aciertos de caché, mejor rendimiento. Lo desplegaron ampliamente y declararon victoria.
Luego las ventanas de backup empezaron a alargarse. No porque ZFS se volviera más lento, sino porque los snapshots empezaron a retener muchas más bloques únicos de lo esperado. El workload incluía archivos grandes que se modificaban frecuentemente en regiones pequeñas. Una elección de recordsize que “estaba bien para throughput secuencial” aumentó la amplificación de escritura para actualizaciones aleatorias. Cada pequeño cambio causó más churn de bloques, que los snapshots preservaron diligentemente. El uso físico subió más rápido que el modelo de cualquiera.
La ironía: la “optimización” mejoró el rendimiento en vivo mientras aumentaba silenciosamente el costo a largo plazo de la estrategia de snapshots. Y como el pool fue dimensionado para el perfil de churn anterior, el crecimiento parecía una fuga.
La solución no fue abandonar la compresión. Fue segmentar workloads: datasets separados con recordsize apropiado, retenciones de snapshots afinadas al churn y monitorización de written por intervalo de snapshot. La lección fue aburrida: afinar rendimiento sin contabilidad del ciclo de vida solo mueve el coste de un eje a otro.
Mini-historia 3: Una práctica aburrida pero correcta que salvó el día
Otra organización tenía una regla aburrida: cada dataset debe tener un informe mensual de “contabilidad de espacio” incluyendo usedbydataset, usedbysnapshots y una lista de los snapshots que más consumen. La gente se quejaba de papeleo. No era papeleo; era reconocimiento.
Un trimestre, un pool empezó a tender al 85% a pesar de métricas de negocio estables. Nadie entró en pánico porque los informes mensuales mostraron un cambio: usedbysnapshots estaba subiendo, pero usedbydataset no. Eso no es “más datos”; es “más historia por unidad de dato”, lo que normalmente significa que el churn aumentó o cambió la retención.
Lo detectaron temprano: una aplicación cambió de logs append-only a reescrituras periódicas de un gran archivo de estado, incrementando dramáticamente el churn. Como el equipo ya vigilaba el espacio de snapshots como métrica de primera clase, pudieron responder antes de que el pool alcanzara la zona de peligro.
La solución fue casi insultantemente simple: cambiar la aplicación para escribir archivos nuevos y rotar, y acortar la retención de snapshots para ese dataset. No migración heroica de datos. No compra de capacidad de emergencia. La práctica aburrida—medir el desglose correcto—salvó el día.
Tareas prácticas: comandos e interpretación
Estos son los movimientos de campo a los que recurro cuando alguien dice “ZFS está usando más espacio que du”. Cada tarea incluye qué buscar y cómo interpretarla.
Task 1: Get a truthful dataset breakdown (usedby*)
cr0x@server:~$ zfs list -o name,used,refer,usedbydataset,usedbysnapshots,usedbychildren,usedbyrefreservation tank/proj
NAME USED REFER USEDBYDATASET USEDBYSNAPSHOTS USEDBYCHILDREN USEDBYREFRESERV
tank/proj 3.40T 1.95T 1.80T 1.55T 50.0G 0B
Interpretation: The mismatch is right there: snapshots account for 1.55T. du is mostly reflecting the ~1.8T live dataset (plus minus compression/metadata), not the snapshot history.
Task 2: Compare du “allocated” vs “apparent” sizes
cr0x@server:~$ du -sh /tank/proj
1.9T /tank/proj
cr0x@server:~$ du -sh --apparent-size /tank/proj
2.6T /tank/proj
Interpretation: Default du is closer to allocated blocks; apparent size is logical file sizes. If compression is on, logical can be much larger than physical.
Task 3: Check compression ratio and logical space at the dataset level
cr0x@server:~$ zfs get -o name,property,value -H compressratio,compression,logicalused,logicalreferenced tank/proj
tank/proj compressratio 1.37x
tank/proj compression lz4
tank/proj logicalused 4.65T
tank/proj logicalreferenced 2.70T
Interpretation: Logical bytes are higher than physical. If someone is comparing logical totals to physical pool occupancy, you’ll have an argument instead of a plan.
Task 4: List snapshots and see which ones are expensive
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used tank/proj
NAME USED REFER CREATION
tank/proj@daily-2025-12-01 120G 1.95T Mon Dec 1 00:00 2025
tank/proj@daily-2025-11-30 118G 1.95T Sun Nov 30 00:00 2025
tank/proj@hourly-2025-12-24-23 22G 1.95T Wed Dec 24 23:00 2025
Interpretation: Snapshot USED is “unique space attributable to this snapshot” (changes since the previous snapshot in that lineage). Large numbers indicate heavy churn during that interval.
Task 5: Find the “written since snapshot” signal (great for churn)
cr0x@server:~$ zfs get -H -o name,property,value written tank/proj
tank/proj written 0B
cr0x@server:~$ zfs snapshot tank/proj@now
cr0x@server:~$ zfs get -H -o name,property,value written tank/proj
tank/proj written 18.4G
Interpretation: written is how much data has been written since the last snapshot. If this is huge every hour, long retention will be expensive.
Task 6: Confirm whether .zfs snapshots are visible and whether du is walking them
cr0x@server:~$ zfs get -H -o value snapdir tank/proj
hidden
cr0x@server:~$ ls -la /tank/proj/.zfs
ls: cannot access '/tank/proj/.zfs': No such file or directory
Interpretation: If snapdir=visible, an incautious du might traverse snapshots and “double count” from a human perspective. If it’s hidden, du will ignore snapshots entirely.
Task 7: Look for reservations and refreservations
cr0x@server:~$ zfs get -H -o name,property,value reservation,refreservation tank/proj
tank/proj reservation 0B
tank/proj refreservation 500G
Interpretation: A refreservation can make the dataset look “big” from ZFS’s point of view even if files are small. It is space promised to that dataset’s referenced data.
Task 8: Inspect quotas and refquotas (why AVAIL looks wrong)
cr0x@server:~$ zfs get -H -o name,property,value quota,refquota,avail,used tank/proj
tank/proj quota 2T
tank/proj refquota none
tank/proj avail 120G
tank/proj used 1.88T
Interpretation: Even if the pool has free space, a quota caps growth. People often mistake this for “mysterious missing capacity.” It’s policy doing its job.
Task 9: Check pool health and slop-space reality via zpool list
cr0x@server:~$ zpool list -o name,size,alloc,free,capacity,health tank
NAME SIZE ALLOC FREE CAPACITY HEALTH
tank 20.0T 17.2T 2.80T 86% ONLINE
Interpretation: At ~86% full, you’re in the zone where fragmentation and allocation behavior can get ugly. Also expect zfs list AVAIL to be smaller than “FREE” due to slop space and dataset-level constraints.
Task 10: Find datasets with heavy snapshot overhead across the pool
cr0x@server:~$ zfs list -o name,used,usedbydataset,usedbysnapshots -r tank | head
NAME USED USEDBYDATASET USEDBYSNAPSHOTS
tank 17.2T 2.10T 14.7T
tank/proj 3.40T 1.80T 1.55T
tank/vm 8.90T 2.40T 6.30T
tank/home 2.10T 1.95T 120G
Interpretation: When usedbysnapshots dominates, the pool is paying for history. That may be correct—but it must be intentional.
Task 11: Spot “deleted but still used” space: open file handles
cr0x@server:~$ sudo lsof +L1 | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 2114 app 123w REG 0,119 1048576 0 912345 /tank/proj/logs/app.log (deleted)
Interpretation: This is not ZFS-specific, but it’s a frequent culprit. The file is deleted, du doesn’t count it, but the space remains allocated until the process closes it.
Task 12: Estimate small-file pressure and metadata-heavy trees
cr0x@server:~$ find /tank/proj -xdev -type f -size -16k | wc -l
4821931
cr0x@server:~$ du -sh /tank/proj
1.9T /tank/proj
Interpretation: Millions of tiny files means metadata overhead and potential special-vdev pressure (if configured). If your “data” is small but you have a huge count of files, expect the pool to behave differently than a big-file workload.
Task 13: Check recordsize and volblocksize choices (write amplification clues)
cr0x@server:~$ zfs get -H -o name,property,value recordsize tank/proj
tank/proj recordsize 1M
cr0x@server:~$ zfs get -H -o name,property,value volblocksize tank/vm/zvol01
tank/vm/zvol01 volblocksize 128K
Interpretation: Large recordsize can be great for streaming IO, but can increase churn cost under snapshots for random updates. Zvol volblocksize matters similarly.
Task 14: Detect copies property multiplying usage
cr0x@server:~$ zfs get -H -o name,property,value copies tank/proj
tank/proj copies 2
Interpretation: You’re paying double for data blocks (plus metadata). If nobody intended this, it’s a clean explanation for “why ZFS is bigger than du.”
Task 15: Show refreservation impact on referenced space specifically
cr0x@server:~$ zfs list -o name,refer,usedbyrefreservation tank/proj
NAME REFER USEDBYREFRESERV
tank/proj 1.95T 500G
Interpretation: Even if REFER is stable, the dataset has 500G pinned for it. That reduces pool flexibility and explains “why free space disappeared.”
Guía de diagnóstico rápido
Esta es la “tengo 10 minutos antes de una llamada de revisión de capacidad” guía. No resolverá todo, pero te dirá hacia dónde correr.
First: determine if the discrepancy is snapshots, reservations, or something else
cr0x@server:~$ zfs list -o name,used,refer,usedbydataset,usedbysnapshots,usedbyrefreservation tank/target
NAME USED REFER USEDBYDATASET USEDBYSNAPSHOTS USEDBYREFRESERV
tank/target 3.4T 1.9T 1.8T 1.6T 0B
If usedbysnapshots is big: it’s historical retention + churn.
If usedbyrefreservation is big: it’s policy/reservation.
If usedbydataset is big but du is small: suspect open-but-deleted files, special cases like copies, or du walking the wrong mountpoint.
Second: check whether du is measuring logical or physical, and whether it’s crossing boundaries
cr0x@server:~$ du -sh /tank/target
cr0x@server:~$ du -sh --apparent-size /tank/target
cr0x@server:~$ mount | grep 'tank/target'
What you learn: whether you’re comparing allocated vs apparent bytes, and whether the path you’re scanning is actually the dataset you think it is.
Third: identify churn rate and the snapshot retention policy
cr0x@server:~$ zfs get -H -o name,property,value written,com.sun:auto-snapshot tank/target
tank/target written 220G
tank/target com.sun:auto-snapshot true
What you learn: If written jumps by hundreds of gigabytes per interval, long snapshot retention will dominate physical usage.
Fourth: confirm pool fullness and whether you’re hitting the “near full” pain curve
cr0x@server:~$ zpool list -o name,alloc,free,capacity tank
NAME ALLOC FREE CAPACITY
tank 17.2T 2.8T 86%
What you learn: At high utilization, everything gets harder: frees don’t show up quickly (because they’re snapshot-held), allocations fragment, performance can sag, and “AVAIL” becomes political.
Errores comunes, síntomas y correcciones
Mistake 1: Using zfs list USED as “live data size”
Symptom: Dataset “USED” is huge, but du is smaller and app teams swear nothing changed.
Cause: USED includes snapshot-referenced space (and potentially reservations).
Fix: Use usedbydataset for live data and usedbysnapshots for historical overhead; adjust snapshot retention or churn behavior.
Mistake 2: Treating “deleted files” as immediately freed space
Symptom: rm -rf runs, du drops, but pool space doesn’t recover.
Cause: Snapshots still reference those blocks; or a process still has the file open (deleted-but-open).
Fix: Check snapshot consumers; run lsof +L1; restart/logrotate misbehaving processes; expire snapshots intentionally.
Mistake 3: Comparing du –apparent-size to zfs USED
Symptom: du --apparent-size says 10T, ZFS says 4T; someone calls it “missing data.”
Cause: Compression (and sometimes sparse files) reduces physical usage.
Fix: Decide whether you want logical or physical accounting. Use logicalused alongside used and label charts clearly.
Mistake 4: Ignoring refreservation and then wondering where capacity went
Symptom: Pool free space shrinks faster than live data growth; AVAIL looks low everywhere.
Cause: refreservation pins space even if not used by files.
Fix: Audit refreservation/reservation across datasets; remove or right-size. Keep reservations for workloads that truly need guaranteed headroom.
Mistake 5: Snapshotting high-churn VM images like they’re home directories
Symptom: Snapshot space grows explosively; rollbacks work great, capacity doesn’t.
Cause: Random writes + COW + frequent snapshots = lots of unique blocks retained.
Fix: Tune snapshot frequency/retention; consider replication cadence; separate VM datasets; ensure recordsize/volblocksize choices fit the IO pattern.
Mistake 6: copies=2 enabled “temporarily” and forgotten
Symptom: Used space is roughly double what you expect; no smoking gun in snapshots.
Cause: copies property duplicates blocks.
Fix: Audit zfs get copies -r. If removing, understand that changing it affects new writes; existing blocks may remain until rewritten.
Mistake 7: Running the pool too full, then calling it a capacity bug
Symptom: Writes slow down, frees don’t seem to help, allocation errors near “still some free.”
Cause: High fragmentation + slop space + metaslab constraints.
Fix: Keep pools under a sensible threshold (many teams aim below ~80% for general workloads). Expand capacity or migrate data before you reach the cliff.
Listas de verificación / plan paso a paso
Checklist: “du más pequeño que zfs list USED” (más común)
- Ejecuta
zfs list -o usedbydataset,usedbysnapshots,usedbyrefreservationpara el dataset. - Si
usedbysnapshotses grande, lista snapshots ordenados porusede identifica la política de retención. - Comprueba la tasa de churn con
zfs get writtenentre intervalos de snapshot. - Confirma que nadie está haciendo snapshots con demasiada frecuencia (propiedades de automatización, cron jobs, herramientas de orquestación).
- Verifica archivos abiertos pero eliminados con
lsof +L1. - Decide: cambiar retención, cambiar comportamiento de churn, o comprar espacio. No “borres snapshots al azar” sin entender dependencias de replicación/backup.
Checklist: “zfs list REFER más pequeño que du”
- Comprueba si usaste
du --apparent-size(lógico) mientrasREFERes físico. - Revisa configuraciones de compresión y
compressratio. - Para archivos sparse, compara
ls -ltamaño lógico vsduasignado. - Asegúrate de que
duno esté cruzando puntos de montaje o escaneando una ruta distinta al mount del dataset.
Checklist: “AVAIL mucho más pequeño que free del pool”
- Revisa
quota/refquotayreservation/refreservationdel dataset. - Revisa la utilización del pool (
zpool list) y considera el comportamiento del slop space. - Busca otros datasets con grandes reservaciones que estén bloqueando capacidad.
- Si el pool está cerca del lleno, deja de tratar “FREE” como usable; planifica expansión/migración.
Preguntas frecuentes
1) ¿Qué número debo confiar: du o zfs list?
Confía en el que responda tu pregunta. Para “qué tan grande es el árbol de directorio en vivo”, usa du (asignado) o du --apparent-size (lógico). Para “cuánta capacidad del pool se consume”, usa la contabilidad física de ZFS (zfs list más el desglose usedby*).
2) ¿Por qué al borrar archivos no se libera espacio en el pool?
Porque los snapshots mantienen bloques antiguos. Borrar solo elimina la referencia en vivo. El espacio vuelve cuando ningún snapshot (y ninguna clone) referencia esos bloques. También revisa archivos eliminados pero abiertos con lsof +L1.
3) ¿Qué significa zfs list REFER?
REFER es la cantidad de espacio físico referenciado por el dataset en sí (no sus descendientes). Está más cerca de la “vista en vivo” que USED, pero aún así no coincidirá con du --apparent-size en datasets comprimidos.
4) ¿Qué representa realmente “snapshot USED”?
El USED de un snapshot es la cantidad de espacio que se liberaría si ese snapshot fuera destruido, asumiendo que ningún otro snapshot/clone también referencia esos mismos bloques. Es una estimación de “contribución única”, no el “tamaño del snapshot” como la gente imagina.
5) ¿Por qué AVAIL es más pequeño que zpool FREE?
Porque AVAIL está limitado por cuotas/reservas del dataset y por el comportamiento del pool (incluido el slop space). ZFS es conservador a propósito cerca de pools llenos.
6) ¿Puede du ser mayor que zfs list?
Sí, si usas du --apparent-size (tamaños lógicos) en un dataset comprimido, o cuando archivos sparse reportan gran tamaño lógico. El uso físico de ZFS puede ser mucho menor.
7) ¿Cómo encuentro qué está consumiendo espacio de snapshots?
Empieza identificando datasets donde usedbysnapshots es alto, luego lista snapshots ordenados por used. Después, mide el churn con written por intervalo de snapshot. Finalmente, correlaciona con cambios en el workload (churn de VM, mantenimientos de BD, patrones de artefactos CI).
8) ¿La compresión hace que los snapshots sean más baratos o más caros?
Ambas cosas, depende del workload. La compresión reduce el tamaño físico de cada bloque, así que retener bloques antiguos puede ser más barato. Pero si la compresión cambia el empaquetado de registros o el comportamiento del workload, también puede incrementar el churn y por ende bloques únicos retenidos por snapshots. Mide written y used por snapshot en lugar de adivinar.
9) ¿Las reservas son “espacio desperdiciado”?
Son espacio reservado—capacidad retenida para que un dataset pueda seguir escribiendo bajo contención. No es desperdicio si evita caídas para servicios críticos. Es desperdicio si la configuras y la olvidas en todo.
10) ¿Es seguro borrar snapshots para recuperar espacio?
Mecánicamente, sí; operativamente, “depende”. Los snapshots pueden ser parte de tu cadena de backup/replicación o requeridos para objetivos de recuperación. Borra intencionalmente: define una política de retención, valida el comportamiento de replicación y elimina snapshots en el orden correcto si las herramientas lo esperan.
Conclusión
du y zfs list discrepan porque miden realidades distintas: el filesystem vivo de archivos alcanzables frente a la propiedad de bloques del pool a través del tiempo, la política y los metadatos. ZFS no miente; te está contando sobre historia, promesas (reservas) y física (compresión, tamaños de bloque, sobrecarga).
La movida operativa es dejar de discutir “el número correcto” y empezar a estandarizar qué número usas para cada decisión: bytes lógicos para expectativas de usuarios, bytes físicos para capacidad y un desglose snapshot/churn para cualquier cosa que implique retención. Una vez hecho eso, la discrepancia deja de ser misteriosa—y pasa a ser una palanca que puedes accionar.