Problema

En entornos homelab con Proxmox, es frecuente combinar varios tipos de discos (NVMe, HDD, SSD) y usar ZFS como capa de datos primaria. El reto aparece cuando se necesita exponer esos datos a usuarios finales mediante SMB o NFS, y se delega la gestión de usuarios y permisos a una instancia de OpenMediaVault (OMV). La arquitectura típica —ZFS en el hipervisor, OMV en una VM o LXC, y discos de respaldo en RAID‑1— genera conflictos de permisos, latencia en escritura y complejidad en la planificación de backups. El síntoma más común es que los clientes Windows o Linux ven archivos con UID/GID inesperados o que los snapshots de ZFS no se sincronizan correctamente con los procesos de rsync programados.

Causa

  1. Desalineación de UID/GID entre Proxmox y OMV
    ZFS almacena propietarios como números UID/GID. Si la VM/contener de OMV tiene un rango de usuarios distinto al del host, los archivos aparecen con propietarios “nobody” o con IDs que no existen en el cliente.

  2. Exportación directa de datasets ZFS sin control de permisos
    Compartir un dataset ZFS mediante zfs set sharenfs=on o zfs set sharesmb=on ignora la capa de control de acceso de OMV, provocando que los permisos de Samba/NFS se resuelvan únicamente a nivel de ZFS.

  3. Uso de LVM para VMs y datasets ZFS en paralelo
    Cuando la unidad NVMe está particionada con LVM para VMs y, al mismo tiempo, se crea un pool ZFS sobre la misma unidad, el hipervisor necesita gestionar dos sistemas de bloques diferentes. Un error de alineación de bloques o de caché puede degradar el rendimiento y causar corrupción en snapshots.

  4. Backups basados en rsync sin snapshots consistentes
    Ejecutar rsync directamente sobre un dataset activo puede copiar archivos en estado intermedio. Sin snapshots de ZFS, los backups pueden quedar inconsistentes, y los cambios de permisos entre ejecuciones pueden generar “Permission denied”.

  5. OMV en VM vs LXC
    En una VM, OMV maneja su propio kernel y controladores, lo que añade una capa de virtualización extra. En LXC, el contenedor comparte el kernel del host, lo que facilita el ID mapping pero requiere configuraciones específicas (lxc.idmap) y permisos de montaje (privileged vs unprivileged).

Solución

1. Consolidar ZFS como única capa de datos y exponerla a OMV mediante bind‑mounts

Crear datasets ZFS para cada tipo de dato (raw, backups, multimedia) y montar esos datasets dentro del contenedor/VM de OMV usando bind‑mounts. De esta forma OMV gestiona únicamente la capa de compartición (Samba/NFS) mientras que los permisos siguen siendo los del host.

# Crear pool ZFS en el NVMe (asumiendo /dev/nvme0n1)
zpool create -f -o ashift=12 tank /dev/nvme0n1

# Datasets
zfs create -o compression=lz4 tank/data
zfs create -o compression=lz4 tank/backups

# Montaje en el host (opcional, Proxmox lo hace automáticamente)
mkdir -p /tank/data /tank/backups
mount -t zfs tank/data /tank/data
mount -t zfs tank/backups /tank/backups

2. Deploy OMV como LXC con ID mapping

Usar un contenedor LXC no privilegiado permite mapear los UID/GID del host a un rango interno del contenedor (100000-165535). Configurar /etc/pve/lxc/ID.conf y el archivo de la plantilla LXC:

# En el host
cat >> /etc/subuid <<EOF
root:100000:65536
EOF
cat >> /etc/subgid <<EOF
root:100000:65536
EOF

# Crear contenedor
pct create 101 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.gz \
  -cores 2 -memory 2048 -net0 name=eth0,bridge=vmbr0,ip=dhcp \
  -features nesting=1,keyctl=1 -unprivileged 1

# Añadir bind‑mounts
pct set 101 -mp0 /tank/data,mp=/srv/omv/data
pct set 101 -mp1 /tank/backups,mp=/srv/omv/backups

Dentro del contenedor, instalar OMV siguiendo la guía oficial y configurar los “Shared Folders” apuntando a /srv/omv/data. OMV ahora usa los UID/GID del host (mapeados) y los permisos se conservan.

3. Configurar Samba y NFS en OMV con “force user/group”

Para evitar que los clientes vean UID/GID diferentes, habilitar la opción Force user y Force group en los shares de OMV. Seleccionar un usuario del host (por ejemplo proxmox) y asignarlo a todos los shares. Así, cualquier acceso SMB/NFS se traduce al mismo UID/GID en ZFS.

4. Automatizar backups con snapshots y rsync

Utilizar snapshots de ZFS como punto de consistencia y rsync para copiar a los discos HDD en RAID‑1. Un cron simple:

#!/bin/bash
# snapshot
SNAP=$(date +%Y-%m-%d_%H-%M)
zfs snapshot tank/data@${SNAP}

# rsync al pool de backup (montado en /mnt/backup)
rsync -aHAX --delete /tank/data/ /mnt/backup/${SNAP}/

# opcional: eliminar snapshots antiguos (>30d)
zfs destroy -r tank/data@$(date -d "-30 days" +%Y-%m-%d_%H-%M)

Programar con crontab -e:

0 2 * * * /root/zfs_backup.sh >> /var/log/zfs_backup.log 2>&1

5. Monitorear integridad y permisos

Instalar zfs-auto-snapshot o zrepl para crear snapshots horarios y diurnos. Además, habilitar zfs get all tank/data | grep aclmode y asegurarse de que aclmode=passthrough para que los ACL de ZFS no interfieran con los permisos de Samba.

Cuándo aplicar esta solución

  • Entornos homelab o pequeñas empresas donde se combina NVMe rápido para datos activos y HDD para backups.
  • Necesidad de compartir datos vía SMB/NFS a usuarios Windows/Linux sin que los permisos se “pierdan” en la capa de virtualización.
  • Preferencia por ZFS como única capa de datos y deseo de evitar la duplicación de sistemas de archivos (por ejemplo, LVM + ZFS).
  • Se busca una solución reproducible que funcione tanto con OMV en VM como en LXC, pero con menor sobrecarga de virtualización.

No es adecuada cuando:

  • Se requiere alta disponibilidad con clústeres Proxmox y failover de VM/CT; en ese caso, la arquitectura de discos debe ser compartida (Ceph, ZFS over iSCSI, etc.).
  • Los usuarios finales necesitan acceso directo a discos físicos sin pasar por Samba/NFS (por ejemplo, bases de datos que requieren latencia mínima).

Código

# Creación de pool y datasets
zpool create -f -o ashift=12 tank /dev/nvme0n1
zfs create -o compression=lz4 tank/data
zfs create -o compression=lz4 tank/backups

# Bind‑mounts en LXC
pct set 101 -mp0 /tank/data,mp=/srv/omv/data
pct set 101 -mp1 /tank/backups,mp=/srv/omv/backups

# Script de backup con snapshot y rsync
#!/bin/bash
SNAP=$(date +%Y-%m-%d_%H-%M)
zfs snapshot tank/data@${SNAP}
rsync -aHAX --delete /tank/data/ /mnt/backup/${SNAP}/
zfs destroy -r tank/data@$(date -d "-30 days" +%Y-%m-%d_%H-%M)

Verificación

  1. Montaje y permisos

    • En el host: ls -l /tank/data debe mostrar UID/GID que coinciden con los usuarios de OMV (proxmox o el UID mapeado).
    • Desde una máquina cliente Windows: crear un archivo en el share SMB y comprobar que el propietario es el mismo UID/GID en el host (stat /tank/data/archivo).
  2. Snapshot y backup

    • Ejecutar manualmente el script y validar que /mnt/backup/<fecha>/ contiene la misma estructura que /tank/data.
    • Verificar que zfs list -t snapshot | grep tank/data@ muestra el snapshot creado.
  3. Rendimiento

    • Medir IOPS/latencia con fio en /tank/data y comparar con los valores esperados de NVMe.
    • Asegurarse de que la carga de Samba/NFS no degrada el rendimiento por más del 10 % en pruebas de transferencia de archivos grandes.

Notas adicionales

  • ID mapping: si decides mantener OMV en una VM, habilita idmapd en el host y en la VM y usa el mismo dominio NFS (Domain = localdomain).
  • ACL vs POSIX: ZFS permite ACLs de nivel de archivo; si OMV usa Samba con ACL, configura zfs set acltype=posixacl tank/data y habilita aclmode=passthrough.
  • Espacio de snapshots: los snapshots consumen espacio incremental. Monitorea con zfs list -o name,used,available,refer,compressratio y programa destrucción automática.
  • Expansión de almacenamiento: si añades un expansor PCIe x16, crea un nuevo pool (por ejemplo tank2) y replica la misma estructura de datasets; luego usa zfs send/receive para migrar datos sin downtime.
  • Seguridad: restringe el acceso a la interfaz web de OMV a redes internas y usa certificados auto‑firmados para HTTPS.

Con esta arquitectura, ZFS sigue siendo la capa de datos confiable, OMV gestiona usuarios y protocolos de compartición, y los backups automáticos se ejecutan de forma consistente y verificable.