Plan Architecture — Migration NVMe WD BLACK SN770¶
Date : 2026-03-31
Contexte : Remplacement sdb défaillant + réorganisation complète stockage
Matériel disponible : WD BLACK SN770 NVMe PCIe 4.0 (boîtier USB-C M.2 disponible)
Méthode : Analyse multi-agents (ZFS vs LVM), débat confronté, synthèse critique
1. DÉCOUVERTE MATÉRIELLE CLɶ
Slot M.2 GA-H170-HD3 : NVMe PCIe Gen3 x4 natif¶
La documentation existante décrivait ce slot comme "SATA uniquement" — c'est incorrect.
Source officielle Gigabyte :
"PCIe Gen3 x4 M.2 Connector with up to 32Gb/s Data Transfer (PCIe NVMe & SATA SSD support)"
| Mode M.2 | Débit max | Ports SATA 6Gb/s | Impact |
|---|---|---|---|
| SATA (Kingston actuel) | ~600 MB/s | 5 disponibles (1 désactivé) | Current |
| PCIe Gen3 x4 NVMe | ~3 400 MB/s | 6 TOUS actifs | Cible SN770 |
Conséquences architecturales : - Le WD SN770 (PCIe 4.0, rétrocompatible Gen3) s'installe directement dans le slot M.2 — aucun adaptateur PCIe nécessaire - Tous les disques SATA actuels (sdb, sdc, sdd, sde) restent branchés pendant toute la migration - Le Kingston 128GB (M.2 SATA) doit libérer le slot → retrait + son workload absorbé par le NVMe - Les slots PCIe 3.0 x16 et x4 restent entièrement libres (GPU futur, cartes d'extension)
2. ÉTAT ACTUEL — PROBLÈMES DOCUMENTÉS¶
Sources : rapports incidents 2026-03-23, 2026-03-24, 2026-03-30
2.1 Problème critique — sdb en fin de vie¶
Samsung SSD 850 EVO 250GB (sdb) — 32 486 heures d'utilisation (≈ 11 ans)
Reallocated_Sector_Ct : 3
Uncorrectable_Error_Cnt : 3
ATA Error Count : 3 (dont UNC à LBA 4 202 496 ≈ 2.0 GB)
Checksum SMART invalide : firmware/hardware dégradé
sdb héberge simultanément : - OS Proxmox VE (partition racine et EFI) - VG "pve" → pool thin "data" (151GB) pour tous les rootfs LXC - Les fragments de LXC ayant causé des erreurs ext4 sur dm-6, dm-8, dm-10
→ Ce disque est une bombe à retardement. Priorité de remplacement maximale.
2.2 Problème structural — pool thin LVM à 92,6%¶
Pool thin "data" : 151GB physique, problèmes documentés
LXC Alloué %Utilisé Réel pool FS réel Overhead
100 32GB 47.64% 15.2GB 8.9GB 6.3GB
101 16GB 41.11% 6.6GB 4.4GB 2.2GB
102 100GB 96.18% 96.2GB 22.0GB 74.2GB ⚠️ (74GB de blocs fantômes)
103 16GB 27.30% 4.4GB 2.0GB 2.4GB
104 20GB 87.28% 17.5GB 12.0GB 5.5GB
184GB total 140.0GB 49.3GB 90.7GB
Cascading failure observée le 23/03 : Pool 92.6% → backups PBS échouent → retries automatiques → pool 95%+ → filesystems READ-ONLY → services DOWN
2.3 Problème récursif — PBS dans le pool thin¶
PBS datastore (19GB) réside dans le rootfs LXC 102 qui est lui-même dans le pool thin qu'il protège : - Pool plein → backup échoue → pas de protection → cercle vicieux
2.4 Erreurs ext4 non corrigées¶
dm-10 (LXC 101) : 9 erreurs, state "clean with errors", orphan_present
Last error: 2026-03-23 18:55:08 — ext4_journal_check_start (EIO)
dm-6 (LXC 100) : 3 erreurs (journal_check_start, 23/03)
dm-8 (LXC 103) : 1 erreur (journal_check_start, 23/03)
e2fsck obligatoire avant migration.
2.5 Autres problèmes identifiés¶
- SnapRAID non-fonctionnel : partition sde montée à 286K (corruption de table de partition) au lieu de 931GB
- LXC 101 sans CPU limits : Uptime Kuma peut monopoliser tous les cores → incident 30/03 (saturation SQLite → starvation)
- LXC 102 non inclus dans les backups PBS : configuration PBS non sauvegardée, perte = reconstruction manuelle
3. DÉBAT ARCHITECTURAL — SYNTHÈSE¶
Deux architectures ont été analysées et confrontées en deux rounds.
3.1 Agent A — ZFS sur NVMe (pool unique)¶
Architecture proposée finale :
rpool (NVMe ZFS, datasets)
├── rpool/ROOT/pve-1 ← OS Proxmox
├── rpool/data/ ← LXC rootfs (quotas par container)
│ ├── vm-100-disk-0 quota 35GB
│ ├── vm-101-disk-0 quota 18GB
│ ├── vm-102-disk-0 quota 45GB
│ ├── vm-103-disk-0 quota 18GB
│ ├── vm-104-disk-0 quota 22GB
│ └── vm-105-disk-0 quota 15GB
├── rpool/lxc-data ← Docker configs bind-mounts (quota 60GB)
├── rpool/scratch ← SABnzbd/Jellyfin (quota 100GB)
└── rpool/pbs ← PBS datastore ISOLÉ (quota 450GB)
Arguments clés :
- Quotas dataset = isolation parfaite (un LXC sature → seul lui bloqué)
- Snapshots ZFS = 0 octet à la création (CoW pur, impossible d'échouer par manque d'espace)
- autotrim=on → TRIM natif et automatique vers le NVMe
- compression=lz4 → ratio ~1.5x transparent, réduction I/O et usure NVMe
- Checksum par bloc → détection corruption sdb-style
- sdc libéré → PBS secondary local (rsync nocturne)
3.2 Agent B — LVM thick sur NVMe (partitionné)¶
Architecture proposée finale (après révision) :
/dev/nvme0n1 — VG nvme-vg (~930GB)
├── lv-proxmox-root 32GB ext4 → /
├── lv-lxc-100 40GB ext4 → LXC 100
├── lv-lxc-101 20GB ext4 → LXC 101
├── lv-lxc-102 50GB ext4 → LXC 102
├── lv-lxc-103 20GB ext4 → LXC 103
├── lv-lxc-104 25GB ext4 → LXC 104
├── lv-lxc-105 18GB ext4 → LXC 105
├── lv-pbs-datastore 200GB ext4 → /mnt/pbs (PBS isolé)
├── lv-scratch 80GB ext4 → /mnt/scratch
└── [libre] ~365GB → headroom + snapshots LVM
Arguments clés : - Pas de courbe d'apprentissage ZFS (risque opérationnel pendant migration) - LV thick = quota fixe natif (même isolation, mécanisme différent) - Migration Proxmox LVM = procédure standard et documentée - Concession : thin pool 60GB initial était une erreur de calcul corrigée - Concession : non-ECC n'est pas un veto ZFS absolu - Compromis proposé : Phase 1 LVM → stabilisation → Phase 2 ZFS optionnel
3.3 Points de convergence¶
| Point | Verdict commun |
|---|---|
| SN770 en M.2 direct | ✅ Les deux agents d'accord (aucun adaptateur nécessaire) |
| Kingston retiré | ✅ Workload scratch absorbé par NVMe |
| sdb retiré | ✅ Immédiatement après migration |
| PBS isolé physiquement du pool LXC | ✅ Les deux architectures le font |
| sdc → PBS secondary (rsync nightly) | ✅ Adopté par les deux agents |
| e2fsck avant migration | ✅ Les deux agents d'accord |
| CPU limits LXC 101 | ✅ Les deux agents d'accord |
| LXC 102 dans les backups PBS | ✅ Les deux agents d'accord |
4. ANALYSE CRITIQUE CONSOLIDÉE¶
4.1 Sur le débat non-ECC + ZFS¶
Verdict : le risque non-ECC est surestimé des deux côtés.
L'argument d'Agent B ("ZFS non-ECC = corruption silencieuse via bit flip RAM") est techniquement correct mais sélectivement incomplet. LVM/ext4 non-ECC est exactement dans le même cas côté RAM, et en plus n'a aucun checksum disque pour détecter le bit rot ultérieur.
Le tableau complet :
| Scénario | ZFS non-ECC | LVM/ext4 non-ECC |
|---|---|---|
| Bit flip RAM avant écriture | Bloc corrompu avec checksum "valide" | Bloc corrompu, aucun checksum jamais |
| Corruption disque (bit rot) | Détectée au scrub mensuel | Non détectable |
| Journal corrompu | Replay ZIL → reconstruction | e2fsck, parfois mount impossible |
| Détection proactive | zpool scrub |
e2fsck une fois arrêté |
La politique ECC de TrueNAS est une politique vendor enterprise (CYA). Pour un homelab à 30% de charge sur DDR3L-1600 à 25°C, l'ordre de grandeur réel est ~1 flip/5-10 ans par module, non cyclique. Le risque sdb défaillant est immédiat et documenté. L'arbitrage est sans appel.
Conclusion : non-ECC n'est pas un veto à ZFS sur ce homelab.
4.2 Sur l'ARC ZFS avec 32GB RAM¶
Allouer zfs_arc_max=8GB sur 32GB laisse :
- 11GB alloués LXC (actuel)
- 8GB ARC max
- ~13GB de marge + host
L'ARC ZFS est dynamique : il libère la mémoire aux processus en cas de pression (<1s). Il ne se comporte pas comme une réservation statique. Sur NVMe Gen3 x4, l'ARC apporte moins de gain marginal qu'il n'en apporterait sur un SATA HDD — mais la compression lz4 et les checksums restent précieux même sans cache RAM significatif.
Recommandation : zfs_arc_max=6GB (conservateur), révisable à la hausse après stabilisation.
4.3 Sur les quotas en pratique¶
La vraie question n'est pas "ZFS ou LVM pour l'isolation" mais "quel mode de défaillance préfère-t-on" :
| Scénario : LXC 100 consomme tout l'espace | ZFS datasets | LVM thick |
|---|---|---|
| Mécanisme | Quota dataset → ENOSPC dans LXC 100 uniquement | LV plein → ENOSPC dans LXC 100 uniquement |
| Impact sur LXC 101, 102, 103... | Aucun | Aucun |
| Impact sur PBS datastore | Aucun (dataset séparé avec son quota) | Aucun (LV séparé) |
| Gestion | Automatique | Automatique |
Les deux architectures résolvent le problème d'isolation. La différence est que ZFS le fait avec allocation dynamique (LXC 100 n'utilisant que 10GB sur son quota de 35GB ne "gaspille" pas 25GB), tandis que LVM thick alloue 35GB physiques même si 25GB sont vides. Sur un NVMe 1TB, ce gaspillage est acceptable.
4.4 Sur les snapshots PBS¶
ZFS gagne ici de façon significative.
PBS vzdump utilise un snapshot du rootfs LXC pendant la copie : - LVM thin : snapshot COW nécessite de l'espace libre dans le pool → si pool saturé, snapshot échoue → backup échoue. C'est exactement le mode de défaillance du 23/03. - LVM thick : snapshot séparé pré-alloué résout le problème mais ajoute une complexité de gestion (pré-allouer, nettoyer en hook PBS). - ZFS : snapshot = pointeur CoW, 0 octet à la création. Physiquement impossible d'échouer par manque d'espace au moment de la création.
Ce seul point, vu la criticité des backups PBS documentée, penche structurellement en faveur de ZFS.
4.5 Sur la migration¶
La découverte M.2 NVMe natif change fondamentalement la balance :
Sans adaptateur PCIe, la procédure de migration est :
1. Installer Proxmox sur SN770 via boîtier USB-C (pendant que le serveur tourne encore)
2. Éteindre → extraire Kingston → insérer SN770 dans M.2 (~30 secondes)
3. Modifier ordre de boot BIOS
4. Démarrer sur nouveau Proxmox
5. pct restore × N conteneurs depuis PBS
6. Reconfigurer PBS datastore
La "complexité ZFS" invoquée par Agent B se réduit à connaître quelques commandes de base (zpool create, zfs set, zfs list). PBS handle la restauration de façon identique quel que soit le backend de stockage. Ce risque opérationnel est faible et gérable.
5. VERDICT ET ARCHITECTURE RECOMMANDÉE¶
5.1 Recommandation : ZFS sur NVMe¶
Rationale : Les problèmes documentés (pool thin saturé, snapshots PBS échouant par manque d'espace, overhead 2x, ext4 errors silencieuses) sont des symptômes structurels de LVM thin. Les remplacer par LVM thick sur NVMe corrige certains problèmes (séparation PBS, bon sizing) mais laisse intact le risque de saturation globale et la complexité des snapshots. ZFS adresse tous ces problèmes nativement.
Le compromis progressif d'Agent B (LVM puis ZFS dans 6 mois) est valide, mais reporter la migration ZFS ne fait que reporter les bénéfices sans éliminer de risque supplémentaire immédiat. Le boîtier USB-C rend la préparation à froid possible.
5.2 Architecture finale recommandée¶
Stockage NVMe (WD BLACK SN770, M.2 PCIe Gen3 x4)¶
# ZFS pool sur NVMe entier
zpool create -o ashift=13 rpool /dev/nvme0n1
zpool set autotrim=on rpool
zfs set compression=lz4 rpool
zfs set atime=off rpool
# OS Proxmox (géré par l'installeur)
# rpool/ROOT/pve-1 ~32GB
# LXC rootfs (enregistré comme storage "local-zfs" dans Proxmox)
zfs create rpool/data
zfs set quota=200G rpool/data # plafond global LXC
# Sous-quotas créés automatiquement par Proxmox au moment de la création LXC
# PBS datastore ISOLÉ (monté dans LXC 102 via bind mount)
zfs create rpool/pbs
zfs set quota=450G rpool/pbs # ~3 générations complètes de backups LXC
zfs set recordsize=1M rpool/pbs # chunks PBS = gros fichiers
zfs set atime=off rpool/pbs
# Docker configs (remplace sdc comme source des bind mounts)
zfs create rpool/lxc-data
zfs set quota=80G rpool/lxc-data # actuel < 10GB, marge ×8
# Scratch (SABnzbd incomplete + transcodes Jellyfin)
zfs create rpool/scratch
zfs set quota=120G rpool/scratch
zfs set sync=disabled rpool/scratch # données temporaires, sans besoin de sync
Tableau disques complet¶
| Disque | Modèle | FS | Rôle | Montage |
|---|---|---|---|---|
| nvme0n1 (M.2) | WD BLACK SN770 | ZFS rpool | OS + LXC + PBS + scratch + lxc-data | Voir datasets ci-dessus |
| sdb | Samsung 850 EVO 250GB ⚠️ DÉFAILLANT | — | Retiré après migration. Garde en cold spare (ne pas monter) | — |
| sdc | Samsung 850 EVO 250GB (SMART OK) | ext4 | PBS secondary — rsync nocturne depuis rpool/pbs |
/mnt/pbs-secondary |
| sdd | HGST 4TB | ext4 | Media Jellyfin (inchangé) | /mnt/media |
| sde | Seagate IronWolf 4TB | ext4 | Storage cloud/archives (inchangé) | /mnt/storage |
| sda (Kingston M.2) | Kingston 128GB | — | Retiré — workload absorbé par rpool/scratch |
— |
| Toshiba 1TB (disponible) | Toshiba DT01ACA100 | ext4 | Data drive MergerFS ou PBS cold backup rotatif | /mnt/archive |
Configuration NVMe ZFS ARC¶
# /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=6442450944 # 6GB plafond ARC
options zfs zfs_arc_min=1073741824 # 1GB minimum (évite flush complet)
# Scrub mensuel automatique
echo "0 3 1 * * root zpool scrub rpool" >> /etc/cron.d/zfs-scrub
Répartition mémoire avec 32GB¶
32GB total
├── ARC ZFS : 4-6GB (dynamique, cède aux processus)
├── LXC 100 Media : 6GB
├── LXC 101 Mgmt : 2GB
├── LXC 102 PBS : 2GB
├── LXC 103 Gateway : 1GB
├── LXC 104 Services: 2GB
├── LXC 105 Web : 1GB
├── Host Proxmox : 2GB
└── Marge libre : 10-12GB
6. PLAN DE MIGRATION¶
Phase 0 — Pré-migration (zéro downtime, boîtier USB-C)¶
# Brancher SN770 dans boîtier USB-C sur le serveur en production
lsblk # identifier /dev/sdX
# Forcer backup PBS IMMÉDIAT avant tout changement
vzdump 100 101 102 103 104 105 \
--storage pbs-backups \
--compress zstd \
--mode snapshot
# Vérifier que tous les backups sont valides dans l'UI PBS
# e2fsck LXC concernés (arrêter les LXC d'abord)
pct stop 100 && e2fsck -f /dev/mapper/pve-vm--100--disk--0 && pct start 100
pct stop 101 && e2fsck -f /dev/mapper/pve-vm--101--disk--0 && pct start 101
pct stop 103 && e2fsck -f /dev/mapper/pve-vm--103--disk--0 && pct start 103
Phase 1 — Installation Proxmox sur NVMe (via USB-C, système toujours sur sdb)¶
# Télécharger ISO Proxmox VE 9.1
# Flasher sur clé USB
# Démarrer depuis clé USB en pointant l'installation vers /dev/sdX (NVMe USB)
# Choisir ZFS comme filesystem, pool name "rpool"
# Configurer IP identique au host actuel (pour faciliter le cutover)
# NE PAS démarrer depuis le NVMe encore
Alternative (sans re-install complète) :
# Créer manuellement le zpool ZFS sur le NVMe via USB
zpool create -f rpool /dev/sdX # sdX = NVMe via boîtier USB
# Cloner Proxmox depuis sdb via rsync
rsync -avHX --exclude={/dev,/proc,/sys,/tmp,/run,/mnt,/media} / /mnt/rpool/ROOT/pve-1/
# Ajuster le fstab, grub, initramfs pour les UUIDs NVMe
→ Préférer l'installation fraîche (moins de risques de configs résiduelles sdb).
Phase 2 — Cutover (downtime ~20 min)¶
# 1. Arrêter tous les LXC
for ct in 100 101 102 103 104 105; do pct stop $ct; done
# 2. Éteindre le serveur
shutdown -h now
# 3. Physiquement : retirer Kingston M.2 → insérer SN770 M.2
# 4. Démarrer sur le NVMe (modifier ordre boot BIOS)
# Vérifier que sdb, sdc, sdd, sde sont toujours détectés (6 ports SATA actifs)
Phase 3 — Restauration LXC depuis PBS¶
# Sur nouveau Proxmox ZFS, déclarer les storages
# Ajouter ZFS storage "local-zfs" (rpool/data)
# Déclarer sdc comme directory storage pour PBS secondary
# Restaurer PBS (LXC 102 en premier pour avoir le datastore)
# Note: PBS actuel est sur sdb encore accessible en lecture
pct restore 102 \
/mnt/sdb-pbs/datastore/backup/ns/ct/102/... \
--storage local-zfs \
--rootfs local-zfs:45
# Reconfigurer PBS datastore vers rpool/pbs
# Dans LXC 102 : modifier /etc/proxmox-backup/datastore.cfg
# Pointer vers /mnt/pbs (bind mount de rpool/pbs)
# Restaurer les autres LXC depuis PBS
for ct in 100 101 103 104 105; do
pct restore $ct /backup/... --storage local-zfs
done
Phase 4 — Post-migration (J+1 à J+7)¶
# Configurer CPU limits LXC 101
pct set 101 --cpulimit 2 --cpuunits 1024
# Ajouter LXC 102 au job PBS backup
pvesh set /cluster/backup/backup-92e016e4-0dfc \
--vmid 100,101,102,103,104,105
# Configurer PBS secondary sur sdc
mkfs.ext4 -L pbs-secondary /dev/sdc1
echo "/dev/sdc1 /mnt/pbs-secondary ext4 defaults,nofail 0 2" >> /etc/fstab
mount /mnt/pbs-secondary
# Cron rsync PBS primary → secondary (04:30 daily, après sync Google Drive)
echo "30 4 * * * root rsync -avz --delete /mnt/pbs/ /mnt/pbs-secondary/" \
>> /etc/cron.d/pbs-secondary-sync
# Corriger SnapRAID partition sde
# ATTENTION: sauvegarder /mnt/storage vers Toshiba avant
sgdisk --zap-all /dev/sde
sgdisk -n 1:0:0 -t 1:8300 /dev/sde
mkfs.ext4 -L storage /dev/sde1
# Restaurer données depuis backup/Toshiba
# Reconfigurer snapraid.conf
# Configurer Uptime Kuma rétention (via UI Settings → Statistical)
# Réduire à 30 jours max (évite la DB de 300MB qui cause clear-history saturation)
# Monitoring pool ZFS
zpool status rpool
zfs list -o name,used,avail,quota rpool rpool/data rpool/pbs rpool/scratch rpool/lxc-data
7. RÉSOLUTION PROBLÈME PAR PROBLÈME¶
| Problème documenté | Cause racine | Solution dans ce plan | Statut |
|---|---|---|---|
| Pool thin 92.6% → services DOWN | LVM thin global, pas d'isolation, overhead 2x | Datasets ZFS avec quotas séparés | ✅ Résolu structurellement |
| PBS consomme l'espace qu'il protège | PBS datastore dans le pool LXC | rpool/pbs dataset dédié avec quota 450GB |
✅ Résolu |
| Snapshots PBS échouent pool > 90% | Snapshot COW LVM nécessite espace libre dans pool | ZFS snapshots = 0 octet à création | ✅ Résolu |
| Backup 10-14× plus lent (50min) | Fragmentation LVM thin + sdb UNC errors | NVMe ZFS, pas de thin provisioning fragmenté | ✅ Résolu |
| sdb UNC errors, 11 ans d'usure | Hardware fin de vie | sdb retiré, NVMe SN770 MTBF ~1.75M heures | ✅ Résolu |
| ext4 errors dm-6, dm-8, dm-10 | I/O EIO propagés depuis sdb défaillant | e2fsck avant migration + NVMe sain | ✅ Résolu |
| SnapRAID non-fonctionnel (286K) | Table de partition corrompue sur sde | sgdisk --zap-all + re-partition + restore data |
✅ Plan inclus |
| LXC 102 non backupé | Oubli configuration PBS | Ajout à job PBS post-migration | ✅ Plan inclus |
| Uptime Kuma CPU starvation LXC 101 | Pas de CPU limits cgroups | pct set 101 --cpulimit 2 |
✅ Plan inclus |
| TRIM LVM fragmenté | Couche dm-thin complique le discard | autotrim=on ZFS → TRIM natif NVMe |
✅ Résolu |
8. ROADMAP INTÉGRÉE (APRÈS STABILISATION)¶
Tier warm — MergerFS + SnapRAID¶
Une fois le NVMe stable (M+1), la roadmap MergerFS/SnapRAID reprend naturellement :
/mnt/media (sdd HGST 4TB) → data d1 SnapRAID
/mnt/storage (sde IronWolf 4TB) → data d2 SnapRAID (après fix partition)
/mnt/archive (Toshiba 1TB) → data d3 SnapRAID OU parity drive
→ Pool MergerFS /mnt/pool si plusieurs disques data
→ Parity SnapRAID sur Toshiba si seul disque data supplémentaire
La documentation Architecture Stockage Prévisionnelle reste valide et s'applique directement.
Migration ZFS vers miroir (M+12, optionnel)¶
Si un second NVMe est acquis (slot PCIe x1 → M.2 SATA ou second M.2 après extension matérielle) :
zpool attach rpool nvme0n1 nvme1n1 # Ajout miroir à chaud
# → rpool passe de ONLINE à MIRROR sans downtime
# → redondance matérielle NVMe atteinte
Monitoring Prometheus + Grafana (Roadmap)¶
ZFS fournit des métriques natives pour node_exporter :