Skip to content

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 :

# zfs_exporter pour Prometheus
# Métriques : arc_size, arc_hit_ratio, pool_alloc, pool_free, scrub_errors