VM Media sur KVM : migrer Sonarr, Radarr et SABnzbd dans une VM isolée

⚡ En bref
Migrer son media stack (Sonarr, Radarr, SABnzbd) dans une VM KVM dédiée sur Ubuntu 24.04 : isolation sécurité, snapshots facilités, NFS QNAP monté dans la VM, reverse proxy nginx sur l’hôte. La migration conserve intégralement les bases de données SQLite et la configuration existante.
Stack cible :
- 🖥️ Hôte : NUC8i3BEH, Ubuntu Server, nginx reverse proxy, Plex (transcodage GPU)
- 📦 VM media-vm : Ubuntu 24.04, 2 vCPU, 8 Go RAM, 120 Go (X5 NVMe), NFS QNAP
- 🎬 Services migrés : Sonarr (port 8989), Radarr (port 7878), SABnzbd (port 6789)
🧠 Pourquoi isoler le media stack dans une VM
Sonarr, Radarr et SABnzbd présentent une surface d’attaque non négligeable : accès réseau vers des indexeurs externes, exécution de scripts post-download, accès au système de fichiers de la bibliothèque. Les confiner dans une VM apporte :
- Isolation sécurité — un compromis de Sonarr ne peut pas atteindre l’hôte directement
- Snapshots avant mise à jour — via
virsh snapshot-create-as, rollback en 30 secondes si une mise à jour casse la base SQLite - Plex reste sur l’hôte — accès au GPU Intel (
/dev/dri) pour le transcodage matériel, non virtualisable simplement
Le NFS QNAP étant déjà monté sur l’hôte, les téléchargements transitent déjà par le réseau — la VM ne change rien à ce comportement, elle monte les mêmes partages directement.
🔧 Étape 1 — Préparer le pool libvirt sur le X5
Le disque virtuel est stocké sur le SSD NVMe X5 (exFAT), via une image loop ext4. fallocate n’est pas supporté sur exFAT — utiliser dd à la place.
# Créer l'image de 150 Go
dd if=/dev/zero of=/mnt/X5/libvirt-media.img bs=1G count=150 status=progress
# Formater en ext4
mkfs.ext4 /mnt/X5/libvirt-media.img
# Monter
mkdir -p /mnt/libvirt-media
mount -o loop /mnt/X5/libvirt-media.img /mnt/libvirt-media
# Persistance au boot
echo '/mnt/X5/libvirt-media.img /mnt/libvirt-media ext4 loop 0 0' >> /etc/fstab
systemctl daemon-reload
# Déclarer le pool libvirt
virsh pool-define-as media-pool dir --target /mnt/libvirt-media
virsh pool-autostart media-pool
virsh pool-start media-pool🔧 Étape 2 — Télécharger l’image cloud Ubuntu 24.04
On utilise une image cloud Ubuntu (pas une ISO live) pour que cloud-init applique la configuration au premier boot sans installateur interactif.
wget -P /mnt/libvirt-media \
https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
qemu-img resize /mnt/libvirt-media/noble-server-cloudimg-amd64.img 120G🔧 Étape 3 — Créer l’image cloud-init
Cloud-init configure l’utilisateur, le mot de passe et les clés SSH au premier boot. Les fichiers doivent s’appeler exactement user-data et meta-data — sans préfixe — sinon cloud-init ne les détecte pas.
⚠️ Piège fréquent : toujours inclure deux clés SSH dans
authorized_keys— la clé du laptop ET la clé NUC→VM. Sans la clé NUC→VM, la connexion SSH depuis l’hôte est impossible.
cat > /tmp/user-data <<'EOF'
#cloud-config
hostname: media-vm
users:
- name: jm
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false
passwd: $VOTRE_HASH$...
ssh_authorized_keys:
- ssh-ed25519 AAAA... cle-laptop
- ssh-ed25519 AAAA... cle-nuc-to-vm
chpasswd:
expire: false
ssh_pwauth: true
package_update: true
packages:
- curl
- wget
- nfs-common
runcmd:
- systemctl enable nfs-client.target
instance-id: media-vm-001
local-hostname: media-vm
EOF
cat > /tmp/meta-data <<'EOF'
instance-id: media-vm-001
local-hostname: media-vm
EOF
genisoimage -output /mnt/libvirt-media/media-vm-cloud-init.img \
-volid cidata -joliet -rock \
/tmp/user-data /tmp/meta-data🔧 Étape 4 — Créer la VM
virt-install \
--name media-vm \
--ram 8192 \
--vcpus 2 \
--disk path=/mnt/libvirt-media/noble-server-cloudimg-amd64.img,format=qcow2 \
--disk path=/mnt/libvirt-media/media-vm-cloud-init.img,device=cdrom \
--os-variant ubuntu24.04 \
--network network=default \
--graphics none \
--noautoconsole \
--import
virsh autostart media-vm
virsh net-dhcp-leases default
ssh -i ~/.ssh/cle-nuc-to-vm jm@192.168.122.X🔧 Étape 5 — Monter le NFS QNAP dans la VM
sudo mkdir -p /mnt/Animes /mnt/Series /mnt/Films
sudo tee -a /etc/fstab <<'EOF'
192.168.1.11:/Animes /mnt/Animes nfs defaults,auto,vers=4.1,rw,nofail,bg,intr,rsize=262144,wsize=262144,noatime,_netdev 0 0
192.168.1.11:/Series /mnt/Series nfs defaults,auto,vers=4.1,rw,nofail,bg,intr,rsize=262144,wsize=262144,noatime,_netdev 0 0
192.168.1.11:/Films /mnt/Films nfs defaults,auto,vers=4.1,rw,nofail,bg,intr,rsize=262144,wsize=262144,noatime,_netdev 0 0
EOF
sudo systemctl daemon-reload && sudo mount -a🔧 Étape 6 — Installer Sonarr, Radarr, SABnzbd
🎬 Sonarr
curl -o- https://raw.githubusercontent.com/Sonarr/Sonarr/develop/distribution/debian/install.sh | sudo bash
# User: jm / Group: jm🎬 Radarr
curl -o servarr-install-script.sh \
https://raw.githubusercontent.com/Servarr/Wiki/master/servarr/servarr-install-script.sh
sudo bash servarr-install-script.sh
# Choisir 3 (Radarr) — User: jm / Group: jm📥 SABnzbd
sudo apt install -y sabnzbdplus
# Éditer /etc/default/sabnzbdplus : USER=jm
sudo systemctl restart sabnzbdplus⚙️ Écoute sur toutes les interfaces
Par défaut, Sonarr et Radarr écoutent sur 127.0.0.1 uniquement. Modifier config.xml :
sudo systemctl stop sonarr radarr
# Dans /var/lib/sonarr/config.xml et /var/lib/radarr/config.xml :
# <BindAddress>0.0.0.0</BindAddress>
sudo systemctl start sonarr radarrMême chose pour SABnzbd dans /home/jm/.sabnzbd/sabnzbd.ini :
host = 0.0.0.0
host_whitelist = media-vm, sabnzbd.arleo.eu,🔧 Étape 7 — Migrer les données
# Sur l'hôte
sudo systemctl stop sonarr radarr sabnzbdplus
scp -i ~/.ssh/cle-nuc-to-vm -r /mnt/X5/config/sonarr jm@192.168.122.X:/tmp/
scp -i ~/.ssh/cle-nuc-to-vm -r /mnt/X5/config/radarr jm@192.168.122.X:/tmp/
scp -i ~/.ssh/cle-nuc-to-vm -r /mnt/X5/config/sabnzbd jm@192.168.122.X:/tmp/# Dans la VM
sudo systemctl stop sonarr radarr sabnzbdplus
sudo rm -rf /var/lib/sonarr /var/lib/radarr /home/jm/.sabnzbd
sudo cp -r /tmp/sonarr /var/lib/sonarr
sudo cp -r /tmp/radarr /var/lib/radarr
sudo cp -r /tmp/sabnzbd /home/jm/.sabnzbd
sudo chown -R jm:jm /var/lib/sonarr /var/lib/radarr /home/jm/.sabnzbd
sudo systemctl start sonarr radarr sabnzbdplus
# Sur l'hôte — désactiver les anciens services
sudo systemctl disable --now sonarr radarr sabnzbdplus🔧 Étape 8 — Reverse proxy nginx sur l’hôte
La VM est sur le réseau NAT libvirt 192.168.122.0/24. Mettre à jour les vhosts existants :
sed -i 's/http:\/\/127\.0\.0\.1:8989/http:\/\/192.168.122.X:8989/g' \
/etc/nginx/sites-enabled/sonarr.arleo.eu
sed -i 's/http:\/\/127\.0\.0\.1:7878/http:\/\/192.168.122.X:7878/g' \
/etc/nginx/sites-enabled/radarr.arleo.eu
nginx -t && systemctl reload nginx🏁 Résultat
Le media stack tourne dans une VM isolée, avec accès complet aux partages NFS QNAP. Sonarr et Radarr voient la bibliothèque intacte et SABnzbd reçoit les téléchargements sur le disque local de la VM avant import.
| Service | URL | Port interne |
|---|---|---|
| Sonarr | https://sonarr.arleo.eu | 192.168.122.X:8989 |
| Radarr | https://radarr.arleo.eu | 192.168.122.X:7878 |
| SABnzbd | https://sabnzbd.arleo.eu | 192.168.122.X:6789 |
Points de vigilance :
- 💡 Inclure deux clés SSH dans cloud-init (laptop + NUC→VM)
- 💡 Les fichiers cloud-init doivent s’appeler exactement
user-dataetmeta-data - 💡
fallocatene fonctionne pas sur exFAT — utiliserdd - 💡 Sonarr/Radarr écoutent sur
127.0.0.1par défaut — modifierBindAddressdansconfig.xml - 💡 Ajouter le hostname du vhost dans
host_whitelistde SABnzbd