Problema

Muchos entusiastas de homelab quieren reutilizar hardware antiguo para crear un NAS doméstico que ofrezca redundancia, snapshots y acceso remoto sin exponer servicios críticos a Internet. El desafío consiste en combinar un hipervisor ligero (Proxmox), un sistema de archivos robusto (ZFS) y un servidor de archivos (Samba) mientras se mantiene una estrategia de backup fuera del pool y se protege el acceso remoto.

Causa

Los fallos habituales en estos setups provienen de:

  • Diseño de pool inadecuado – RAIDZ1 con tres SSD puede ser suficiente para tolerar una falla, pero la falta de espacio libre y la ausencia de un segundo nivel de protección (backup externo) generan vulnerabilidades.
  • Servicios mezclados en el host – Ejecutar Samba directamente en el nodo Proxmox complica la gestión de permisos y la actualización independiente.
  • Acceso remoto sin túnel – Abrir puertos SMB al exterior expone el NAS a ataques de fuerza bruta y ransomware.
  • Discos reutilizados sin pruebas exhaustivas – SSD de laptops pueden ocultar bloques defectuosos o haber alcanzado su límite de escritura.
  • Memoria insuficiente – ZFS consume RAM para ARC; 32 GB suele ser suficiente, pero la configuración de datasets y cachés influye.

Solución

Una arquitectura modular que separa responsabilidades y facilita la recuperación:

  1. Instalar Proxmox VE en el SSD de arranque (256 GB). Mantener el hipervisor limpio.
  2. Crear el pool ZFS en el host con los tres SSD de 1 TB usando raidz1. Reservar al menos 20 % de espacio libre para operaciones de scrubbing y snapshots.
  3. Desplegar una VM ligera (Debian) o un LXC que ejecute Samba. El contenedor accede al pool mediante un zfs dataset exportado como zvol o mediante montaje directo (/etc/pve/lxc/<id>.conf con mp0).
  4. Configurar snapshots automáticos (cron o zfs-auto-snapshot) y replicar mensualmente a un HDD externo mediante zfs send/receive o rsync según preferencia.
  5. Aislar el acceso remoto con Tailscale instalado en el host Proxmox (o en una pequeña VM de firewall). Utilizar ACLs para limitar la red de SMB a los dispositivos autorizados.
  6. Implementar monitoreo: smartctl, zpool status, zfs scrub, y notificaciones vía email o Telegram. Integrar el UPS con NUT para apagados seguros.
  7. Procedimientos de recuperación documentados: reemplazo de disco, recreación del pool, restauración desde HDD y reinstalación de Proxmox.

Paso a paso

1. Preparación del hardware

  • Ejecutar memtest86+ desde un USB para validar la RAM.
  • Realizar SMART short y extended tests en cada SSD:
smartctl -a /dev/nvme0n1   # revisar atributos
smartctl -t short /dev/nvme0n1
smartctl -t long  /dev/nvme0n1
  • Opcional: badblocks -sv /dev/nvme0n1 para detectar sectores defectuosos (solo lectura).

Sanity check: Todos los atributos Critical Warning deben ser 0 y los contadores de errores cercanos a 0. Si aparece alguna alerta, descartar el disco.

2. Instalación de Proxmox VE

  • Boot desde ISO, seleccionar el SSD de 256 GB como destino.
  • Desactivar swap durante la instalación (ZFS gestiona la memoria).
  • Después del primer arranque, actualizar paquetes:
apt update && apt full-upgrade -y
reboot

Sanity check: Acceder a la GUI en https://<ip>:8006. pveversion -v debe mostrar la versión actual.

3. Creación del pool ZFS

zpool create -f -o ashift=12 \
  tank raidz1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1
zfs set compression=lz4 tank
zfs set atime=off tank
  • ashift=12 alinea a 4 KB, recomendado para SSD.
  • Verificar espacio y salud:
zpool status -v
zfs get all tank | grep compression

Sanity check: zpool status debe mostrar ONLINE para los tres discos y scan: none requested. Si aparece DEGRADED, abortar y revisar cables/BIOS.

4. Datasets y exportación a Samba

zfs create -o mountpoint=/mnt/tank/data tank/data
zfs create -o mountpoint=/mnt/tank/backups tank/backups
  • Para LXC: editar /etc/pve/lxc/101.conf:
mp0: tank/data,mp=/srv/samba,backup=0
  • Para VM Debian: montar el dataset en /etc/fstab dentro de la VM.

5. Instalación y configuración de Samba (en LXC)

apt install samba -y
cat > /etc/samba/smb.conf <<'EOF'
[backup]
   path = /srv/samba
   read only = no
   guest ok = no
   valid users = @smbusers
EOF
groupadd smbusers
useradd -M -s /usr/sbin/nologin -G smbusers alice
smbpasswd -a alice
systemctl enable --now smbd

Sanity check: Desde otro host en la LAN, ejecutar smbclient -L //host -U alice. La lista debe incluir el share backup.

6. Snapshots automáticos

Instalar zfs-auto-snapshot:

apt install zfs-auto-snapshot -y
systemctl enable --now zfs-auto-snapshot.timer

Esto crea snapshots cada hora, diario y semanal. Ajustar retención editando /etc/cron.d/zfs-auto-snapshot.

7. Replicación mensual al HDD

  • Crear pool secundario (solo cuando el HDD está conectado):
zpool create backup /dev/sda
zfs create backup/replica
  • Replicar el dataset más reciente:
latest=$(zfs list -t snapshot -o name -s creation -r tank/data | tail -1)
zfs send -R $latest | zfs receive -F backup/replica
  • Programar con cron (primer día de mes a las 02:00):
0 2 1 * * root /usr/local/bin/monthly_backup.sh

monthly_backup.sh contiene los comandos anteriores y registra salida en /var/log/backup.log.

Sanity check: Verificar que el snapshot llegó al pool backup con zfs list -t snapshot -r backup/replica. Comparar tamaños con zfs get used,available backup/replica.

8. Acceso remoto con Tailscale

Instalar en el host Proxmox (más sencillo para enrutar el tráfico del contenedor):

curl -fsSL https://tailscale.com/install.sh | sh
tailscale up --authkey <YOUR_KEY> --advertise-routes=10.0.0.0/24
  • En la ACL de Tailscale, permitir solo los usuarios que necesiten SMB y bloquear el rango de gestión de Proxmox.
  • En el firewall de Proxmox (/etc/pve/firewall/cluster.fw), denegar tráfico entrante a 8006 desde la subred Tailscale y permitir solo 445 desde la IP asignada por Tailscale.

Sanity check: Desde un cliente con Tailscale, ping 10.0.0.X (IP del host) y montar el share con mount -t cifs //10.0.0.X/backup /mnt/remote -o user=alice. Si el montaje falla, revisar reglas ACL y firewall.

9. Monitoreo y alertas

  • SMART: smartd con /etc/smartd.conf para enviar correo al detectar errores.
  • ZFS scrubs: programar semanalmente:
echo "0 3 * * 0 root zpool scrub tank" >> /etc/crontab
  • Notificaciones de zpool status mediante script que envía mensaje a Telegram:
#!/bin/bash
if ! zpool status -x tank >/dev/null; then
  curl -s -X POST https://api.telegram.org/bot$BOT_TOKEN/sendMessage \
    -d chat_id=$CHAT_ID -d text="⚠️ ZFS pool tank degraded"
fi
  • UPS: instalar nut y habilitar upsmon para apagar Proxmox ordenadamente.

10. Recuperación

  • Fallo de disco: Reemplazar, ejecutar zpool replace tank <old> <new> y esperar al resilver.
  • Fallo del host: Arrancar desde USB con la misma versión de Proxmox, importar el pool (zpool import tank) y reinstalar la VM LXC.
  • Pérdida total del pool: Conectar el HDD externo, recibir el último snapshot con zfs receive -F backup/replica y recrear el pool en nuevo hardware.

Sanity check: Después de cada recuperación, ejecutar zpool status y montar los datasets para validar integridad.

Cuándo aplicar esta solución

  • Necesitas un NAS doméstico con redundancia y snapshots pero sin requerimientos de alta disponibilidad.
  • El tráfico está limitado a 1 GbE, por lo que la prioridad es fiabilidad, no rendimiento máximo.
  • Dispones de al menos 16 GB de RAM (32 GB recomendado) y un UPS.
  • Quieres acceso remoto sin abrir puertos SMB al exterior.

No es adecuada si:

  • Requieres rendimiento de NVMe a plena velocidad (bifurcación PCIe y RAIDZ1 pueden limitar IOPS).
  • Necesitas alta disponibilidad multi‑nodo; entonces considera Ceph o una solución de clustering.

Código

# 1. SMART tests
smartctl -t long /dev/nvme0n1
smartctl -a /dev/nvme0n1 | grep -i "critical"

# 2. ZFS pool
zpool create -f -o ashift=12 tank raidz1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1
zfs set compression=lz4 tank

# 3. Datasets
zfs create -o mountpoint=/mnt/tank/data tank/data
zfs create -o mountpoint=/mnt/tank/backups tank/backups

# 4. Samba share (inside LXC)
apt install samba -y
cat > /etc/samba/smb.conf <<'EOF'
[backup]
   path = /srv/samba
   read only =