El error aparece en el peor momento posible: despliegues, copias de seguridad, trabajos de CI, llamadas de incidente donde todos insisten en que “no cambiaron nada”.
Tu proceso intenta leer un archivo y el kernel responde con un encogimiento de hombros: Stale file handle.
En Ubuntu 24.04 no hay nada “nuevo” en esto—pero las combinaciones que lo disparan siguen evolucionando: contenedores fijando rutas, systemd automount,
cambios agresivos en exports, conmutaciones por fallo de NAS y equipos de almacenamiento “optimizando” layouts a mitad del día. Trátalo como un bug de producción:
comprende el mecanismo, aísla el modo de fallo y elimínalo con prácticas aburridas y repetibles.
Qué significa realmente “Stale file handle”
En Linux, “Stale file handle” corresponde a ESTALE. En términos de NFS, es el cliente diciendo:
“Mantengo una referencia a un archivo/directorio que el servidor ya no reconoce.”
Esa referencia es un file handle—bytes opacos devueltos por el servidor NFS que identifican de forma única a un objeto tipo inode en ese servidor.
El cliente cachea esos handles, porque pedir al servidor que vuelva a resolver rutas en cada operación sería un crimen de rendimiento.
Cuando el servidor después no puede resolver el handle, obtienes ESTALE.
Lo importante: esto no es un error de permisos, ni es “la red que hace cosas raras.” Es una descoordinación entre lo que el cliente cree
que existe y lo que el servidor puede mapear de nuevo a un objeto. A veces es causado por la eliminación legítima del objeto. Con más frecuencia es causado por
un cambio en la identidad del lado del servidor cuando no lo esperabas.
En un outage, el mensaje del kernel es brutalmente honesto: el servidor dice “nunca oí hablar de ese handle”, y tu proceso no puede continuar.
Si tu carga de trabajo es un sistema de compilación, un runtime de contenedores o una base de datos que espera semánticas consistentes del sistema de archivos,
verás fallos que parecen aleatorios hasta que los relaciones con eventos de topología de NFS.
Por qué ocurre (las causas reales)
Los file handles son identidades, no rutas
NFS trata los archivos mayormente como objetos. Las rutas se resuelven en file handles, luego las operaciones usan esos handles.
Por eso un archivo puede renombrarse mientras está abierto y aún leerse localmente—Linux sigue la inode. Con NFS, el cliente rastrea una identidad emitida por el servidor.
El desencadenante principal: el servidor ya no puede mapear el handle
Eso ocurre cuando:
- El objeto subyacente del sistema de archivos desapareció de verdad (borrado, filesystem recreado, rollback de snapshot, etc.).
- El servidor cambió su idea de identidad (export movido, fsid cambiado, filesystem remontado de forma diferente, failover a un backend distinto).
- El handle cacheado por el cliente se refiere a un filesystem distinto del que el servidor ahora expone en esa ruta (clásico error al cambiar exports).
Eventos comunes del lado del servidor que invalidan handles
- Reconfiguración de exports: cambiar /etc/exports, mover exports, pasar de exportar un directorio a exportar su padre, etc.
- Reemplazo del filesystem: reformateo, restauración desde backup, recrear un dataset, recrear un árbol de directorios.
- Rollback de snapshot en el servidor: la ruta existe, pero las identidades de los objetos se han retrocedido.
- Failover a un nodo distinto: clústeres HA donde el nodo nuevo sirve la “misma ruta” pero no las mismas IDs de objeto.
- Cruce de puntos de montaje en exports: exportar una ruta que contiene otros mounts y luego cambiar esos mounts.
Contribuyentes del lado cliente (no suelen ser la causa raíz, pero lo amplifican)
- Procesos de larga duración que mantienen FDs de directorio y asumen que siempre serán válidos (CI runners, agentes de logs, language servers).
- Contenedores que hacen bind-mounts de rutas NFS; el contenedor sobrevive mientras la identidad NFS cambia por debajo.
- Automounters que ocultan el churn de mount/unmount hasta que un job en background golpea un FD stale.
- Suposiciones de cache agresivas (attribute caching, lookup caching) que hacen que el comportamiento parezca “pegajoso”.
NFSv3 vs NFSv4: la idea errónea de “stateful”
NFSv4 es stateful en formas que NFSv3 no lo es (locks, delegaciones, sesiones), pero los file handles aún pueden quedar stale. El protocolo puede negociar mucho,
pero no puede resucitar una identidad que el servidor no puede mapear a un objeto.
Una cita que la gente de operaciones tiende a reaprender con dolor: «Todo falla, todo el tiempo.»
— Werner Vogels.
Es directa, no es cínica. Diseña pensando en ello.
Dos chistes, porque los humanos los necesitan
Chiste #1: Los file handles de NFS son como tarjetas de acceso corporativas—una vez que seguridad invalida la tuya, aún conoces el pasillo, pero no vas a entrar.
Chiste #2: Si “arreglaste” stale file handles reiniciando clientes, felicidades: acabas de inventar la estrategia de invalidación de caché más cara.
Datos históricos e interesantes (breve y concreto)
- NFS se originó en Sun Microsystems en los 80; la idea central era “transparencia de red” para archivos Unix.
- NFSv2 usaba tamaños de archivo de 32 bits; los archivos grandes impulsaron la evolución hacia NFSv3 y posteriores.
- NFSv3 es mayormente stateless en el servidor, lo que simplificó la recuperación pero puso más carga en clientes y file handles.
- NFSv4 introdujo un pseudo-root y un modelo de namespace más fuerte; los exports se comportan diferente que en v3 “solo exportar un directorio”.
- ESTALE precede a NFS como error Unix para referencias remotas obsoletas, pero NFS lo hizo popular en ops.
- Los file handles son intencionalmente opacos para que los servidores puedan cambiar el mapeo interno de inodes sin que los clientes lo noten—hasta que el servidor no puede mapearlo.
- Algunos proveedores NAS codifican IDs de filesystem en los handles; si esos IDs cambian durante un failover, los handles mueren aunque las rutas parezcan iguales.
- “Subtree checking” (una característica del servidor) históricamente causó comportamientos raros con renombres; muchos administradores lo deshabilitan por cordura.
- Los clientes Linux cachean dentries agresivamente; esto es bueno para rendimiento y malo para depurar cuando las identidades cambian por debajo.
Playbook de diagnóstico rápido (primero/segundo/tercero)
Estás de guardia. No tienes tiempo para un seminario filosófico sobre sistemas de archivos distribuidos. Aquí está el camino más corto hacia la verdad.
Primero: confirma que es ESTALE y encuentra el alcance
- ¿Es un host o muchos?
- ¿Un montaje o todos los montajes NFS?
- ¿Un subtree de directorio o archivos aleatorios por todas partes?
Segundo: determina si la identidad del servidor cambió
- ¿Se cambiaron los exports?
- ¿Se remontó, reemplazó, hizo failover o se restauró el filesystem?
- ¿Alguien “reconstruyó el share” manteniendo la misma ruta?
Tercero: decide la estrategia de recuperación según el riesgo de la carga
- Si es una carga mayoritariamente de lectura: remount suele ser suficiente.
- Si es intensivo en escritura o usa locks (bases de datos, caches de build): para la app de forma segura, luego remonta y reinicia. Evita medias tintas.
- Si son muchos clientes a la vez: trátalo como un evento del servidor/export, no como “flakiness del cliente”. Arregla la identidad del servidor primero.
Después: prevenir recurrencias
- Deja de cambiar exports en producción durante horas de negocio.
- Fija identidades de filesystem estables (estrategia fsid, datasets de backend estables).
- Usa systemd automount con criterio, no como una alfombra mágica sobre el churn subyacente.
Tareas prácticas: comandos, salidas, decisiones (12+)
Estas son comprobaciones reales que puedes ejecutar en clientes y servidores Ubuntu 24.04. Cada tarea incluye: comando, salida típica, qué significa
y qué decisión tomar.
Task 1: Verificar que el error es realmente ESTALE (cliente)
cr0x@server:~$ dmesg -T | tail -n 20
[Mon Dec 29 11:02:14 2025] NFS: stale file handle
[Mon Dec 29 11:02:14 2025] NFS: v4 server nfs01 returned a stale file handle
Significado: El cliente NFS del kernel recibió ESTALE del servidor. Esto no es una excepción a nivel de aplicación; es la capa de sistema de archivos.
Decisión: Trátalo como un problema de consistencia/identidad. Empieza a revisar eventos en el servidor/export, no solo latencia de red.
Task 2: Identificar qué montaje está afectado (cliente)
cr0x@server:~$ findmnt -t nfs,nfs4 -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET SOURCE FSTYPE OPTIONS
/mnt/build nfs01:/exports/build nfs4 rw,relatime,vers=4.1,hard,timeo=600,retrans=2
/mnt/archive nfs02:/exports/archive nfs4 ro,relatime,vers=4.2,hard,timeo=600,retrans=2
Significado: Muestra el servidor:ruta exacta y las opciones. La cadena source importa al correlacionar con cambios de export.
Decisión: Si solo un montaje está implicado, céntrate en el backend de ese export. Si son varios, sospecha de eventos a nivel de servidor o failover.
Task 3: Reproducir y capturar la ruta que falla (cliente)
cr0x@server:~$ ls -la /mnt/build/workdir
ls: cannot access '/mnt/build/workdir/.': Stale file handle
total 0
Significado: El handle del directorio está stale. A menudo indica un rename/move/remape de export o un swap de filesystem detrás de ese directorio.
Decisión: No reinicies aplicaciones a ciegas aún. Recoge evidencia: qué directorio, qué montaje y si otras rutas funcionan.
Task 4: Comprobar si el servidor NFS es alcanzable y responde (cliente)
cr0x@server:~$ rpcinfo -t nfs01 nfs 4
program 100003 version 4 ready and waiting
Significado: Alcanzabilidad RPC básica. No demuestra que tu export esté sano, pero descarta “servidor caído.”
Decisión: Si esto falla, trátalo primero como problema de conectividad/firewall/servicio RPC. ESTALE puede ser ruido secundario.
Task 5: Listar exports desde la perspectiva del servidor (cliente)
cr0x@server:~$ showmount -e nfs01
Export list for nfs01:
/exports/build 10.20.0.0/16
/exports/shared 10.20.0.0/16
Significado: Confirma lo que el servidor dice que exporta (a menudo más útil con NFSv3; sigue siendo útil para comprobaciones de cordura).
Decisión: Si el export desapareció o cambió, tienes la pistola humeante: tu montaje cliente apunta a una suposición antigua.
Task 6: Confirmar versión NFS y parámetros negociados (cliente)
cr0x@server:~$ nfsstat -m
/mnt/build from nfs01:/exports/build
Flags: rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.20.3.41,local_lock=none
Significado: Muestra los parámetros de montaje negociados. Te ayuda a detectar montajes “soft”, timeouts extraños o versiones legacy.
Decisión: Si ves soft para datos críticos, planea cambiarlo. Los montajes soft pueden convertir problemas transitorios en corrupción de datos o escrituras parciales.
Task 7: Determinar si el problema se limita a un subtree (cliente)
cr0x@server:~$ (cd /mnt/build && ls -la . && ls -la ./workdir) 2>&1 | sed -n '1,6p'
total 8
drwxr-xr-x 4 root root 4096 Dec 29 10:58 .
drwxr-xr-x 3 root root 4096 Dec 29 10:58 ..
ls: cannot access './workdir': Stale file handle
Significado: La raíz del montaje está bien; un subdirectorio tiene el handle stale. A menudo causado por rename en el servidor, rollback de snapshot o reemplazo de ese subtree.
Decisión: Escala al propietario del servidor/almacenamiento: “La identidad de este directorio cambió.” El remount puede ayudar; pero también busca el evento en el servidor.
Task 8: Identificar procesos que mantienen FDs en la ruta stale (cliente)
cr0x@server:~$ sudo lsof +D /mnt/build/workdir 2>/dev/null | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 4112 ci cwd DIR 0,1234 0 862 /mnt/build/workdir (stale)
make 4188 ci cwd DIR 0,1234 0 862 /mnt/build/workdir (stale)
Significado: Estos procesos tienen su current working directory o un FD abierto apuntando al directorio stale.
Decisión: Para una recuperación segura, para estos procesos antes de remontar. De lo contrario seguirán fallando o, peor, mantendrán reintentos y bloquearán pipelines.
Task 9: Intentar un remount limpio en el cliente (cliente)
cr0x@server:~$ sudo umount /mnt/build
umount: /mnt/build: target is busy.
Significado: Algo mantiene el montaje ocupado (archivos abiertos, cwd, bind mounts dentro de contenedores).
Decisión: No fuerces a ciegas. Encuentra los holders (tarea siguiente) y detenlos, o usa un lazy unmount solo como medida táctica.
Task 10: Encontrar qué mantiene el montaje ocupado (cliente)
cr0x@server:~$ sudo fuser -vm /mnt/build
USER PID ACCESS COMMAND
/mnt/build: root kernel mount /mnt/build
ci 4112 ..c.. python3
ci 4188 ..c.. make
Significado: Lista procesos que usan el montaje. Las banderas ACCESS indican cómo lo tocan (cwd, archivo abierto, etc.).
Decisión: Para/termina a los ofensores, o coordina con los responsables de las cargas. Si es un host de build compartido, un drain educado vence a un kill sorpresa.
Task 11: Usar unmount lazy cuando sea necesario (cliente)
cr0x@server:~$ sudo umount -l /mnt/build
Significado: Desconecta el montaje del namespace; las referencias existentes permanecen hasta que los procesos las liberen.
Decisión: Usa esto solo si ya estás en modo incidente y necesitas restaurar servicio. Posteriormente reinicia los servicios afectados para soltar FDs stale.
Task 12: Remontar con opciones explícitas y probar (cliente)
cr0x@server:~$ sudo mount -t nfs4 -o vers=4.1,hard,timeo=600,retrans=2 nfs01:/exports/build /mnt/build
cr0x@server:~$ stat /mnt/build/workdir
File: /mnt/build/workdir
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 0,1234 Inode: 862 Links: 12
Significado: Si stat funciona de nuevo, la recuperación del lado cliente tuvo éxito. Si sigue fallando, el servidor sigue presentando una identidad distinta o un export roto.
Decisión: Si el remount lo arregla, planifica trabajo de prevención de todos modos. Remontar es recuperación, no cura.
Task 13: Revisar la configuración de export y eventos de recarga (servidor)
cr0x@server:~$ sudo exportfs -v
/exports/build 10.20.0.0/16(rw,wdelay,root_squash,no_subtree_check,sec=sys,fsid=101)
/exports/shared 10.20.0.0/16(rw,wdelay,root_squash,no_subtree_check,sec=sys,fsid=102)
Significado: Muestra las opciones efectivas de export. Fíjate en la presencia de fsid—la estabilidad importa, especialmente con NFSv4 y HA.
Decisión: Si falta fsid o cambió recientemente, considera fijarlo. Si las rutas de export cambiaron, espera handles stale hasta que los clientes remunten.
Task 14: Inspeccionar logs del servidor por re-export/restart/eventos de FS (servidor)
cr0x@server:~$ sudo journalctl -u nfs-server -u rpcbind -u nfs-idmapd --since "2 hours ago" | tail -n 25
Dec 29 10:41:08 nfs01 systemd[1]: Stopped NFS server and services.
Dec 29 10:41:08 nfs01 systemd[1]: Starting NFS server and services...
Dec 29 10:41:09 nfs01 exportfs[2143]: exporting 10.20.0.0/16:/exports/build
Dec 29 10:41:09 nfs01 systemd[1]: Started NFS server and services.
Significado: Confirma reinicios de servicio y recargas de export. Los reinicios por sí solos no siempre causan ESTALE, pero correlacionan con remounts de backend y acciones de failover.
Decisión: Si ves reinicios alrededor del tiempo del incidente, busca qué los desencadenó: mantenimiento, gestión de configuración, actualizaciones de paquetes, scripts de failover.
Task 15: Confirmar que la ruta exportada es el filesystem que crees (servidor)
cr0x@server:~$ findmnt -T /exports/build -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET SOURCE FSTYPE OPTIONS
/exports/build /dev/sdb1 ext4 rw,relatime
Significado: Verifica qué respalda el export. Si este dispositivo/dataset de origen cambió desde ayer, los handles pueden quedar stale aunque la ruta no cambie.
Decisión: Si el backing filesystem cambió, trata el evento como un cambio rompedor. Comunica a los propietarios de clientes y planifica ventanas de remount/restart.
Task 16: Detectar indicios de rollback de snapshot / recreación de dataset (servidor)
cr0x@server:~$ sudo tune2fs -l /dev/sdb1 | sed -n '1,12p'
tune2fs 1.47.0 (5-Feb-2023)
Filesystem volume name: builddata
Filesystem UUID: 6f8f2b7f-7c3f-4b0e-9b8b-3a8e5f7e0c2a
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit
Significado: Los cambios de UUID son una gran pista de que el filesystem fue recreado. Un rollback puede no cambiar UUID, pero la recreación usualmente sí.
Decisión: Si el UUID cambió, deja de discutir “no hubo cambios.” Algo reemplazó el filesystem. Espera handles stale hasta que los clientes remunten y las apps reabran rutas.
Task 17: Revisar estadísticas RPC y retrans por una red ruidosa (cliente)
cr0x@server:~$ nfsstat -rc
Client rpc stats:
calls retrans authrefrsh
153209 18 153227
Significado: Retrans altas pueden indicar pérdida de paquetes o congestión. Esto no causará ESTALE directamente, pero puede hacer la recuperación más lenta y los síntomas más confusos.
Decisión: Si retrans es alta, tienes dos problemas: un problema de identidad y uno de transporte. Arregla primero la identidad, luego estabiliza la red.
Task 18: Si systemd automount está involucrado, confirma el estado de la unidad de montaje (cliente)
cr0x@server:~$ systemctl status mnt-build.automount --no-pager
● mnt-build.automount - Automount mnt-build
Loaded: loaded (/etc/systemd/system/mnt-build.automount; enabled; preset: enabled)
Active: active (waiting) since Mon 2025-12-29 10:12:10 UTC; 51min ago
Where: /mnt/build
Significado: Automount puede desmontar cuando está inactivo y remontar después. Eso está bien—hasta que algo mantiene un FD stale mientras el montaje es reemplazado.
Decisión: Si ves churn frecuente de montaje, considera si tu carga es compatible. Daemons de larga duración + automount pueden ser una mala combinación.
Errores comunes: síntoma → causa raíz → solución
“Solo un directorio da error; el resto del share está bien”
Síntoma: ls funciona en la raíz del montaje, pero un subdirectorio específico da “Stale file handle.”
Causa raíz: Ese subtree fue renombrado, reemplazado, revertido por snapshot o es un punto de montaje que cambió debajo de un padre exportado.
Solución: Remonta el cliente para vaciar handles. Si se repite, deja de exportar rutas que contienen mountpoints volátiles; exporta límites estables de filesystem.
“Comenzó justo después de que ‘limpiamos’ /etc/exports”
Síntoma: Muchos clientes ven ESTALE poco después de un cambio de export.
Causa raíz: Cambiaron las semánticas de la ruta exportada (padre vs hijo, export cruzado de mounts, cambios de fsid). Los clientes mantienen handles a objetos servidos bajo la antigua asignación.
Solución: Trata los cambios de export como cambios rompientes. Coordina una ventana de remount/restart para clientes. Fija valores fsid y mantiene topología de exports estable.
“Reiniciar el servidor NFS lo arregló, pero volvió”
Síntoma: Alivio temporal, luego handles stale recurrentes.
Causa raíz: Failover de backend o acciones del ciclo de vida del almacenamiento (rollback, rebuild, corte de replicación) están cambiando identidad de objeto repetidamente.
Solución: Arregla el proceso de ciclo de vida: backends estables, IDs de filesystem consistentes y cortes controlados con coordinación de clientes. Los reinicios son manejo de síntomas.
“Sucede durante tests de failover”
Síntoma: Un evento HA dispara ESTALE generalizado.
Causa raíz: El nodo de failover presenta la misma ruta export pero diferente identidad de filesystem subyacente o mapeo fsid inconsistente.
Solución: Diseña HA con identidad estable: mismo backend replicado con IDs consistentes, o acepta que los clientes deben remount y reiniciar apps como parte del runbook de failover.
“Es un problema de Docker/Kubernetes”
Síntoma: Pods informan stale handles; los nodos parecen bien; el equipo de almacenamiento señala a Kubernetes.
Causa raíz: Los contenedores mantienen montajes de larga duración y FDs de directorio; el servidor NFS cambió identidad. Kubernetes solo facilita mantener cosas corriendo lo suficiente para notarlo.
Solución: Deja de tratar NFS como un backing store infinitamente mutable. Si debes cambiar exports/backends, haz rotación de pods/nodos para refrescar handles. Prefiere PVs estables y evita mover exports.
“Usamos montajes ‘soft’ para evitar bloqueos, ahora tenemos artefactos raros”
Síntoma: Fallos intermitentes, salidas parciales y a veces handles stale después de estrés.
Causa raíz: Los montajes soft permiten que operaciones fallen a mitad de flujo; la aplicación no estaba diseñada para manejar I/O parcial.
Solución: Para datos críticos, usa montajes hard y ajusta timeouts. Si necesitas comportamiento no bloqueante, rediseña el flujo (staging local, reintentos en la capa de aplicación).
Tres micro-historias corporativas desde el terreno
Incidente #1: la suposición equivocada (“Las rutas son identidades”)
Una empresa ejecutaba un farm de builds monorepo en Ubuntu. Los workers de build montaban /mnt/build desde un servidor NFS.
Un viernes, un ingeniero de almacenamiento migró el share a un volumen nuevo. Mantuvieron la misma ruta en el servidor: /exports/build.
La suposición fue simple y muy humana: si la ruta es la misma, los clientes no se van a enterar.
El lunes por la mañana, CI empezó a fallar de una forma que parecía un compilador inestable. Trabajos aleatorios morían intentando hacer stat() de directorios temporales.
Algunos trabajos funcionaban, otros no. Los reintentos a veces tenían éxito. El equipo de build culpó al scheduler de CI. El equipo de scheduler culpó a los workers.
El equipo de almacenamiento culpó a “la caché de Linux.”
La pista fue que solo los workers de larga duración fallaban. Los workers recién reiniciados estaban bien. En hosts afectados,
lsof mostró antiguos cwd dentro del workspace apuntando a directorios stale. Remontar lo arregló al instante.
La migración del servidor no “rompió NFS.” Cambió las identidades de objeto detrás de una ruta.
La solución no fue heroica: coordinar migraciones de share con una ventana de refresco de clientes. Eso implicó un drain controlado de workers,
unmount/remount, y luego reincorporación. La acción postmortem fue aún más aburrida: trata los backends NFS como versiones de API.
Si cambias el mapa de identidad, los clientes deben reciclarse.
Incidente #2: la optimización que salió mal (exportar el padre para “simplificar”)
Otra organización tenía múltiples exports: /exports/app1, /exports/app2, /exports/app3.
Alguien decidió que eran “demasiados mounts.” La propuesta: exportar solo /exports y dejar que los clientes usen subdirectorios.
Un montaje para gobernarlos a todos. El cambio se desplegó gradualmente, porque la gestión de configuración facilitó “simplemente switch.”
Un mes después, aparecieron stale file handles en solo una app, y solo durante despliegues. Fue enloquecedor.
El proceso de deploy creaba un nuevo directorio de release, cambiaba un symlink y luego limpiaba releases antiguas. Cosas estándar.
En discos locales funciona. En NFS, generalmente funciona—hasta que exportas un padre que contiene otros puntos de montaje y los reordenas.
El detalle oculto: /exports/app2 ya no era un directorio en el mismo filesystem; se había convertido en un mount separado.
El “export único” ahora cruzaba límites de filesystem. Durante una ventana de mantenimiento, el mount que respaldaba app2 fue desmontado y remontado.
Los clientes que tenían handles cacheados en ese subtree empezaron a obtener ESTALE, mientras otras apps seguían sanas.
Volver a exports separados lo arregló. No porque “más mounts sean mejores”, sino porque cada export volvió a mapear a un límite de filesystem estable.
La optimización redujo la complejidad de configuración e incrementó la complejidad de incidentes. Ese intercambio rara vez compensa.
Incidente #3: la práctica aburrida que salvó el día (IDs estables y remounts coordinados)
Una empresa de perfil financiero usaba NFS para herramientas compartidas y artefactos de build. Tenían un par HA y hacían tests regulares de failover.
Al principio, aceptaron que los failovers eran caóticos: los clientes a veces lanzaban stale handles y la gente “simplemente reiniciaba cosas.”
Luego se pusieron serios y escribieron un runbook que trataba la estabilidad de identidad como requisito de primera clase.
Fijaron valores fsid en exports, mantuvieron rutas de export ligadas a límites estables de filesystem y se negaron a cambiar topología de exports en sitio.
Cuando los backends tuvieron que moverse, lo planificaron como una migración de esquema. Hubo ticket de cambio, drain de clientes y un remount coordinado.
Nada heroico, solo disciplina.
Durante un incidente posterior por firmware de almacenamiento, el par HA hizo failover inesperado. Algunos clientes aún necesitaron remount,
pero la zona afectada fue menor: menos stale handles, recuperación más rápida y menos procesos “fantasma” manteniendo viejos FDs de directorio.
El runbook funcionó porque no dependía de la suerte ni del conocimiento tribal.
La lección: la fiabilidad no es una característica secreta que activas. Es una colección de restricciones que acuerdas no violar, incluso cuando tienes prisa.
Cómo evitarlo: patrones de prevención que funcionan
1) Mantén los exports estables y aburridos
La palanca más grande: no cambies el mapeo de identidad detrás de una ruta export de forma casual.
Si debes migrar datos, hazlo de forma que preserves la identidad de objeto (a menudo imposible), o acepta que los clientes deben refrescarse.
- Evita intercambiar filesystems bajo la misma ruta export sin un plan de mantenimiento para clientes.
- Evita exportar un directorio padre que contenga mountpoints que puedan remontarse independientemente.
- Prefiere exportar la raíz del filesystem de respaldo (o un límite estable de dataset), no un directorio aleatorio.
2) Usa NFSv4 con una estrategia de namespace consistente
Para NFSv4, trata el namespace del servidor como una API. Cambiar la estructura del pseudo-root o mover exports rompe clientes de formas que se sienten no locales.
Mantén el namespace estable. Si necesitas un layout nuevo, crea un export nuevo y migra clientes deliberadamente.
3) Fija fsid donde corresponda (servidor)
En servidores NFS Linux, valores explícitos fsid= en /etc/exports pueden ayudar a mantener la identidad del export consistente a través de reboots y eventos HA
(según la arquitectura). No es un hechizo mágico, pero evita renumeraciones accidentales.
4) No «montes soft» para salir del paso
Para fiabilidad, los montajes hard suelen ser el valor por defecto porque preservan expectativas POSIX: las escrituras no fallan aleatoriamente a mitad.
Si necesitas fallos acotados, impléméntalos en la aplicación (timeouts, reintentos, staging local), no dejando que el sistema de archivos mienta.
5) Planifica el refresco de clientes: reinicia servicios que mantengan referencias stale
Incluso después de remount, los daemons de larga duración pueden mantener descriptores de archivo apuntando a objetos stale. Identifícalos y reinícialos como parte de la recuperación.
Esto es especialmente cierto para: CI runners, language servers, servidores de aplicaciones que leen plantillas/config, y cualquier cosa que monitorice directorios.
6) Trata las operaciones del ciclo de vida del almacenamiento como cambios rompientes
Rollback de snapshot, restauración de filesystem, corte de replicación y “reconstruir el share” no son transparentes.
Si tu proceso de almacenamiento cambia la identidad de objeto, necesita:
- un anuncio (quién se verá afectado),
- un plan de recuperación (remount/restart),
- un paso de validación (probar rutas desde un cliente canario).
7) Si ejecutas NFS bajo Kubernetes, adopta el reciclado
Cuando la identidad del servidor cambia, los pods no se curarán mágicamente. Tu mejor defensa es un patrón operacional:
rota despliegues (o incluso nodos) de forma controlada tras eventos de almacenamiento. Suena rudo. También es efectivo.
8) Observabilidad: registra los “eventos de identidad”, no solo la latencia
Todos grafican IOPS y throughput. Menos equipos grafican: recargas de export, cambios de UUID de filesystem, eventos de failover, rollbacks de snapshot.
Esos son los eventos que correlacionan con stale handles. Ponlos en la línea de tiempo del incidente.
Listas de verificación / plan paso a paso
Checklist A: Durante un incidente (lado cliente)
- Confirma que el kernel lo ve: revisa
dmesgpor mensajes de stale handle. - Identifica el montaje: usa
findmntynfsstat -m. - Determina el alcance: ¿un subtree o todo el montaje?
- Encuentra holders:
lsof +D(cuidado en árboles grandes) ofuser -vm. - Detén las cargas que mantienen FDs stale (o drena el nodo).
- Desmonta limpiamente; si está ocupado y debes proceder, usa
umount -ly luego reinicia los servicios ofensores. - Remonta y verifica con
staty una pequeña prueba de lectura/escritura adecuada al share. - Si se repite rápidamente, para: probablemente sea un problema de churn de identidad del servidor/export.
Checklist B: Durante un incidente (lado servidor)
- Revisa reinicios de servicio/recargas de export en
journalctl. - Verifica exports efectivos con
exportfs -v. - Confirma qué respalda el export:
findmnt -Ten la ruta exportada. - Confirma que no hay pistas recientes de recreación de filesystem (cambios de UUID, nuevos datasets, logs de remount).
- Si hay HA: confirma si ocurrió un failover y si el nodo nuevo sirve una identidad de backend idéntica.
- Comunica claramente: “Los clientes deben remount y reiniciar servicios” es una instrucción operacional válida cuando cambió la identidad.
Checklist C: Despliegue de prevención (plan de cambio)
- Inventaria exports y sus filesystems de respaldo. Documenta cuáles son límites estables.
- Decide estrategia de namespace (especialmente para NFSv4): layout de pseudo-root estable, evita mover exports.
- Fija valores fsid donde sea necesario; mantenlos estables entre nodos en diseños HA.
- Define procedimiento de recuperación de cliente: remount + reinicio de servicios específicos.
- Añade canarios: uno o dos clientes que ejecuten periódicamente
stat/findy alerten en ESTALE. - Realiza un día de pruebas de failover o cambio de export en horario laboral, con responsables presentes.
- Escribe el runbook y conviértelo en la opción por defecto, no en folklore.
FAQ
1) ¿Es “Stale file handle” un bug de Ubuntu 24.04?
Normalmente no. Es el cliente NFS de Linux reportando fielmente que el servidor invalidó una identidad de objeto. Ubuntu 24.04 solo ejecuta kernels modernos
y cargas modernas que facilitan disparar churn de identidad y lo hacen más difícil de ignorar.
2) ¿Por qué remontar lo arregla?
Remontar fuerza al cliente a descartar handles cacheados y a resolver rutas de nuevo. Si el servidor ahora es consistente y estable, los nuevos handles funcionarán.
Si el servidor sigue cambiando identidad, el problema volverá.
3) ¿Pueden las opciones de attribute caching evitar stale handles?
No realmente. El attribute caching afecta la rapidez con que se notan cambios de metadata (mtime/size/permissions). Los stale handles ocurren cuando el servidor
no puede mapear el handle en absoluto. Puedes cambiar el caching y aún así obtener ESTALE si las identidades cambian.
4) ¿NFSv4 elimina los stale handles?
No. NFSv4 mejora muchas cosas (sesiones, modelo de bloqueo, namespace), pero no puede mantener un handle válido si la identidad del backend del servidor cambia.
5) ¿Esto lo causan problemas de red?
La pérdida de paquetes y la congestión pueden hacer que NFS se sienta roto, pero normalmente no producen ESTALE directamente. Pueden, sin embargo, aumentar el churn de montaje,
timeouts, reintentos y acciones de “recuperación” que coincidan con cambios de identidad. Diagnostica ambos, pero no los confundas.
6) ¿Cuál es el conjunto de opciones de montaje más seguro para producción?
No hay un conjunto universal, pero una base sensata para montajes críticos es: NFSv4.1+, hard, TCP, timeo razonable,
y evita ajustes exóticos hasta tener una razón medida. La fiabilidad vence a la sofisticación.
7) ¿Por qué solo fallan procesos de larga duración mientras que shells nuevos funcionan?
Los procesos de larga duración mantienen descriptores de archivo abiertos o un current working directory apuntando a objetos que se invalidaron.
Los shells nuevos resuelven rutas de forma fresca y obtienen handles nuevos, por eso parecen “bien.” Esa división es una firma clásica de stale handle.
8) ¿Cómo manejamos stale handles en Kubernetes?
Asume que tendrás que reciclar algo: reinicia pods que tocaron el share, posiblemente drena/reinicia nodos en casos extremos.
Más importante: evita churn de identidad manteniendo exports/backends estables y trata los cutovers de almacenamiento como operaciones coordinadas.
9) ¿Podemos “limpiar” stale handles sin desmontar?
A veces puedes trabajar alrededor haciendo cd fuera del directorio stale, reabriendo archivos o reiniciando el servicio.
Pero si las referencias stale están muy distribuidas, el unmount/remount es el reinicio limpio.
10) ¿Qué debemos decir a los equipos de aplicaciones cuando esto ocurre?
Diles la verdad en términos operacionales: “La identidad del servidor NFS cambió; tu proceso mantiene referencias stale.
Vamos a remountear y reiniciar tu servicio para reabrir rutas.” No lo vendas como “aleatorio flake de NFS.”
Conclusión: siguientes pasos prácticos
“Stale file handle” es lo que ocurre cuando tratas NFS como una carpeta mágica en lugar de un sistema distribuido con identidad.
La solución rara vez es exótica. Suele ser disciplina: exports estables, backends estables y refresco coordinado de clientes cuando cambian identidades.
- Ahora mismo: usa el playbook de diagnóstico rápido, identifica el montaje y el subtree, y remonta limpiamente tras parar a los que mantienen FDs.
- Esta semana: audita la topología de exports y deja de exportar rutas que crucen mountpoints volátiles. Fija identidades estables donde corresponda.
- Este trimestre: convierte migraciones, rollbacks y failovers en runbooks con validación canaria y pasos explícitos de reinicio/remount de clientes.
No tienes que amar NFS. Tienes que operarlo como infraestructura real. Porque lo es.