Siempre sucede en el peor momento: un pool alcanza el 95% de uso, las escrituras se ralentizan hasta casi detenerse, y alguien dice: “Tenemos snapshots. Solo borra unos cuantos.” Así es como los equipos descubren que la contabilidad de espacio de ZFS es a la vez brutalmente honesta y, a veces, contraintuitiva.
Si eliminas snapshots de la forma incorrecta, puede que no liberes nada—o que liberes lo equivocado. Esta guía es el camino pragmático y orientado a producción: recupera espacio rápidamente, demuestra qué pasará antes de hacerlo y evita el error que convierte una limpieza en un proyecto de restauración.
Qué hace (y qué no hace) realmente borrar un snapshot
Un snapshot de ZFS es una referencia en un punto en el tiempo a bloques. No es una “copia” en el sentido tradicional. Cuando creas un snapshot, ZFS simplemente promete: “Esos bloques, tal como existían en ese momento, seguirán siendo accesibles.” A medida que los datos cambian después, ZFS usa copy-on-write, por lo que las nuevas escrituras van a nuevos bloques y los bloques antiguos permanecen porque el snapshot aún los referencia.
Así que cuando destruyes un snapshot, ZFS no “borra archivos.” Elimina un conjunto de referencias. Entonces ZFS puede liberar cualquier bloque que ya no esté referenciado por nada: ni por el sistema de ficheros en vivo, ni por otros snapshots, ni por clones, ni por holds, ni por características especiales.
Por eso la eliminación de snapshots es la palanca correcta para la recuperación de espacio, pero no es una palanca mágica. Si la tiras y no ocurre nada, normalmente es porque algo más sigue referenciando esos bloques.
El error #1 “Acabo de borrar mis datos”
Destruir el dataset cuando querías destruir un snapshot. O hacer rollback cuando querías eliminar. Los comandos son cortos, las consecuencias duran mucho, y la producción no se interesa por que fuera un error honesto.
Aquí está el modelo mental que previene outages:
zfs destroy pool/ds@snapelimina un snapshot. Puede liberar espacio (eventualmente, según referencias).zfs destroy pool/dsdestruye el dataset y todo lo que tenga debajo (a menos que esté prevenido). Eso es “hemos terminado con estos datos”.zfs rollback pool/ds@snaprebobina el dataset vivo al snapshot. Eso es “reemplazar los datos actuales por datos antiguos”.
Broma corta #1: ZFS es como un archivador que nunca olvida—hasta que le dices que lo haga, y entonces se vuelve extremadamente bueno olvidando.
Hechos interesantes y contexto histórico (porque cambia cómo operas)
- ZFS debutó en Sun a mediados de los 2000 con la idea radical de que el sistema de ficheros y el gestor de volúmenes deberían ser uno, para que los snapshots fueran de primera clase y baratos.
- Copy-on-write no era nuevo, pero ZFS lo hizo práctico para sistemas de ficheros de propósito general a escala, por eso los snapshots se convirtieron en algo operativo normal.
- Los snapshots de ZFS son “baratos” de crear (principalmente metadatos), lo que llevó a una generación de equipos a crear muchos—y luego aprender que la retención es el verdadero coste.
- La visibilidad de “snapdir” solía confundir: el directorio
.zfs/snapshotpuede exponer snapshots para navegación, lo cual es conveniente hasta que alguien hace scripts basándose en él y olvida que no es un directorio normal. - Los clones llegaron pronto y cambiaron la historia de la eliminación: si clonas un snapshot, creas un filesystem dependiente que puede mantener vivos los bloques del snapshot.
- La contabilidad de espacio está deliberadamente en capas:
USED,REFER,USEDDS,USEDSNAPexisten porque “¿cuánto espacio ocupa esto?” tiene múltiples respuestas correctas según la decisión que tomes. - Se añadieron los holds por seguridad, porque las herramientas de replicación y backup necesitaban una forma de decir “no elimines este snapshot todavía”.
- Las eliminaciones masivas de snapshots causaban históricamente picos de latencia en algunas cargas porque liberar metadatos puede ser costoso; las implementaciones modernas mejoraron, pero la eliminación aún puede ser no trivial.
Una idea de confiabilidad para llevar en un post-it: paraphrased idea
— Richard Cook: “El éxito y el fracaso provienen del mismo sistema; la gente se adapta para que las cosas funcionen.” En la limpieza de snapshots de ZFS, tu “arreglo rápido” se convierte en tu incidente futuro.
Guion de diagnóstico rápido: encuentra el cuello de botella en minutos
Te falta espacio, o estás peligrosamente cerca, y alguien quiere alivio inmediato. Haz esto en orden. Es el camino más corto hacia una decisión correcta.
Primero: ¿el pool está realmente lleno, y la fragmentación lo empeora?
- Revisa
zpool listpara capacidad y salud. - Comprueba
zpool get fragmentationyzpool statuspor señales de alarma. - Si estás al 90%+ de uso, espera dolor de rendimiento y liberaciones más lentas. A ZFS le gusta tener espacio para respirar.
Segundo: ¿el espacio es “usado real” o “reservado/retenido”?
- Inspecciona el uso a nivel de dataset con
zfs list -o space. - Busca
refreservation, cuotas y reservaciones que hagan que “disponible” sea más pequeño de lo esperado. - Revisa el uso de snapshots y los holds. Que el espacio no vuelva después de borrar suele significar que holds, clones u otros snapshots aún referencian bloques.
Tercero: ¿qué snapshots son los grandes en términos de espacio referenciado único?
- Ordena los snapshots por
used(único para ese snapshot) y apunta a los mayores consumidores únicos primero. - Confirma que no dependen clones (
originy patrones conzfs get clones). - Haz un simulacro de los comandos destructivos cuando sea posible; si no puedes, simula el impacto con la salida de contabilidad primero.
Si aún no puedes liberar espacio rápidamente
- Busca un dataset con una
refreservationmasiva o un desajuste de recordsize/compression que cause uso “real” inesperadamente alto. - Considera liberar espacio fuera de snapshots (archivos de log, volcados de crash, réplicas antiguas).
- Si el pool está completamente lleno, puede que necesites una triage de emergencia: borrar el dataset no crítico más grande, añadir capacidad de vdev, o adjuntar almacenamiento temporal y migrar.
Contabilidad de espacio que realmente necesitas: USED, REFER, AVAIL, USEDDS, USEDREFRESERV
ZFS reporta varios números porque los operadores hacen preguntas diferentes:
- REFER: datos accesibles desde la cabeza actual del dataset (no incluye snapshots). “¿Qué tamaño tiene el sistema de ficheros en vivo?”
- USED: espacio total consumido por el dataset más sus descendientes, incluyendo snapshots (y a veces reservaciones dependiendo de la vista). “¿Cuánto espacio del pool es atribuible a este subárbol?”
- AVAIL: cuánto puede aún asignar este dataset, teniendo en cuenta cuotas/reservaciones. Este es el número que tu aplicación alcanzará primero.
- USEDSNAP: cuánto espacio usan los snapshots bajo ese dataset (no necesariamente único por snapshot).
- USEDDS: espacio usado por el dataset en sí (no snapshots, no hijos).
- USEDREFRESERV: espacio fijado por refreservation. Esto es “espacio que prometiste mantener disponible”, que se convierte en “espacio que no puedes usar” cuando estás en emergencia.
Si recuerdas una cosa: la eliminación de snapshots solo libera bloques que ya no están referenciados en ningún lugar. Así que necesitas encontrar qué los sigue referenciando.
Tareas prácticas (comandos + significado de la salida + la decisión)
Estas son las tareas que realmente ejecuto cuando alguien pregunta: “¿El pool ZFS está lleno, puedes borrar snapshots?” Cada tarea incluye un comando, qué significa su salida y la decisión que tomas a partir de ello.
Task 1: Confirmar capacidad y salud básica del pool
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 7.25T 6.85T 410G - - 42% 94% 1.00x ONLINE -
Significado: CAP 94% es territorio peligroso. FRAG 42% sugiere que las asignaciones y liberaciones pueden ser más costosas.
Decisión: Trata esto como un incidente, no como mantenimiento. Planea liberar espacio rápidamente y luego crear margen (capacidad o cambios de retención).
Task 2: Comprobar si existe un checkpoint (raro, pero relevante)
cr0x@server:~$ zpool get checkpoint tank
NAME PROPERTY VALUE SOURCE
tank checkpoint - -
Significado: No hay checkpoint. Si existiera uno, el comportamiento del espacio puede sorprender porque hacer rollback de un checkpoint puede revertir asignaciones.
Decisión: Avanza; no hay consideraciones de checkpoint.
Task 3: Encontrar qué datasets son responsables del uso
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -r tank | head
NAME USED AVAIL REFER MOUNTPOINT
tank 6.70T 320G 192K /tank
tank/home 410G 320G 260G /tank/home
tank/postgres 3.90T 320G 1.10T /tank/postgres
tank/vm 2.20T 320G 1.80T /tank/vm
tank/backups 190G 320G 175G /tank/backups
Significado: USED muestra atribución incluyendo snapshots/hijos; REFER muestra el tamaño en vivo. tank/postgres usa 3.90T pero solo 1.10T en vivo—los snapshots probablemente son grandes.
Decisión: Centra la atención en datasets donde USED es mucho mayor que REFER.
Task 4: Desglosar el uso del dataset en componentes
cr0x@server:~$ zfs list -o name,used,usedds,usedsnap,usedrefreserv,usedchild -r tank/postgres
NAME USED USEDDS USEDSNAP USEDREFRESERV USEDCHILD
tank/postgres 3.90T 1.10T 2.80T 0B 0B
Significado: USEDSNAP 2.80T es la historia. La retención de snapshots consume la mayor parte del espacio.
Decisión: Identificar qué snapshots representan el espacio único y si son eliminables (holds/clones/replicación).
Task 5: Listar snapshots y su uso de espacio único
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used -r tank/postgres | tail
tank/postgres@autosnap_2025-12-01_00:00:00 12.4G 1.05T Mon Dec 1 00:00 2025
tank/postgres@autosnap_2025-12-08_00:00:00 28.1G 1.06T Mon Dec 8 00:00 2025
tank/postgres@autosnap_2025-12-15_00:00:00 44.7G 1.08T Mon Dec 15 00:00 2025
tank/postgres@autosnap_2025-12-22_00:00:00 71.2G 1.09T Mon Dec 22 00:00 2025
tank/postgres@autosnap_2025-12-29_00:00:00 110.3G 1.10T Mon Dec 29 00:00 2025
Significado: used del snapshot es el espacio único para ese snapshot (la cantidad que normalmente liberarías al borrarlo, suponiendo que no existan otras referencias). Los snapshots más recientes a menudo tienen mayor used si la churn es alta.
Decisión: Apunta a los snapshots con mayor used primero, pero verifica dependencias.
Task 6: Comprobar holds en snapshots (el culpable de “¿por qué no se elimina?”)
cr0x@server:~$ zfs holds tank/postgres@autosnap_2025-12-29_00:00:00
NAME TAG TIMESTAMP
tank/postgres@autosnap_2025-12-29_00:00:00 zrepl_last_ok Mon Dec 30 02:10 2025
Significado: Este snapshot está retenido por una etiqueta, probablemente puesta por herramientas de replicación/backup. No puedes destruirlo hasta que la retención sea liberada (o fuerces, dependiendo de la implementación y el riesgo).
Decisión: Habla con el responsable de replicación; si es seguro, libera la retención primero. No uses la fuerza y esperes que la cadena de backups sobreviva.
Task 7: Liberar un hold (solo cuando sepas por qué existe)
cr0x@server:~$ sudo zfs release zrepl_last_ok tank/postgres@autosnap_2025-12-29_00:00:00
Significado: Hold eliminado. Ahora el snapshot es eliminable por políticas normales.
Decisión: Procede a destruir, pero confirma que no hay clones.
Task 8: Comprobar si un snapshot tiene clones dependientes
cr0x@server:~$ zfs get -H -o value clones tank/postgres@autosnap_2025-12-29_00:00:00
tank/devdb
Significado: Hay un dataset clone tank/devdb dependiente de ese snapshot. No puedes destruir el snapshot origen a menos que destruyas/promuevas el clone.
Decisión: Decide si el clone es prescindible, debe ser promovido, o debe respaldarse y recrearse.
Task 9: Inspeccionar el origen del clone y decidir la ruta de promoción
cr0x@server:~$ zfs get -H -o value origin tank/devdb
tank/postgres@autosnap_2025-12-29_00:00:00
Significado: tank/devdb es un clone de ese snapshot. Borrar el snapshot rompe la genealogía, cosa que ZFS previene.
Decisión: Si debes borrar el snapshot, o destruye el clone o promuévelo (lo que cambia la dependencia).
Task 10: Promover un clone para permitir la eliminación del snapshot (con cuidado)
cr0x@server:~$ sudo zfs promote tank/devdb
Significado: El clone se vuelve “principal”, y el dataset original pasa a depender (o al menos cambia el grafo de dependencia). Esto puede sorprender operativamente a herramientas que asumen una relación origen específica.
Decisión: Solo promueve si entiendes quién depende de qué dataset para replicación/backups. Tras promover, vuelve a verificar origin y clones en los snapshots implicados.
Task 11: Destruir un único snapshot de forma segura (con una lista de verificación)
cr0x@server:~$ zfs list -t snapshot tank/postgres@autosnap_2025-12-22_00:00:00
NAME USED AVAIL REFER MOUNTPOINT
tank/postgres@autosnap_2025-12-22_00:00:00 71.2G - 1.09T -
Significado: Estás viendo el objeto exacto que planeas borrar. Este paso humano evita errores de dedo.
Decisión: Si es el snapshot correcto, destrúyelo.
cr0x@server:~$ sudo zfs destroy tank/postgres@autosnap_2025-12-22_00:00:00
Significado: Snapshot eliminado. El espacio puede no reflejarse instantáneamente si otros snapshots aún referencian los mismos bloques, o si el sistema necesita tiempo para procesar liberaciones.
Decisión: Vuelve a comprobar métricas de espacio; si no hay cambio, investiga dependencias en lugar de repetir eliminaciones a ciegas.
Task 12: Destruir un rango de snapshots (limpieza dirigida)
cr0x@server:~$ zfs list -t snapshot -o name,creation -r tank/postgres | grep autosnap_2025-12 | head
tank/postgres@autosnap_2025-12-01_00:00:00 Mon Dec 1 00:00 2025
tank/postgres@autosnap_2025-12-08_00:00:00 Mon Dec 8 00:00 2025
tank/postgres@autosnap_2025-12-15_00:00:00 Mon Dec 15 00:00 2025
Significado: Has validado el esquema de nombres y qué snapshots coinciden con el mes/prefijo que deseas.
Decisión: Usa -d para borrado diferido si es necesario, y evita comodines que puedan coincidir con más de lo que crees.
cr0x@server:~$ sudo zfs destroy -d tank/postgres@autosnap_2025-12-01_00:00:00%autosnap_2025-12-15_00:00:00
Significado: Destruye snapshots en el rango inclusivo. El borrado diferido los marca para limpieza posterior, lo que puede reducir la perturbación inmediata pero demora la recuperación de espacio en algunos casos.
Decisión: Usa el borrado por rango cuando tengas nombres consistentes y hayas confirmado los límites. Si necesitas espacio inmediato, evita la diferición a menos que estés intercambiando latencia por sobrevivencia.
Task 13: Vigilar si el espacio libre aumenta realmente
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 7.25T 6.72T 530G - - 41% 92% 1.00x ONLINE -
Significado: El espacio libre aumentó de ~410G a ~530G. Eso es recuperación real.
Decisión: Sigue hasta tener margen operativo (típicamente 15–20% libre para comodidad en pools muy activos), luego detén las eliminaciones y arregla la política de retención para no volver la semana siguiente.
Task 14: Identificar sorpresas de refreservation/cuotas
cr0x@server:~$ zfs get -o name,property,value -s local -r tank | egrep 'refreservation|reservation|quota'
tank/vm refreservation 500G
tank/backups quota 250G
Significado: tank/vm está fijando 500G vía refreservation. Incluso si borras snapshots en otros lugares, puede que no recuperes AVAIL donde lo necesitas porque el espacio está reservado.
Decisión: Si estás en modo emergencia, considera bajar temporalmente la refreservation (con aprobación de las partes interesadas). Las cuotas también pueden causar errores de “espacio agotado” incluso cuando el pool tiene bloques libres.
Task 15: Comprobar si un dataset hijo es el verdadero acumulador de snapshots
cr0x@server:~$ zfs list -o name,used,usedsnap,refer -r tank/vm
NAME USED USEDSNAP REFER
tank/vm 2.20T 320G 1.80T
tank/vm/win1 900G 250G 640G
tank/vm/linux1 640G 40G 600G
Significado: El uso de snapshots se concentra en tank/vm/win1. Ahí obtendrás retornos significativos.
Decisión: No borres a lo loco en el dataset padre. Ve a donde está la churn.
Task 16: Confirmar que no vas a borrar el dataset por accidente
cr0x@server:~$ zfs list tank/postgres
NAME USED AVAIL REFER MOUNTPOINT
tank/postgres 3.55T 420G 1.10T /tank/postgres
Significado: Este es el dataset. No hay sufijo @snap. Si destruyes esto, no estarás “limpiando snapshots”.
Decisión: Si tu comando no incluye @, pausa y vuelve a teclearlo.
Eliminar snapshots de forma segura: patrones que no te pegarán un susto
Patrón 1: Elimina por “mayor usado único” primero, no por antigüedad
Eliminar por edad es emocionalmente satisfactorio y operativamente perezoso. Puede funcionar, pero no es la vía rápida cuando estás al 94% de capacidad. Cuando necesitas espacio ahora, borra los snapshots que sean únicos y costosos ahora.
En datasets de alta churn (bases de datos, imágenes de VM, cachés de build), los snapshots más nuevos suelen contener la mayor cantidad de datos únicos. Borrar “los más antiguos” podría liberar casi nada porque los snapshots viejos comparten la mayoría de sus bloques con otros snapshots y con el dataset en vivo.
Patrón 2: Trata holds y clones como dependencias de primera clase
Si un snapshot no se elimina, el sistema no está siendo terco. Está siendo correcto. Los holds significan que algún proceso declaró “este snapshot es necesario.” Los clones significan “un filesystem se construyó sobre este snapshot.” Ambos son razones para detenerse, no para añadir -f y esperar.
Patrón 3: Usa destroy diferido cuando la latencia importe más que la recuperación inmediata
La eliminación de snapshots puede ser costosa. Si tienes una carga sensible a la latencia y aún no estás sin espacio, el destroy diferido puede ser el compromiso correcto. Pero si estás sin espacio, diferir las liberaciones es como programar apagar un incendio para después del almuerzo.
Patrón 4: Prefiere una política de retención sobre limpiezas ad-hoc
Las purgas manuales de snapshots son un olor a mala práctica. La solución real es una retención acorde a los requisitos del negocio y a las realidades de replicación, además de alertas con umbrales sensatos (no “99% lleno”).
Broma corta #2: Retención de snapshots sin monitorización es como guardar recibos para siempre porque podrías devolver la casa algún día.
Por qué no vuelve el espacio después de borrar snapshots
Esta es la parte que hace desconfiar a la gente de ZFS. Borran snapshots, miran df -h, y ven… básicamente nada. El sistema no miente; simplemente estás mirando la capa equivocada o te falta una dependencia.
1) Otro snapshot todavía referencia los bloques
Si borras el snapshot A, pero el snapshot B (o el dataset vivo) todavía referencia la mayoría de los mismos bloques, no hay nada que liberar. La columna used en los snapshots es tu amiga porque aproxima lo que es atribuible de forma única.
2) Un clone depende del snapshot
Los clones son el problema silencioso de “¿por qué no puedo borrar esto?” en entornos corporativos porque alguien usó un snapshot para crear una copia de desarrollo “por una semana” y luego se convirtió en un servicio semi-producción con canal de Slack y sentimientos.
3) Un hold impide la eliminación
Las herramientas de backup/replicación suelen colocar holds para evitar que los humanos (tú) rompan las cadenas de replicación. Si liberas holds sin entender, puedes recuperar espacio hoy y perder tu punto de restauración mañana.
4) Liberaste bloques, pero el dataset que te importa aún tiene una cuota/refreservation
El espacio libre del pool y el espacio disponible del dataset no son lo mismo. Si tu aplicación escribe en un dataset con cuota, puede fallar aunque el pool tenga bloques libres. Por otro lado, una refreservation puede hacer que el pool parezca “lleno” aunque el filesystem no sea grande.
5) Estás revisando la métrica equivocada (df vs ZFS)
df -h reporta la vista a nivel de sistema de ficheros en los puntos de montaje. Para ZFS, zfs list y zpool list son las fuentes autorizadas. Usa df para comprobaciones de compatibilidad de aplicaciones, no para decisiones de incidentes.
Errores comunes: síntomas → causa raíz → solución
Error 1: “Borré snapshots y no liberé nada”
Síntomas: zpool list sigue mostrando ALLOC alto; USEDSNAP del dataset disminuye un poco o nada; la presión permanece.
Causa raíz: Borraste snapshots con bajo used único, o los bloques aún son referenciados por otros snapshots/clones.
Solución: Ordena snapshots por used y apunta a los mayores. Revisa zfs get clones y zfs holds para bloqueos.
Error 2: “zfs destroy dice que el dataset está ocupado / no se puede destruir”
Síntomas: La destrucción falla o se queja de dependencias.
Causa raíz: Existe un clone, o hay holds, o intentas destruir un snapshot que es origen para algo.
Solución: Identifica dependientes con zfs get clones en el snapshot. Decide: destruir clone, promover clone, o conservar el snapshot.
Error 3: “Liberamos espacio del pool pero la app sigue con ‘no space left’”
Síntomas: zpool list muestra espacio libre, pero las escrituras de la aplicación fallan; zfs list muestra AVAIL bajo en el dataset.
Causa raíz: Cuota, reservación o refreservation del dataset restringiendo AVAIL.
Solución: Inspecciona zfs get quota,reservation,refreservation. Ajusta con control de cambios; no elimines cuotas “temporalmente” sin un plan.
Error 4: “Alguien hizo rollback a un snapshot para ‘liberar espacio’”
Síntomas: Los datos aparecen revertidos; cambios recientes faltan; incidentes etiquetados como “corrupción” cuando en realidad es viaje en el tiempo.
Causa raíz: Confundir rollback con destroy; creer que el rollback elimina datos de snapshots.
Solución: Usa rollback solo como acción de recuperación con aprobación explícita y un snapshot previo al rollback. Para espacio, destruye snapshots; no hagas rollback.
Error 5: “Borramos el dataset en vez del snapshot”
Síntomas: Todo el filesystem desaparece; los puntos de montaje se van; servicios fallan; todo el mundo de repente sabe tu nombre.
Causa raíz: Error de dedo con zfs destroy pool/ds en lugar de zfs destroy pool/ds@snap; faltan medidas de seguridad; no hay revisión para comandos destructivos.
Solución: Requiere listar el dataset inmediatamente antes de destruir. Usa mínimo privilegio. Prefiere envoltorios interactivos de confirmación para operaciones manuales.
Error 6: “La eliminación de snapshots causó un pico de latencia”
Síntomas: La latencia de I/O aumenta durante la eliminación; las colas de latencia de la aplicación empeoran; el monitor muestra discos saturados.
Causa raíz: Eliminar gran número de snapshots fuerza actualizaciones de metadatos y trabajo de liberado de bloques; el pool ya está lleno/fragmentado; el hardware está saturado.
Solución: Borra en lotes más pequeños fuera de horas pico, considera destroy diferido, y mantiene el pool por debajo de umbrales dolorosos. A largo plazo: ajusta la cadencia y retención de snapshots.
Error 7: “La replicación falló después de la limpieza”
Síntomas: send incremental falla; la herramienta de replicación se queja de snapshot base faltante.
Causa raíz: Borraste snapshots que se usaban como anclas de replicación; los holds fueron ignorados o inexistentes.
Solución: Alinea la retención entre origen y destino. Usa holds para “no borrar” snapshots hasta que la replicación confirme. Valida con el responsable de replicación antes de purgar.
Tres mini-historias del mundo corporativo (reales, dolorosas, útiles)
Mini-historia 1: El incidente causado por una suposición equivocada
Tenían un pool con carga mixta: imágenes de VM, PostgreSQL y montones de artefactos de CI que nadie quería administrar. El pool empezó a llenarse más rápido después de un lanzamiento, y el on-call hizo lo que hace: borrar snapshots antiguos. Muchos.
El espacio no se movió. Para nada. La suposición era “los snapshots antiguos son grandes,” porque así piensa la mayoría sobre backups: viejo = pesado. Pero el tamaño de un snapshot ZFS no es la edad; es los bloques únicos. Los snapshots más antiguos tenían muy poco used único porque la churn había sido reciente.
Siguieron borrando. Nada. El pánico subió. Alguien sugirió “quizá ZFS está atascado,” que suele significar “no hemos identificado la dependencia.” El problema real: el snapshot más nuevo tenía un hold puesto por el trabajo de replicación. Era el ancla para incrementales, y además tenía la mayor churn única por una migración de esquema.
Una vez que inspeccionaron holds y apuntaron a snapshots por used único, liberaron suficiente espacio para estabilizar. La lección no fue “los holds molestan.” La lección fue: si no entiendes el grafo de snapshots, no estás operando almacenamiento—solo tiras palancas esperando suerte.
Mini-historia 2: La optimización que salió mal
Otra empresa quería “puntos de recuperación agresivos” para un servicio de cara al cliente. Configuraron autosnapshots cada 5 minutos y los mantuvieron durante dos semanas. Las cuentas en la hoja de cálculo parecían correctas porque el dataset “solo crecía un poco cada día”.
En realidad era una carga respaldada por VM con alta churn de bloques. Cada snapshot de 5 minutos capturó deltas únicos que eran individualmente pequeños pero acumulativamente brutales. El pool no falló inmediatamente. Falló en el peor momento: durante un incidente donde el servicio ya estaba degradado y los logs explotaban.
Intentaron borrar snapshots rápido para ganar espacio. Borrar decenas de miles de snapshots de golpe creó carga adicional, convirtiendo un servicio degradado en no disponible. No porque ZFS “no pueda manejarlo”, sino porque le pidieron al sistema hacer trabajo pesado de metadatos mientras manejaba carga de escrituras pico en un pool casi lleno.
La solución fue aburrida: reducir la cadencia de snapshots para ese dataset, implementar retención por niveles (frecuente a corto plazo, escasa a largo plazo) y mantener margen libre. “Más snapshots” no es fiabilidad; es trabajo almacenado como metadatos y bloques.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un equipo del área financiera usaba ZFS para servicios internos. Nada glamoroso. Sus sistemas eran los que olvidas hasta que caen. Tenían un runbook escrito para la limpieza de snapshots que exigía tres cosas: listar el objeto objetivo, simular la coincidencia, y confirmar dependencias (holds/clones) antes de eliminar.
Un fin de semana, un desarrollador pidió con urgencia un clone de datos de producción para depuración. Lo crearon desde un snapshot y lo dejaron en funcionamiento. Semanas después, la presión de almacenamiento empezó a subir. El on-call vio unos snapshots consumiendo espacio único inusualmente alto.
El runbook obligó a comprobar dependencias. Encontraron el clone atado exactamente a los snapshots que querían borrar. En vez de forzar, coordinaron: el clone de desarrollo se reemplazó por una copia actualizada en otro pool, y el clone viejo se destruyó. Entonces los snapshots pudieron borrarse de forma segura, y el espacio volvió de inmediato.
Lo mejor: nadie entró en pánico. No porque fueran más inteligentes, sino porque eran procedimentales. La práctica aburrida—comprobar clones y holds cada vez—previno un juego de dados de pérdida de datos y evitó romper replicación en un entorno regulado.
Listas de verificación / plan paso a paso
Checklist A: Limpieza de snapshots de emergencia “el pool está casi lleno”
- Confirma estado y capacidad del pool. Si
CAPes 95%+, trátalo como incidente. - Identifica los datasets principales por
USEDvsREFER. Gran delta implica mucha carga de snapshots. - Desglosa
USEDSNAPdel dataset peor. No adivines. - Lista snapshots ordenados por
usedúnico. Apunta a candidatos de alto retorno. - Para cada snapshot candidato: checa holds y clones.
- Elimina en lotes. Revisa el espacio libre del pool tras cada lote.
- Detente cuando tengas margen. Luego arregla la política y la planificación de capacidad para no repetir esto bajo estrés.
Checklist B: Operaciones diarias seguras (el plan “nunca más me llamen por esto”)
- Define retención por clase de dataset (BD, VM, home dirs, backups).
- Alinea retención con requisitos de replicación; no borres el último snapshot común.
- Configura alertas con umbrales sensatos (por ejemplo, 75/85/90%), no 98%.
- Revisa
refreservationy cuotas trimestralmente; documenta por qué existen. - Documenta el ciclo de vida de clones: quién puede crearlos, dónde viven y cuándo expiran.
- Practica restaurar desde snapshots periódicamente. Descubrirás fricciones operativas mientras sigue siendo un ejercicio.
Checklist C: Antes de ejecutar cualquier comando ZFS destructivo a mano
- Ejecuta
zfs listsobre el nombre exacto que vas a destruir. - Verifica la presencia de
@para snapshots. - Revisa
zfs holdsyzfs get clonespara snapshots. - Confirma puntos de montaje y nombres de datasets; cuidado con nombres similares (
tank/prodvstank/prod-db). - Pega el comando en un editor de texto primero si estás cansado. Los humanos somos poco fiables a las 03:00.
Preguntas frecuentes
1) ¿Eliminar un snapshot ZFS borra mis archivos?
No. Elimina la referencia del snapshot. Los archivos en vivo permanecen. Pero si dependías de ese snapshot para recuperar archivos borrados, ese punto de recuperación ya no existirá.
2) ¿Por qué zfs list -t snapshot muestra un snapshot con REFER enorme?
REFER en un snapshot refleja el tamaño del dataset en el momento del snapshot. No es “único.” La columna USED es la más relevante para saber “¿cuánto espacio probablemente recupero si borro esto?”
3) Destruí snapshots pero df -h no cambió. ¿Se rompió algo?
Probablemente no. Usa zpool list y zfs list para confirmar asignación de pool y datasets. df puede no reflejar lo que esperas porque no conoce todas las sutilezas de contabilidad de ZFS.
4) ¿Cuál es la diferencia entre borrar snapshots y hacer rollback?
Borrar snapshots elimina puntos de restauración y puede liberar espacio. El rollback reemplaza el contenido actual del dataset por el estado del snapshot, descartando cambios más recientes. El rollback es una acción de recuperación, no una herramienta de limpieza.
5) ¿Qué son los holds y quién los pone?
Los holds son etiquetas que impiden la eliminación de un snapshot. Herramientas de replicación/backup suelen ponerlos para garantizar que los snapshots requeridos permanezcan hasta que la replicación confirme el éxito.
6) ¿Por qué no puedo borrar un snapshot aun después de quitar holds?
Porque existe un clone que depende de él, o hay otra dependencia. Revisa con zfs get clones snapshot. Si hay clones, debes destruirlos o promoverlos antes de que el snapshot origen pueda eliminarse.
7) ¿Es seguro borrar snapshots en un sistema de producción ocupado?
Normalmente sí—pero puede ser costoso. Eliminaciones grandes o numerosas pueden aumentar la latencia, especialmente en pools llenos o fragmentados. Hazlo en lotes y fuera de horas pico cuando sea posible.
8) ¿Cuánto espacio libre debo mantener en un pool ZFS?
Las reglas varían según la carga, pero mantenerse muy por debajo del 90% es un buen valor por defecto. Alta utilización aumenta la presión de fragmentación y puede amplificar latencias durante escrituras y liberaciones intensas.
9) ¿Borrar snapshots mejorará el rendimiento?
A veces. Si el pool está muy lleno, recuperar espacio puede reducir la presión del asignador y mejorar el comportamiento de escritura. Pero el acto de eliminar puede temporalmente añadir carga.
10) ¿Debería usar destroy diferido (-d)?
Úsalo cuando el trabajo de borrado inmediato dañaría la latencia de producción y puedas tolerar la recuperación retrasada. Si necesitas espacio ahora, el destroy diferido puede no ofrecer el alivio que crees.
Conclusión: qué hacer a continuación en un sistema en producción
Si estás mirando un pool casi lleno, no “limpies snapshots antiguos” por instinto. Hazlo como una operación controlada:
- Cuantifica el problema:
zpool list, luegozfs list -o spaceen los mayores responsables. - Apunta a los snapshots correctos: ordena por
usedúnico, no por antigüedad. - Elimina bloqueos de forma segura: revisa holds y clones antes de cada destroy.
- Vuelve a comprobar tras cada lote: detente cuando hayas recuperado margen.
- Arregla la causa raíz: política de retención, alineación de replicación, cuotas/reservaciones y planificación de capacidad.
La verdadera victoria no es liberar 200GB a las 03:00. Es asegurarte de no tener que hacerlo otra vez bajo la deliciosa presión de “las escrituras fallan” y “¿quién ejecutó ese comando?”