❯ infra · Running

Un NAS x86 à 6W rejoint le cluster Pi — partie 2 : la stack media déménage A 6W x86 NAS Joins the Pi Cluster — Part 2: The Media Stack Moves In

Migration de 12 conteneurs Docker — Jellyfin, la suite *arr, Nextcloud et compagnie — d'un vieil i5 à 84W vers le nouveau NAS N100 : rsync, une décision RAID1 pragmatique qui a battu le plan RAIDZ1, une bascule de routes Cloudflare Tunnel, et zéro interruption visible. Migrating 12 Docker containers — Jellyfin, the *arr suite, Nextcloud and friends — from an aging 84W i5 to the new N100 NAS: rsync, a pragmatic RAID1 decision that beat the RAIDZ1 plan, a Cloudflare Tunnel route flip, and zero user-visible downtime.

· 8 min de lecture 8 min read ·
nasdockermigrationjellyfinraidcloudflareinfra
🇬🇧 Read this article in English

La partie 1 s’arrêtait sur un NAS monté, instrumenté, et vide. Voici la suite : la stack media complète — Jellyfin, la suite *arr, qBittorrent, Nextcloud — a déménagé de l’ancien i5 (84 W, 2014) vers gbnas (6 W). Au moment où j’écris, 12 conteneurs tournent depuis plus d’une semaine sans un incident.

Le plan a perdu contre le pragmatisme : RAID1, pas RAIDZ1

La partie 1 annonçait fièrement un RAIDZ1 sur 3× 6 To (~12 To utiles). La réalité installée :

sdc                5.5T  disk
└─md127            5.5T  raid1  /mnt/storage
sdd                5.5T  disk
└─md127            5.5T  raid1  /mnt/storage

Un miroir mdadm à 2 disques, ~5,5 Tio utiles. Pas de ZFS. Ce qui a fait pencher la balance :

  • mdadm est ennuyeux — au sens noble : vingt ans de production, intégré au kernel, cat /proc/mdstat et c’est diagnostiqué. C’était déjà la techno de l’ancien NAS : migration mentale nulle.
  • ZFS et ses appétits : la règle d’or ~1 Go de RAM par To gérée se marie mal avec le plafond de 16 Go du N100M (un seul slot SO-DIMM). Entre un ARC affamé et de la RAM pour les conteneurs, j’ai choisi les conteneurs.
  • Deux disques en service, des baies libres : le Jonsbo N4 a 6 baies. Commencer en miroir simple et agrandir plus tard (ou basculer vers un vrai RAIDZ quand le besoin et les disques seront là) est plus honnête que d’immobiliser trois disques pour un besoin actuel de ~2 To.

La leçon méta : un plan d’infrastructure est une hypothèse, pas un engagement. Documenter le choix réel et ses raisons vaut mieux que tordre la réalité pour coller au plan publié — c’est exactement pour ça que cette partie 2 existe.

Le découpage final du stockage : NVMe 256 Go pour l’OS (LVM), SSD SATA 500 Go monté sur /mnt pour les volumes applicatifs Docker (configs, bases SQLite, métadonnées), et le miroir 5,5 Tio sur /mnt/storage pour les données.

La migration elle-même : l’avantage Docker Compose

C’est ici que le choix « Ubuntu Server + Docker Compose » de la partie 1 paie. La migration d’une stack Compose, c’est trois opérations sans magie :

  1. rsync -avP des données media et des dossiers appdata — les bases SQLite de Sonarr/Radarr, les métadonnées Jellyfin, tout voyage avec les fichiers. Rien à exporter, rien à reconstruire.
  2. Adapter les chemins dans le docker-compose.yml (les montages pointent vers /mnt/storage et le SSD appdata) et docker compose up -d.
  3. Basculer les routes : les hostnames publics (jellyfin.*, qbit.*, nextcloud.*…) sont définis dans le dashboard Cloudflare Tunnel et pointaient vers l’IP de l’ancien NAS. Changement de cible vers 10.0.0.30 — les URLs publiques, les certificats et les accès ne bougent pas d’un pixel.

Le résultat côté docker ps, une semaine plus tard :

jellyfin      lscr.io/linuxserver/jellyfin      Up 9 days
sonarr        lscr.io/linuxserver/sonarr        Up 9 days
radarr        lscr.io/linuxserver/radarr        Up 9 days
bazarr        lscr.io/linuxserver/bazarr        Up 9 days
prowlarr      lscr.io/linuxserver/prowlarr      Up 9 days
qbittorrent   lscr.io/linuxserver/qbittorrent   Up 9 days
flaresolverr  flaresolverr/flaresolverr         Up 9 days
nextcloud     lscr.io/linuxserver/nextcloud     Up 8 days

Bonus du gain de puissance : le N100 apporte un QuickSync moderne (décodage AV1, encodage HEVC) — le transcodage Jellyfin se fait désormais en hardware sur l’iGPU, là où le vieil i5 ramait en software sur ses gros profils.

Les invités surprise

Une machine confortable attire les workloads. Deux services ont rejoint la stack sans être dans le plan initial :

  • Home Assistant — la domotique méritait mieux qu’une existence précaire ; elle a maintenant un port d’attache stable
  • Bookshelf (tracker de lectures, flavor Hardcover) — le genre de petit service qu’on ne déploie jamais « parce qu’il n’y a pas de place où le mettre »

C’est un effet de second ordre des 6 W : la machine tourne 24/7 sans culpabilité énergétique, donc chaque nouveau service a un coût marginal nul.

La boucle est bouclée côté monitoring

Tout ce qui a été câblé en partie 1 couvre désormais une machine qui fait vraiment quelque chose : node_exporter remonte l’I/O du miroir RAID1 dans Grafana (le disque du NAS apparaît dans les audits du cluster), smartctl_exporter surveille la santé des disques qui portent désormais toutes les données, et l’agent Portainer (passé en 2.42.0 au passage) donne la vue conteneurs aux côtés des workloads K3s.

L’ancien NAS est éteint. 84 W → 6 W en continu, soit environ 680 kWh économisés par an — le genre de chiffre qui rend un homelab défendable en cuisine.

Ce qui reste (la vraie partie 3)

Le NAS héberge, mais il ne sert pas encore le cluster :

  • Backup offload : déplacer la cible S3 des backups Longhorn (MinIO, aujourd’hui 200 Gi sur le précieux SSD de worker-2) vers le miroir du NAS
  • backup.sh → CronJob K8s qui pousse les dumps hors cluster, vers /mnt/storage/backups
  • StorageClass NFS pour offrir au cluster un stockage orienté capacité, à côté de Longhorn
  • La question UPS : des disques mécaniques qui portent tout n’aiment pas les coupures sèches

Ce sera la partie 3 — quand elle sera réellement faite, pas avant. J’ai retenu la leçon du RAIDZ1.

nasdockermigrationjellyfinraidcloudflareinfra