Problema

En muchos homelabs el storage de medios o datos críticos se separa del hipervisor para aprovechar ZFS, replicación o simplemente por conveniencia. La arquitectura típica es: un servidor NAS (TrueNAS, FreeNAS, etc.) que exporta un share NFS, y un nodo Proxmox que monta ese share y lo inyecta dentro de contenedores LXC o máquinas virtuales Docker. El inconveniente surge al reiniciar o tras un corte de energía: Proxmox arranca sus máquinas en orden alfabético o según prioridad, sin comprobar si el NAS ya está online. Si un LXC que depende del NFS se inicia antes de que el share esté disponible, el contenedor verá directorios vacíos. Aplicaciones como Jellyfin, Plex o bases de datos pueden interpretar esa ausencia como borrado y comenzar a limpiar sus índices, lo que genera escaneos innecesarios y, en casos extremos, pérdida de metadatos. El reto es garantizar que los contenedores esperen a que el NFS esté montado sin forzar a que todo el resto del entorno dependa del NAS.

Causa

  1. Orden de arranque predeterminado – Proxmox no tiene una noción de dependencias entre contenedores y recursos externos. Los LXC se inician tan pronto como el host termina su propio proceso de boot, lo que puede ser antes de que la red LAN o el NAS estén operativos.
  2. Montaje NFS en /etc/fstab sin opciones de retry – Un entry típico (<nas_ip>:/media /mnt/media nfs ro 0 0) falla rápidamente si el servidor no responde, dejando el punto de montaje sin contenido.
  3. Falta de supervisión de disponibilidad – Ni systemd ni los scripts de arranque de LXC verifican la presencia de archivos o la conectividad al NAS antes de lanzar los servicios internos.
  4. Hardlinks y permisos – Cuando la capa de almacenamiento depende de hardlinks (por ejemplo, qBittorrent → Radarr → NFS), una ausencia temporal del share rompe la cadena de enlaces y los contenedores pueden intentar recrear estructuras vacías.

Estos factores combinados hacen que el problema sea recurrente en setups donde el storage está desacoplado del hipervisor.

Solución

La solución se basa en tres pilares:

  1. Montaje resiliente del NFS – Configura el cliente NFS para que intente reconectar indefinidamente y para que el montaje sea “lazy”.
  2. Unit de systemd que controle la disponibilidad del share – Crea un servicio que verifique la conectividad y el contenido esperado antes de marcar el recurso como listo.
  3. Dependencia explícita de los contenedores LXC – Usa la opción lxc.cgroup2.devices.allow o, más sencillo, la directiva after/requires de systemd para que el contenedor solo arranque cuando la unidad NFS esté activa.

Paso a paso

1. Montaje NFS con reconexión automática

Edita /etc/fstab añadiendo opciones bg,soft,intr,retry=5,timeo=14. Un ejemplo:

<NAS_IP>:/media /mnt/media nfs ro,bg,soft,intr,retry=5,timeo=14 0 0
  • bg envía el intento a segundo plano si el servidor no responde.
  • soft permite que las operaciones fallen después de los reintentos, evitando que el kernel bloquee el arranque.
  • retry=5 y timeo=14 ajustan la cantidad y el intervalo de reintentos.

2. Unit de systemd para validar el share

Crea /etc/systemd/system/nfs-media.mount (si no usas fstab) o un servicio de verificación independiente:

# /etc/systemd/system/nfs-media-wait.service
[Unit]
Description=Esperar a que el share NFS de medios esté disponible
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/mnt/media

[Service]
Type=oneshot
ExecStart=/usr/local/bin/wait-nfs.sh /mnt/media 30
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

El script wait-nfs.sh verifica que el punto de montaje exista y que contenga al menos un archivo esperado (por ejemplo, movies/):

#!/usr/bin/env bash
MOUNT=$1
TIMEOUT=$2
ELAPSED=0
while [[ $ELAPSED -lt $TIMEOUT ]]; do
    if mountpoint -q "$MOUNT" && [[ -d "$MOUNT/movies" ]]; then
        exit 0
    fi
    sleep 2
    ((ELAPSED+=2))
done
echo "NFS share no disponible después de $TIMEOUT segundos" >&2
exit 1

Hazlo ejecutable (chmod +x /usr/local/bin/wait-nfs.sh) y habilita el servicio:

systemctl enable --now nfs-media-wait.service

3. Vincular LXC al servicio NFS

En la configuración del contenedor (/etc/pve/lxc/ID.conf) añade:

lxc.start-order: 100
lxc.start-delay: 10
lxc.hook.start: /usr/local/bin/lxc-nfs-wait.sh

El hook lxc-nfs-wait.sh simplemente llama a systemctl start nfs-media-wait.service y espera a que termine con éxito antes de devolver control a Proxmox. Alternativamente, puedes usar la opción systemd dentro del contenedor para declarar:

[Unit]
After=nfs-media-wait.service
Requires=nfs-media-wait.service

Si el contenedor está basado en un template con systemd, crea /etc/systemd/system/jellyfin.service.d/override.conf con esas directivas.

Con este enfoque, el LXC no iniciará hasta que el share NFS esté montado y contenga la estructura esperada, pero los demás VMs y contenedores que no dependan de NFS seguirán su arranque normal.

Cuándo aplicar esta solución

  • Síntomas: Al reiniciar, el contenedor muestra directorios vacíos, logs de “media not found”, o la aplicación elimina entradas del índice.
  • Entorno: Proxmox con LXC o VMs que consumen datos vía NFS/SMB desde un NAS independiente.
  • Escenarios válidos: Homelabs, laboratorios de pruebas y pequeñas infraestructuras donde el NAS puede tardar en arrancar (RAID resync, ZFS pool import).

No aplicar si el share está en el mismo host o si la aplicación tolera la ausencia del directorio (por ejemplo, contenedores que crean su propio storage local). En esos casos, la complejidad adicional no aporta valor.

Código

# /etc/fstab entry (ejemplo)
192.168.1.10:/media /mnt/media nfs ro,bg,soft,intr,retry=5,timeo=14 0 0

# /usr/local/bin/wait-nfs.sh
#!/usr/bin/env bash
MOUNT=$1
TIMEOUT=${2:-30}
ELAPSED=0
while [[ $ELAPSED -lt $TIMEOUT ]]; do
    if mountpoint -q "$MOUNT" && [[ -d "$MOUNT/movies" ]]; then
        exit 0
    fi
    sleep 2
    ((ELAPSED+=2))
done
echo "NFS share no disponible después de $TIMEOUT segundos" >&2
exit 1

# systemd unit (nfs-media-wait.service)
[Unit]
Description=Esperar a que el share NFS de medios esté disponible
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/mnt/media

[Service]
Type=oneshot
ExecStart=/usr/local/bin/wait-nfs.sh /mnt/media 30
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Verificación

  1. Reinicia el nodo Proxmox sin encender el NAS.
  2. Ejecuta systemctl status nfs-media-wait.service. Debería estar en failed o inactive mientras el share falta.
  3. Enciende el NAS y verifica que el servicio cambie a active (exited) en menos de 30 s.
  4. Comprueba que el contenedor LXC haya arrancado (pct status <ID>). Los logs de la aplicación (Jellyfin, Plex, etc.) deben mostrar los archivos de media presentes.
  5. Simula un corte del NAS mientras el contenedor está en ejecución; el servicio NFS se marcará como failed pero el contenedor continuará operando con los datos ya montados, evitando una nueva inicialización.

Notas adicionales

  • Hardlink preservation: Mantén la opción no_root_squash en el export de TrueNAS para que los UID/GID coincidan entre host y contenedor; de lo contrario, los hardlinks pueden romperse al montar como read‑only.
  • Timeout ajustable: En entornos con discos ZFS que tardan más de 30 s en importarse, incrementa el parámetro del script (wait-nfs.sh) a 60 s o más.
  • Monitorización: Añade una alerta de Prometheus o Zabbix que vigile systemd unit nfs-media-wait.service para detectar fallos recurrentes.
  • Alternativa con autofs: Si prefieres montar bajo demanda, autofs puede crear el punto de montaje la primera vez que el contenedor accede, eliminando la necesidad de un servicio de espera, aunque perderás la garantía de que el share está listo antes del arranque del contenedor.

Con estas prácticas, los contenedores LXC en Proxmox pueden coexistir de forma segura con storage externo NFS, reduciendo la ventana de error tras reinicios y manteniendo la integridad de los índices de medios.