Los fallos de almacenamiento en el arranque no suelen anunciarse con fuegos artificiales. Llegan de manera más silenciosa: un sistema que “arranca” pero no encuentra /var, un inicio de sesión que funciona pero los servicios no, o un host que monta el dataset de ayer porque parecía más dispuesto que el de hoy. Si usas ZFS el tiempo suficiente, acabarás encontrando la propiedad que decide si tus datasets esperan educadamente su turno o se abalanzan en la tabla de montajes en el peor momento posible.
Esa propiedad es canmount. Es pequeña, fácil de ignorar y ha salvado más sistemas de producción que cualquiera de las características más llamativas de ZFS. Si alguna vez un dataset se montó “misteriosamente” sobre un directorio existente, o un entorno de arranque arrancó con una raíz equivocada, normalmente la historia termina (o comienza) aquí — según la cantidad de café que quede.
Qué controla realmente canmount
canmount responde a una sola pregunta: “¿Es este dataset elegible para montarse?” No elige el punto de montaje; eso es mountpoint. No decide si el sistema intentará montarlo en el arranque; eso es una mezcla de canmount, mountpoint y la lógica del servicio de montaje ZFS de tu OS. Pero cuando las cosas salen mal, la presencia o ausencia de canmount suele ser la diferencia entre un arranque limpio y una búsqueda por todo /mnt.
Por qué esta propiedad importa operativamente:
- Los datasets ZFS son sistemas de archivos montables por defecto. Es una característica hasta que se convierte en un arma de doble filo.
- ZFS monta encantado los datasets en orden de dependencia, pero aún puede hacer algo que no pretendías—especialmente si clonaste, rehiciste, renombraste o importaste pools en un nuevo contexto.
- Los diseños tipo “entorno de arranque” (comunes en illumos, FreeBSD y algunas configuraciones Linux) dependen de
canmount=noautoen lugares concretos para evitar que la raíz equivocada se monte sola.
Una verdad operativa: ZFS no conoce tu ventana de cambios. Si dejas un dataset elegible para montarse con un mountpoint que apunta a una ruta crítica, ZFS lo tratará como una petición perfectamente razonable. Es como darle un filete a tu perro y esperar que espere hasta la cena.
Datos interesantes y contexto histórico
Algunos puntos breves de contexto que hacen que canmount parezca menos un interruptor aleatorio y más una cicatriz operativa:
- ZFS fue diseñado con la idea de que “los sistemas de archivos son baratos.” Por eso proliferan los datasets—por servicio, por inquilino, por aplicación—y por eso el comportamiento de montaje necesita barreras de seguridad.
- Las primeras implementaciones de ZFS aprendieron por las malas que el montaje automático es magnífico hasta que introduces clones, snapshots y raíces alternativas (
altroot) durante la recuperación. - La noción de “entornos de arranque” (múltiples raíces en el mismo pool) popularizó el patrón de poner las raíces en
canmount=noautopara que el sistema elija cuál será/. - La herencia de
mountpointes una característica central y conveniente de ZFS, pero se vuelve peligrosa cuando un dataset hijo hereda un mountpoint que no querías activo en ese host. - La lógica de montaje de ZFS ha diferido históricamente entre plataformas (SMF en Solaris/illumos, rc en FreeBSD, unidades systemd en Linux), pero
canmountsigue siendo el interruptor portátil de “no montes esto a menos que yo lo diga”. - Porque ZFS guarda propiedades en disco, un pool importado en otra máquina trae consigo sus intenciones de montaje. Es un regalo para la consistencia y una maldición para las sorpresas.
- Los clones y datasets promovidos pueden conservar propiedades de mountpoint que tenían sentido en su entorno original, no en el actual.
- Los “puntos de montaje solapados” (un dataset montado encima de un directorio que ya tiene contenido) han sido una fuente recurrente de interrupciones silenciosas porque el sistema parece sano mientras lee archivos equivocados.
Un modelo práctico: elegibilidad de montaje vs ubicación de montaje
Si no te llevas nada más de este artículo, llévate esto: el montaje en ZFS es una decisión en dos partes.
1) Elegibilidad: “¿Puedo montar este dataset?”
Eso es canmount. Si canmount=off, la respuesta es “no, nunca.” Si canmount=noauto, la respuesta es “no automáticamente.” Si canmount=on, la respuesta es “sí.”
2) Ubicación: “¿Dónde se montaría?”
Eso es mountpoint. Puede ser una ruta real como /var, puede heredarse, puede ser legacy (lo que significa “el montaje lo gestiona /etc/fstab o equivalente”), o puede ser none.
En el mundo real, los desastres ocurren cuando la elegibilidad y la ubicación se alinean accidentalmente con algo importante. Por ejemplo:
- Un clon de un dataset raíz es elegible (
canmount=on) y tiene mountpoint/. Eso no es un sistema de archivos; es un intento de golpe. - Un dataset destinado a trabajo de recuperación hereda
mountpoint=/vary es elegible para montarse. De repente, tu dataset de “recuperación” se convierte en tu/varde producción.
Broma #1: ZFS no “roba” tus puntos de montaje. Simplemente se los lleva a dar un paseo largo y vuelve vistiendo tu ropa.
Los tres valores: on, off, noauto
canmount suele tomar estos valores:
canmount=on
El dataset puede montarse y, por lo general, se montará automáticamente si tiene un mountpoint válido y el servicio de montaje ZFS de tu OS está activo. Esto es lo que quieres para datasets normales como pool/home o pool/var.
canmount=off
El dataset no puede montarse en absoluto. Aun así podrás hacerle snapshots, replicarlo y acceder a él por otros medios (por ejemplo montando snapshots en otro sitio), pero ZFS no lo montará como sistema de archivos. Esto es ideal para datasets “contenedor” que existen solo para albergar hijos o para llevar propiedades heredadas.
Patrón común: un dataset de nivel superior que existe solo para contener hijos y fijar propiedades comunes:
pool/ROOTconcanmount=off, hijos son entornos de arranquepool/containersconcanmount=off, hijos son datasets por contenedor
canmount=noauto
El dataset puede montarse, pero no se montará automáticamente. Este es el ajuste de «entorno de arranque» que evita que se monte por sí solo. No es “off”, es “no te metas a menos que yo lo pida”.
Es especialmente valioso para datasets que solo deberían montarse en un contexto específico:
- Un entorno de arranque que el cargador de arranque selecciona como raíz
- Un dataset que montas temporalmente para trabajo forense
- Un dataset usado para mantenimiento chroot, donde el montaje automático podría colisionar con la raíz en vivo
Broma #2: canmount=noauto es el ajuste de “no soy antisocial, simplemente no voy a tu reunión”.
De dónde vienen las sorpresas al arrancar
La mayoría de las sorpresas de ZFS en el arranque no son “ZFS está roto”. Son “ZFS hizo lo que se le dijo y nos olvidamos de lo que le dijimos el trimestre pasado.” Las causas recurrentes:
Sorpresa #1: Un dataset se monta sobre un directorio que ya tiene archivos críticos
Esto es sombreado de puntos de montaje. El directorio subyacente sigue existiendo, pero no puedes verlo porque hay un sistema de archivos montado encima. Si el directorio tenía archivos de configuración (por ejemplo bajo /etc en un escenario chroot, o /var/lib para una base de datos), puedes arrancar y ejecutar—pero con un conjunto de archivos distinto al esperado.
Sorpresa #2: Pools importados traen sus mountpoints
Las propiedades ZFS viven con el pool. Si importas un pool de otro host, puede venir con mountpoints como /data, /var o algo aterrador como /. En un sistema con montaje automático, eso puede convertir “solo estoy conectando un disco” en “¿por qué SSH dejó de funcionar?”.
Sorpresa #3: Los entornos de arranque se multiplican y uno se monta cuando no debe
Un diseño limpio de entornos de arranque espera que solo el BE seleccionado se monte como raíz. Si BEs antiguos son elegibles y tienen mountpoints significativos, también pueden montarse, a veces dentro del mismo árbol de directorios, causando conflictos y vistas parciales extrañas del sistema de archivos.
Sorpresa #4: “Optimizaste” los montajes y cambiaste el orden sin querer
El orden de montaje importa cuando los servicios esperan que los directorios existan antes de arrancar. Un dataset que monta tarde puede hacer que los servicios creen directorios en el sistema de archivos subyacente, y luego ZFS se monta encima, ocultando esos directorios recién creados. El servicio continúa feliz, ahora escribiendo en un lugar distinto al que creó en el arranque. Así es como terminas con “mis logs se perdieron” que en realidad es “mis logs están bajo el punto de montaje, no dentro de él”.
Tres micro-historias del mundo corporativo
Micro-historia 1: Un incidente causado por una suposición errónea
En una empresa mediana con una configuración ZFS-on-Linux bastante estándar, el equipo de almacenamiento mantenía un “pool de utilidades” usado para backups y restauraciones puntuales. El pool normalmente solo se importaba en un host de recuperación dedicado. Un viernes, alguien conectó las estanterías de backup a un servidor de aplicaciones de producción para restauraciones más rápidas—temporalmente, dijeron, con esa confianza particular que solo aparece justo antes de que un ticket se convierta en incidente.
La suposición fue simple: “Importar el pool no cambiará nada a menos que montemos algo.” Pero los datasets del pool tenían mountpoints como /var/lib/postgresql y /srv de una vida anterior. Esos datasets aún estaban en canmount=on, porque ¿por qué no iban a estarlo? El host de producción importó el pool, las unidades de systemd para montar ZFS hicieron su trabajo y de repente el servidor de aplicaciones tenía un nuevo /srv y un nuevo /var/lib/postgresql—justo encima de los antiguos.
La aplicación no falló inmediatamente. Eso fue lo más cruel. Siguió funcionando, leyendo algunos archivos de los datasets recién montados mientras aún tenía descriptores de archivo abiertos hacia los antiguos. Luego la base de datos se reinició durante la rotación de logs (como hacen las bases de datos cuando las tocas indirectamente), y volvió a arrancar viendo el directorio de datos “equivocado”. Desde fuera parecía pérdida de datos espontánea. Desde dentro, eran dos datasets peleando por el mismo espacio de nombres.
La solución fue aburrida: exportar el pool, poner datasets de nivel superior en canmount=off o mountpoint=none, y reimportar con un altroot cuando se hiciera el trabajo de restauración. La lección quedó clara: importar no es un acto pasivo cuando el auto-montaje está activado; es un evento de despliegue.
Micro-historia 2: Una optimización que salió mal
Una gran compañía estandarizó datasets por servicio: pool/var, pool/var/log, pool/var/lib y más profundos. Hizo las cuotas y snapshots ordenados. Pero también hizo que el arranque dependiera de una larga cadena de montajes. Durante una mejora de rendimiento, alguien intentó “simplificar” colapsando mountpoints y confiando en la herencia para reducir la proliferación de propiedades.
El cambio parecía limpio en la revisión: poner mountpoint=/var en un dataset padre y hacer que algunos hijos hereden. Pero un child dataset había sido configurado intencionadamente con canmount=noauto porque se usaba para una migración en curso: contenía datos en preparación y solo se montaba durante el corte. En el nuevo esquema de herencia, ese child empezó a heredar cambios de mountpoint mientras conservaba su valor de canmount—y alguien luego lo cambió a canmount=on para “probar algo”.
Semanas después, durante un reinicio, el dataset de preparación se montó automáticamente y sombreó un directorio bajo /var/lib que un servicio usaba para estado. El servicio arrancó limpio, creó nuevo estado en el sistema de archivos subyacente antes de que el montaje se completara (el tiempo importa), y luego ZFS se montó encima. El estado “desapareció.” El servicio entró en una ruta de inicialización y comenzó a reinyectar datos desde sistemas upstream, lo que causó picos de carga y una cascada de limitaciones. No fue un fallo único; fue una prueba de sistemas distribuidos autoinfligida.
La conclusión del postmortem fue contundente: la optimización ahorró casi ningún esfuerzo operativo pero eliminó una salvaguarda crítica. La remediación fue marcar los datasets “contenedor” como canmount=off por política, y tratar los cambios de canmount como una modificación de alto riesgo que exige una prueba de reinicio en staging. El problema no era la complejidad de ZFS—era olvidar que el arranque es el flujo de trabajo más sensible al tiempo que tienes.
Micro-historia 3: Una práctica aburrida pero correcta que salvó el día
Otra organización tenía pools ZFS en una flota de servidores de build. Estos hosts se reimaginaban constantemente, pero los pools a veces se movían entre máquinas cuando el hardware se reutilizaba. El equipo SRE tenía una práctica que nadie celebraba: cada importación de pool para pools no-root usaba altroot, y cada dataset de nivel superior usado para organizar subdatasets tenía canmount=off y mountpoint=none.
Un día un host falló y su pool se conectó a otro servidor para recuperar artefactos de build. El pool contenía datasets con mountpoints que, en su máquina original, apuntaban bajo /srv/build. En el host de recuperación, /srv ya existía y se usaba para otra cosa. Sin salvaguardas, esto habría sido el clásico fallo de “por qué mi servicio se reconfiguró”.
En cambio, la importación fue así: zpool import -o altroot=/mnt/recovery poolname. Todos los mountpoints se reubicaron bajo /mnt/recovery. Los datasets que no debían montarse permanecieron inelegibles. El equipo montó solo lo necesario, copió artefactos y exportó el pool. Ningún servicio parpadeó.
Esa práctica nunca hizo una diapositiva. Sí evitó que una acción de recuperación se convirtiera en un incidente. En operaciones de producción, “aburrido” no es un insulto; es un KPI.
Tareas prácticas (comandos + interpretación)
Estas son tareas que puedes ejecutar hoy en un sistema ZFS. Los comandos están escritos para el comportamiento típico del CLI de OpenZFS. Ajusta los nombres de pools/datasets para tu entorno.
Tarea 1: Listar datasets con el comportamiento de montaje en una vista
cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank
NAME CANMOUNT MOUNTPOINT MOUNTED
tank on /tank yes
tank/ROOT off none no
tank/ROOT/default noauto / yes
tank/var on /var yes
tank/var/log on /var/log yes
Interpretación: Busca datasets que estén en canmount=on (o accidentalmente noauto) con mountpoints que choquen con rutas críticas. tank/ROOT en off y none es un patrón sano de “dataset contenedor”.
Tarea 2: Encontrar datasets “elegibles para montarse” que actualmente no están montados
cr0x@server:~$ zfs list -H -o name,canmount,mountpoint,mounted -r tank | awk '$2=="on" && $4=="no" {print}'
tank/tmp on /tmp no
Interpretación: Un dataset elegible y no montado puede estar bien (quizá es nuevo o intencionalmente desmontado), pero también es una sorpresa al arranque esperando ocurrir si su mountpoint es sensible y el servicio de montaje cambia de comportamiento.
Tarea 3: Identificar datasets con mountpoint=legacy
cr0x@server:~$ zfs list -H -o name,mountpoint -r tank | awk '$2=="legacy" {print}'
tank/oldroot legacy
Interpretación: legacy significa que tu OS confiará en /etc/fstab (o equivalente) para montar. Mezclar montajes legacy con auto-montaje de ZFS a veces es necesario, pero aumenta el número de rutas de arranque que debes razonar.
Tarea 4: Comprobar la herencia de propiedades (herramienta de “por qué ocurrió esto”)
cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank/var
NAME PROPERTY VALUE SOURCE
tank/var canmount on local
tank/var mountpoint /var local
tank/var/log canmount on inherited from tank/var
tank/var/log mountpoint /var/log local
Interpretación: El campo source te dice si estás tratando con una anulación local o con herencia. La mayoría de misterios de “se montó en un lugar raro” se resuelven al notar que un hijo heredó una propiedad que asumías local en otro sitio.
Tarea 5: Hacer que un dataset contenedor no sea montable (por defecto seguro)
cr0x@server:~$ sudo zfs set canmount=off tank/containers
cr0x@server:~$ sudo zfs set mountpoint=none tank/containers
cr0x@server:~$ zfs get -o name,property,value tank/containers canmount,mountpoint
NAME PROPERTY VALUE SOURCE
tank/containers canmount off local
tank/containers mountpoint none local
Interpretación: Esto evita el montaje accidental del padre mientras permite que hijos como tank/containers/app1 se monten donde deben.
Tarea 6: Marcar un dataset de entorno de arranque como “montar solo cuando sea seleccionado”
cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/be-2025q4
cr0x@server:~$ zfs get -o name,property,value tank/ROOT/be-2025q4 canmount
NAME PROPERTY VALUE SOURCE
tank/ROOT/be-2025q4 canmount noauto local
Interpretación: Un entorno de arranque normalmente no debe montarse como un sistema de archivos regular tras la importación; debe montarse como raíz solo cuando el proceso de arranque lo elija. noauto es la barrera de seguridad.
Tarea 7: Montar temporalmente un dataset noauto para inspección
cr0x@server:~$ sudo zfs mount tank/ROOT/be-2025q4
cr0x@server:~$ zfs list -o name,mountpoint,mounted tank/ROOT/be-2025q4
NAME MOUNTPOINT MOUNTED
tank/ROOT/be-2025q4 / yes
Interpretación: Si el dataset tiene mountpoint=/, montarlo en un sistema en vivo es peligroso a menos que también uses una raíz alternativa (ver la siguiente tarea). En la práctica, para BEs normalmente los montas bajo una ruta de altroot, no en /.
Tarea 8: Importar un pool de forma segura usando altroot (mejor práctica de recuperación)
cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup
cr0x@server:~$ zfs mount | head
backup/ROOT/be-old /mnt/recovery/
Interpretación: altroot antepone un prefijo seguro a los mountpoints, así que un dataset cuyo mountpoint es /var pasa a ser /mnt/recovery/var. Así evitas que el trabajo de recuperación pise rutas de producción.
Tarea 9: Desmontar un dataset que se montó donde no debía
cr0x@server:~$ zfs list -o name,mountpoint,mounted | grep '/var/lib/postgresql'
backup/pgdata /var/lib/postgresql yes
cr0x@server:~$ sudo zfs unmount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME MOUNTPOINT MOUNTED
backup/pgdata /var/lib/postgresql no
Interpretación: Desmontar detiene el sangrado inmediato, pero no evita un remonte al reiniciar. Para eso necesitas arreglar canmount y/o mountpoint.
Tarea 10: Prevenir remonte estableciendo canmount=off
cr0x@server:~$ sudo zfs set canmount=off backup/pgdata
cr0x@server:~$ zfs get -o name,property,value backup/pgdata canmount
NAME PROPERTY VALUE SOURCE
backup/pgdata canmount off local
Interpretación: Esto hace que el dataset no sea elegible para montarse. Si aún necesitas acceso a su contenido, puedes cambiarlo temporalmente o usar otra técnica de recuperación, pero habrás dejado la configuración segura por defecto.
Tarea 11: Arreglar un mountpoint peligroso sin cambiar la elegibilidad
cr0x@server:~$ sudo zfs set mountpoint=/mnt/pgdata-staging backup/pgdata
cr0x@server:~$ sudo zfs set canmount=on backup/pgdata
cr0x@server:~$ sudo zfs mount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME MOUNTPOINT MOUNTED
backup/pgdata /mnt/pgdata-staging yes
Interpretación: A veces quieres que sea montable, simplemente no encima de un directorio de producción. Mover el mountpoint suele ser más seguro que confiar en “nadie importará este pool aquí”.
Tarea 12: Detectar riesgo de sombreado (datasets montados bajo árboles críticos)
cr0x@server:~$ zfs list -H -o name,mountpoint,canmount -r tank | awk '$2 ~ "^/(etc|var|usr|srv|home)($|/)" {print}'
tank/var /var on
tank/var/log /var/log on
tank/home /home on
Interpretación: Esto no es “malo”. Es un inventario de datasets que viven bajo árboles críticos. Cualquier entrada inesperada aquí merece investigarse antes del próximo reinicio.
Tarea 13: Verificar qué piensa ZFS que está montado
cr0x@server:~$ zfs mount | sed -n '1,8p'
tank /tank
tank/var /var
tank/var/log /var/log
tank/home /home
Interpretación: Esta es la vista de ZFS, no necesariamente la tabla de montajes completa del sistema. Aun así, es la lista autorizada de sistemas de archivos montados por ZFS.
Tarea 14: Cruzar comprobación con la tabla de montajes del kernel
cr0x@server:~$ mount | grep ' type zfs ' | head
tank/var on /var type zfs (rw,xattr,noacl)
tank/var/log on /var/log type zfs (rw,xattr,noacl)
Interpretación: Si zfs mount y mount discrepan, estás en territorio de casos límite: quizá un montaje fallido, quizá un montaje legacy, quizá un problema de namespace en contenedores.
Tarea 15: Encontrar datasets que intentarán montarse pero tienen mountpoint=none
cr0x@server:~$ zfs list -H -o name,canmount,mountpoint -r tank | awk '$2=="on" && $3=="none" {print}'
tank/ROOT on none
Interpretación: Esto suele ser un olor a mala configuración. Si un dataset puede montarse pero no tiene donde ir, probablemente sea un dataset contenedor que debería estar en canmount=off.
Tarea 16: Auditar cambios recientes comparando propiedades locales vs heredadas
cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank | awk '$4=="local" {print}' | head
tank mountpoint /tank local
tank/ROOT canmount off local
tank/ROOT mountpoint none local
tank/var mountpoint /var local
Interpretación: Las propiedades “locales” son donde tienden a introducirse las sorpresas. Esto te da una lista corta de lugares que los humanos han tocado.
Guía rápida de diagnóstico
Este es el flujo de trabajo de “son las 03:12 y el host no volvió limpio”. Asume que sospechas problemas de montaje ZFS. El objetivo es encontrar el cuello de botella rápido: ¿es un problema de importación, de elegibilidad de montaje, de conflicto de mountpoint o de orden de servicios?
Primero: confirmar importación del pool y salud básica
cr0x@server:~$ sudo zpool status
pool: tank
state: ONLINE
scan: scrub repaired 0B in 00:10:12 with 0 errors on Sun Dec 22 02:00:03 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
sda3 ONLINE 0 0 0
Interpretación: Si el pool no está importado o está degradado/faulted, deja de perseguir mountpoints. Arregla primero la importación/dispositivos. Las sorpresas de montaje son consecuencia de “el pool no está usable”.
Segundo: buscar colisiones de montaje obvias y trampas de elegibilidad
cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank | egrep ' /(var|etc|usr|srv|home|)($|/)'
Interpretación: Estás buscando datasets inesperados con mountpoints críticos. Presta atención a cualquier cosa que monte en /, /var o /usr que no diseñaste explícitamente.
Tercero: verificar qué está montado y qué falta
cr0x@server:~$ zfs mount
cr0x@server:~$ mount | grep ' type zfs '
Interpretación: Si faltan datasets esperados, comprueba canmount y mountpoint. Si hay datasets inesperados, busca pools extranjeros importados o entornos de arranque elegibles por error.
Cuarto: inspeccionar la herencia y la fuente de propiedades del dataset raro
cr0x@server:~$ zfs get -o name,property,value,source canmount,mountpoint tank/suspect
NAME PROPERTY VALUE SOURCE
tank/suspect canmount on inherited from tank
tank/suspect mountpoint /var local
Interpretación: Un mountpoint local con elegibilidad heredada es un patrón clásico de sorpresas: alguien cambió dónde se monta, pero no si debería ser elegible.
Quinto: si esto es una importación de recuperación, detente y usa altroot
Si importaste un pool no nativo y empezó a montarse en rutas de producción, la solución suele ser exportar y reimportar con una raíz alternativa.
cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup
Interpretación: Esto convierte la “montada sorpresa” en “todos los montajes están cercados bajo /mnt/recovery”, que es la postura correcta durante la respuesta a incidentes.
Errores comunes: síntomas y soluciones
Error 1: Dejar datasets contenedor en canmount=on
Síntoma: Un dataset padre como tank/ROOT aparece montado en algún sitio, o intenta montarse y genera advertencias confusas.
Por qué ocurre: Alguien lo creó y nunca cambió el valor por defecto. Hereda un mountpoint, o se le estableció un mountpoint durante experimentos.
Solución: Hazlo explícitamente no montable y quítale el mountpoint.
cr0x@server:~$ sudo zfs set canmount=off tank/ROOT
cr0x@server:~$ sudo zfs set mountpoint=none tank/ROOT
Error 2: Confundir canmount=noauto con “deshabilitado”
Síntoma: Un dataset se monta inesperadamente después de un zfs mount -a manual o por alguna automatización, o un operador lo monta sin darse cuenta de que es un dataset tipo BE/raíz.
Por qué ocurre: noauto no impide el montaje; impide el montaje automático. Humanos y scripts aún pueden montarlo.
Solución: Si realmente no quieres que se monte nunca, usa canmount=off. Si quieres que sea montable solo en contextos acotados, combina noauto con importaciones altroot y herramientas cuidadosas.
cr0x@server:~$ sudo zfs set canmount=off tank/archive/never-mount
Error 3: Importar un pool extranjero sin altroot
Síntoma: Tras importar un pool, los servicios se comportan de forma extraña, los directorios de datos “cambian” o rutas como /srv contienen contenido distinto.
Por qué ocurre: Los datasets del pool se montan donde estaban configurados para montarse y tu host lo respeta.
Solución: Exporta inmediatamente y re-importa con altroot. Opcionalmente, configura canmount=off o mountpoint=none en datasets de primer nivel antes de moverlos en el futuro.
cr0x@server:~$ sudo zpool export foreignpool
cr0x@server:~$ sudo zpool import -o altroot=/mnt/foreign foreignpool
Error 4: Poner mountpoint=/ en múltiples datasets (caos de entornos de arranque)
Síntoma: El arranque cae en shell de emergencia, o el sistema raíz no es el que esperabas. Después del arranque, ves múltiples datasets tipo BE que podrían plausiblemente montarse como raíz.
Por qué ocurre: Clonar o renombrar BEs sin actualizar propiedades de montaje y elegibilidad.
Solución: Asegura que solo la raíz seleccionada esté montada como / en el arranque. En muchos esquemas de BE, las raíces no seleccionadas deben estar en canmount=noauto. El ROOT contenedor debería ser off.
cr0x@server:~$ zfs list -o name,canmount,mountpoint -r tank/ROOT
cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/oldbe
Error 5: Confiar en “mounted=no” como prueba de que no se montará en el próximo arranque
Síntoma: Todo parece bien ahora, pero tras un reinicio el dataset se monta otra vez y repite el problema.
Por qué ocurre: mounted es estado, no política. canmount y mountpoint son política.
Solución: Establece la política explícitamente.
cr0x@server:~$ sudo zfs set canmount=off pool/surprise
cr0x@server:~$ sudo zfs set mountpoint=none pool/surprise
Error 6: Mezclar montajes legacy y auto-montaje ZFS sin un contrato claro
Síntoma: Algunos datasets se montan dos veces, o los montajes fallan de forma intermitente al arrancar, o ves estado contradictorio entre zfs mount y mount.
Por qué ocurre: Has dividido la responsabilidad entre ZFS y el sistema de montaje del OS, pero nadie documentó qué dataset gestiona quién.
Solución: O mueve esos datasets de vuelta a mountpoints gestionados por ZFS, o comprométete totalmente con montajes legacy para ellos y asegúrate de que canmount encaje con esa elección. Como mínimo, audita datasets con mountpoint=legacy y mantenlos intencionales.
Listas de verificación / plan paso a paso
Lista A: Diseñar una jerarquía de datasets que no te sorprenda al arrancar
- Crea datasets contenedor para agrupar y heredar propiedades (compression, atime, recordsize).
- Inmediatamente establece los datasets contenedor en
canmount=offymountpoint=none. - Fija mountpoints explícitos solo en datasets que deben montarse en operación normal.
- Usa
canmount=noautopara entornos de arranque o datasets que solo deben montarse por acción explícita. - Evita mountpoints ambiguos (especialmente
/,/var,/usr) salvo que lo hayas diseñado intencionadamente. - Documenta una invariante: “Solo estos datasets pueden montarse bajo rutas críticas,” y aplícala con auditorías periódicas.
Lista B: Antes de importar un pool desde otro sistema
- Decide el directorio de staging seguro (p. ej.,
/mnt/recovery). - Importa con
altroot. - Lista mountpoints y valores de canmount antes de montar nada explícitamente.
- Si necesitas que el pool sea portable, pon datasets de nivel superior en
canmount=offymountpoint=noneantes de exportarlo.
Lista C: Antes de reiniciar tras cambios en almacenamiento
- Ejecuta
zfs list -o name,canmount,mountpoint,mounted -r pooly busca rutas críticas. - Confirma que ningún dataset inesperado tenga
mountpoint=/. - Confirma que los datasets contenedor no sean montables.
- Si cambiaste la herencia, verifica con
zfs get ... sourcepara saber qué se propagará. - Si es posible, haz un reinicio controlado en staging primero. Los problemas de orden de arranque rara vez aparecen en un sistema en ejecución.
Preguntas frecuentes
1) ¿Cuál es la diferencia entre canmount=off y mountpoint=none?
canmount=off hace que el dataset no sea elegible para montarse. mountpoint=none significa “no tiene ubicación de montaje.” En producción, los datasets contenedor suelen tener ambos: no deben montarse y ni siquiera deberían tener un objetivo plausible.
2) Si pongo canmount=noauto, ¿puede el dataset montarse en el arranque?
En general, noauto impide que ZFS lo monte automáticamente. Pero los entornos de arranque son especiales: el proceso de arranque puede montar un dataset noauto como raíz porque está seleccionado explícitamente. Además, un admin o script aún puede montarlo manualmente.
3) ¿Por qué se montó un dataset después de importar un pool? No ejecuté zfs mount.
En muchos sistemas, la importación de un pool activa servicios de montaje ZFS que montan los datasets elegibles con mountpoints válidos. Importar no es “solo adjuntar almacenamiento”; es “activar las intenciones de filesystem del pool.” Usa zpool import -o altroot=... para pools extranjeros.
4) ¿Puedo usar canmount=off en un dataset que tiene hijos?
Sí. Los hijos aún pueden montarse con normalidad. Esta es una buena práctica para datasets padre/contenedor como pool/ROOT o pool/containers.
5) ¿Cómo sé si un montaje sorprendente se debe a herencia?
Usa zfs get ... source. Si canmount o mountpoint muestran “inherited from …” tienes una historia de herencia, no un comportamiento aleatorio.
6) ¿Es canmount lo mismo que poner readonly=on?
No. readonly=on aún monta el dataset, pero en modo solo lectura. canmount=off evita el montaje por completo. En escenarios de recuperación podrías usar ambos: mantener datasets montables pero en solo lectura, o mantenerlos no montables hasta que estés listo.
7) ¿Cuál es la forma más segura de inspeccionar un dataset de entorno de arranque que tiene mountpoint=/?
Importa el pool con altroot (o monta el dataset bajo un directorio seguro cambiando temporalmente su mountpoint). La clave es: no montes un dataset “raíz” en / de un sistema en ejecución a menos que disfrutes vivir peligrosamente.
8) ¿Debo poner canmount=noauto para todos los datasets y montarlos vía systemd/fstab en su lugar?
Puedes, pero estás cambiando una fuente de verdad por dos. Los montajes gestionados por ZFS suelen ser más simples si te comprometes con ellos. Reserva noauto para casos especiales: entornos de arranque, datasets de staging y flujos de trabajo de mantenimiento.
9) ¿Por qué veo datasets con canmount=on pero mounted=no?
La elegibilidad no garantiza que esté montado ahora. El montaje pudo fallar, el punto de montaje puede no existir, el pool pudo haberse importado con -N (no montar) o el servicio de montaje del OS no se ha ejecutado aún. Trátalo como una pista, no como un veredicto.
Conclusión
canmount no es glamoroso. No comprime datos, no acelera lecturas y no impresionará a nadie en una demo de producto. Lo que hace es evitar el tipo de sorpresas en el arranque y en la importación que consumen mañanas enteras y te dejan con la sensación incómoda de que los sistemas de archivos son conscientes.
Si usas ZFS en producción, conviértelo en hábito: los datasets contenedor en canmount=off; los datasets de propósito especial en canmount=noauto; el resto explícitamente diseñado como on con un mountpoint que puedas defender en un postmortem. Los mejores incidentes de almacenamiento son los que nunca tienes que nombrar.