Seedbox Docker complète : *arr, Jellyfin et torrent sous VPN

Automatise ta seedbox avec Docker : Sonarr, Radarr, Prowlarr, Cross-Seed, Jellyfin et Seerr, et le torrent qui passe par ton propre VPN WireGuard.

Introduction

Niveau requis : intermédiaire. Tu dois être à l'aise avec un terminal Linux, SSH, les bases de Docker, et avoir un accès root à ton serveur WireGuard. Si ce n'est pas le cas, commence par mettre en place ce serveur avant de revenir ici.

Monter une seedbox, c'est bien. Ajouter à la main chaque série et chaque film qui t'intéresse, c'est vite pénible. Dans ce tutoriel, on écrit un Docker Compose complet qui assemble une stack automatisée : Sonarr et Radarr pour les séries et les films, Prowlarr pour les indexeurs, Deluge pour le téléchargement, Cross-Seed pour partager davantage, Jellyfin pour regarder le tout, et Seerr pour demander un film ou une série en deux clics.

Le trafic BitTorrent, lui, passe par ton propre VPN WireGuard grâce à Gluetun. Ton IP de FAI n'apparaît jamais dans les swarms, et si le tunnel tombe, Deluge est coupé d'internet au lieu de basculer sur ta connexion normale. Bonus d'avoir son propre serveur : le port entrant est fixe, et tu n'as rien à ouvrir sur ta box.

(Comme toujours : utilise cette stack pour des contenus dont tu as les droits. Un VPN ne rend pas légal ce qui ne l'est pas.)

Glossaire

Terme Définition
Stack *arr L'ensemble Sonarr, Radarr, Prowlarr et compagnie, qui automatisent la recherche et le rangement des médias
Indexeur Un site qui référence des fichiers .torrent, interrogé par Prowlarr
Gluetun Un conteneur client VPN (WireGuard ou OpenVPN) avec un pare-feu intégré
Kill switch Mécanisme qui bloque tout le trafic quand le VPN est coupé
Peer WireGuard Un appareil autorisé à se connecter à ton serveur WireGuard, avec ses propres clés et sa propre IP dans le tunnel
DNAT Règle de pare-feu qui renvoie le trafic reçu sur un port vers une autre machine : ici, du serveur WireGuard vers ta seedbox
Hardlink Un second nom pour un même fichier sur le disque : deux emplacements, une seule copie, zéro espace en plus
Cross-seed Partager les mêmes fichiers sur plusieurs trackers sans les retélécharger
Seerr Une interface de demandes : tu cherches un film ou une série, tu cliques, et Radarr ou Sonarr s'occupe du reste

Ce que fait cette stack Docker

Voici le rôle de chaque service, et par où il sort sur internet :

Service Rôle Port Sort par
Gluetun Client WireGuard, pare-feu et kill switch - Ton serveur WireGuard
Deluge Client BitTorrent 8112 Ton serveur WireGuard (via Gluetun)
Prowlarr Gère les indexeurs et les synchronise avec Sonarr et Radarr 9696 Ta connexion
Sonarr Surveille, télécharge et range les séries 8989 Ta connexion
Radarr Même chose pour les films 7878 Ta connexion
Cross-Seed Trouve tes fichiers sur d'autres trackers et les ajoute à Deluge - Ta connexion
Jellyfin Serveur multimédia pour regarder ta bibliothèque 8096 Ta connexion
Seerr Interface de demandes, connectée à Jellyfin, Sonarr et Radarr 5055 Ta connexion

Le principe à retenir : Deluge n'a pas de réseau à lui. Il est branché directement sur la pile réseau de Gluetun (network_mode: service:gluetun). Il ne peut sortir que par le tunnel, et si le tunnel tombe, il ne sort plus du tout.

Côté connexions entrantes, le chemin est le suivant :

Pair BitTorrent -> IP publique du serveur WireGuard, port 6881
               -> règle DNAT sur le serveur
               -> tunnel WireGuard
               -> Gluetun (10.8.0.2)
               -> Deluge

Ta box ne voit passer que du trafic WireGuard chiffré. Aucun port n'y est ouvert.

Prérequis

  • Un serveur Ubuntu ou Debian allumé en continu chez toi, avec Docker et le plugin Compose installés (docker compose version doit répondre).
  • Un serveur WireGuard qui fonctionne (un VPS, par exemple), qui fait déjà sortir ses clients sur internet : IP forwarding activé et NAT (masquerade) en place. Si ton téléphone navigue déjà à travers lui, c'est bon.
  • Un peer dédié à la seedbox sur ce serveur, avec son fichier de configuration client. Dans les exemples, il a l'IP 10.8.0.2 dans le tunnel.
  • Un accès root au serveur WireGuard, pour la redirection de port.
  • De la place disque chez toi, sur un seul système de fichiers pour les téléchargements et la bibliothèque (on verra pourquoi).
  • Ton UID et ton GID : id -u et id -g (généralement 1000).

Vérifie les conditions de ton hébergeur avant de commencer : beaucoup de fournisseurs de VPS interdisent ou limitent le P2P, et certains facturent le trafic au-delà d'un quota.

Comment organiser les dossiers pour les hardlinks ?

C'est l'étape qu'on bâcle souvent, et c'est celle qui fait qu'une stack *arr fonctionne bien ou mal.

Quand Sonarr importe un épisode téléchargé, il doit le placer dans ta bibliothèque sans le retirer de Deluge, sinon tu arrêtes de partager. S'il le copie, tu occupes deux fois l'espace. S'il crée un hardlink, le fichier apparaît aux deux endroits pour le prix d'un seul.

La condition : les téléchargements et la bibliothèque doivent être sur le même système de fichiers, et vus par Docker à travers un seul montage. Deux volumes séparés, même sur le même disque, sont deux points de montage différents pour le conteneur, et les hardlinks échouent.

On suit donc la structure recommandée par les TRaSH Guides, avec une seule racine :

sudo mkdir -p /srv/data/{torrents,media/{tv,movies}}
sudo chown -R $USER:$USER /srv/data

Ce qui donne :

/srv/data
├── torrents        <- ce que Deluge télécharge et partage
└── media
    ├── movies      <- bibliothèque films (Radarr, Jellyfin)
    └── tv          <- bibliothèque séries (Sonarr, Jellyfin)

Chaque conteneur ne voit que ce dont il a besoin, mais toujours sous le même chemin /data :

  • Sonarr, Radarr et Cross-Seed voient tout /srv/data sous /data ;
  • Deluge ne voit que /data/torrents ;
  • Jellyfin ne voit que /data/media, en lecture seule.

Crée ensuite le dossier du projet et un sous-dossier de configuration par service :

mkdir -p ~/mediastack/{gluetun,deluge,prowlarr,sonarr,radarr,cross-seed,jellyfin,seerr}
cd ~/mediastack

Les créer toi-même évite que Docker les crée avec root comme propriétaire.

Setup

Étape 1 : rediriger un port sur ton serveur WireGuard

Pour que les autres pairs puissent se connecter à Deluge, ton serveur WireGuard doit renvoyer un port de son IP publique vers ta seedbox, à travers le tunnel. On prend le 6881, en TCP et en UDP.

Sur le serveur WireGuard, repère d'abord le nom de l'interface publique :

ip route | grep default

Le nom après dev (souvent eth0, parfois ens3 ou autre) est celui à utiliser. Ouvre ensuite la configuration du serveur, généralement /etc/wireguard/wg0.conf, et ajoute ces lignes dans la section [Interface], à côté de tes PostUp existants. Remplace eth0 et 10.8.0.2 si besoin :

PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 6881 -j DNAT --to-destination 10.8.0.2
PostUp = iptables -t nat -A PREROUTING -i eth0 -p udp --dport 6881 -j DNAT --to-destination 10.8.0.2
PostUp = iptables -I FORWARD -i eth0 -o %i -d 10.8.0.2 -p tcp --dport 6881 -j ACCEPT
PostUp = iptables -I FORWARD -i eth0 -o %i -d 10.8.0.2 -p udp --dport 6881 -j ACCEPT
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 6881 -j DNAT --to-destination 10.8.0.2
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp --dport 6881 -j DNAT --to-destination 10.8.0.2
PostDown = iptables -D FORWARD -i eth0 -o %i -d 10.8.0.2 -p tcp --dport 6881 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o %i -d 10.8.0.2 -p udp --dport 6881 -j ACCEPT

Ce que font ces règles :

  • les deux DNAT renvoient tout ce qui arrive sur le port 6881 de l'IP publique vers 10.8.0.2, l'IP de ta seedbox dans le tunnel ;
  • les deux FORWARD autorisent ce trafic à traverser le serveur. On les insère en tête de chaîne (-I) pour qu'elles passent avant les règles d'un éventuel UFW ;
  • %i est remplacé automatiquement par le nom de l'interface WireGuard ;
  • les PostDown retirent proprement les mêmes règles quand l'interface s'arrête.

Pas besoin de règle de retour : la seedbox renvoie tout son trafic dans le tunnel, et le serveur sait à qui rendre les réponses. Les pairs voient même leur vraie IP de source, ce que BitTorrent apprécie.

Si ton serveur WireGuard ne sert qu'à ça, applique la configuration en redémarrant l'interface (les clients sont coupés quelques secondes) :

sudo systemctl restart wg-quick@wg0

S'il a d'autres clients, évite de le redémarrer. Charge plutôt le nouveau peer à chaud, puis lance une fois à la main les commandes iptables des lignes PostUp (elles seront rejouées automatiquement au prochain démarrage) :

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

Avant de choisir ton port, vérifie qu'il n'est pas déjà redirigé vers une autre machine avec sudo iptables -t nat -S PREROUTING. On y revient dans la section de débogage.

Enfin, si ton hébergeur a un pare-feu dans son interface web, ouvre le port 6881 en TCP et en UDP de ce côté aussi.

Ton serveur tourne avec une interface comme wg-easy plutôt qu'avec wg-quick ? Le principe est le même, mais les règles se déclarent dans ses propres réglages de hooks.

Étape 2 : mettre la configuration WireGuard dans un fichier .env

Gluetun ne lit pas tel quel le fichier client généré par ton serveur : il en reprend les valeurs une par une. Voici la correspondance :

Dans ton fichier client Variable du .env
[Interface] PrivateKey WG_PRIVATE_KEY
[Interface] Address WG_ADDRESS (l'adresse IPv4 seule, en /32)
[Peer] PublicKey WG_SERVER_PUBLIC_KEY
[Peer] PresharedKey WG_PRESHARED_KEY
[Peer] Endpoint WG_ENDPOINT_IP et WG_ENDPOINT_PORT

Les lignes DNS et AllowedIPs ne sont pas reprises : Gluetun gère son propre DNS, et fait passer tout le trafic dans le tunnel par défaut. C'est exactement ce qu'on veut.

nano ~/mediastack/.env
PUID=1000
PGID=1000
TZ=Europe/Paris
DATA_DIR=/srv/data
TORRENT_PORT=6881

WG_PRIVATE_KEY=cle_privee_du_client
WG_ADDRESS=10.8.0.2/32
WG_SERVER_PUBLIC_KEY=cle_publique_du_serveur
WG_PRESHARED_KEY=cle_partagee
WG_ENDPOINT_IP=203.0.113.10
WG_ENDPOINT_PORT=51820

Trois précisions :

  • DATA_DIR : la racine des données créée plus haut. Garde /srv/data, ou adapte si tes données sont ailleurs. Ce dossier doit exister sur l'hôte : sinon, Docker peut le créer dans un espace minuscule interne à sa VM (c'est le cas sous Docker Desktop), et rien ne se télécharge.
  • WG_ENDPOINT_IP attend une adresse IP. Si ton fichier client indique un nom de domaine, remplace-le par l'IP du serveur.
  • WG_ADDRESS : si ta configuration contient aussi une adresse IPv6 et que ton Docker n'a pas d'IPv6, ne garde que l'IPv4.

Restreins les droits du fichier, il contient ta clé privée :

chmod 600 ~/mediastack/.env

Étape 3 : écrire le docker-compose.yml complet

nano ~/mediastack/docker-compose.yml
services:
  gluetun:
    image: qmcgaw/gluetun:v3
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    environment:
      - TZ=${TZ}
      - VPN_SERVICE_PROVIDER=custom
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=${WG_PRIVATE_KEY}
      - WIREGUARD_PUBLIC_KEY=${WG_SERVER_PUBLIC_KEY}
      - WIREGUARD_PRESHARED_KEY=${WG_PRESHARED_KEY}
      - WIREGUARD_ADDRESSES=${WG_ADDRESS}
      - WIREGUARD_ENDPOINT_IP=${WG_ENDPOINT_IP}
      - WIREGUARD_ENDPOINT_PORT=${WG_ENDPOINT_PORT}
      - FIREWALL_VPN_INPUT_PORTS=${TORRENT_PORT}
    volumes:
      - ./gluetun:/gluetun
    ports:
      - 8112:8112 # interface web de Deluge, publiée ici et pas sur Deluge
    networks:
      - mediastack
    restart: unless-stopped

  deluge:
    image: lscr.io/linuxserver/deluge:latest
    container_name: deluge
    network_mode: service:gluetun
    depends_on:
      gluetun:
        condition: service_healthy
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./deluge:/config
      - ${DATA_DIR}/torrents:/data/torrents
    restart: unless-stopped

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./prowlarr:/config
    ports:
      - 9696:9696
    networks:
      - mediastack
    restart: unless-stopped

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./sonarr:/config
      - ${DATA_DIR}:/data
    ports:
      - 8989:8989
    networks:
      - mediastack
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./radarr:/config
      - ${DATA_DIR}:/data
    ports:
      - 7878:7878
    networks:
      - mediastack
    restart: unless-stopped

  cross-seed:
    image: ghcr.io/cross-seed/cross-seed:6
    container_name: cross-seed
    user: "${PUID}:${PGID}"
    command: daemon
    volumes:
      - ./cross-seed:/config
      - ${DATA_DIR}:/data
    networks:
      - mediastack
    restart: unless-stopped

  jellyfin:
    image: lscr.io/linuxserver/jellyfin:latest
    container_name: jellyfin
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./jellyfin:/config
      - ${DATA_DIR}/media:/data/media:ro
    ports:
      - 8096:8096
    networks:
      - mediastack
    restart: unless-stopped

  seerr:
    image: ghcr.io/seerr-team/seerr:latest
    container_name: seerr
    init: true
    environment:
      - TZ=${TZ}
    volumes:
      - ./seerr:/app/config
    ports:
      - 5055:5055
    networks:
      - mediastack
    restart: unless-stopped

networks:
  mediastack:

Les points qui comptent :

  • VPN_SERVICE_PROVIDER=custom : Gluetun ne se connecte pas à un fournisseur commercial, mais au serveur décrit par les variables WIREGUARD_*.
  • FIREWALL_VPN_INPUT_PORTS : le pare-feu de Gluetun bloque tout ce qui entre par le tunnel. Cette variable laisse passer le port 6881 redirigé à l'étape 1. Sans elle, ta règle DNAT ne sert à rien.
  • Pas de 6881 dans ports : le trafic des pairs arrive par le tunnel, pas par ton réseau local. Publier ce port sur l'hôte serait inutile.
  • WIREGUARD_PRESHARED_KEY : si ta configuration client n'a pas de PresharedKey, supprime cette ligne du compose et du .env.
  • network_mode: service:gluetun : Deluge vit dans la pile réseau de Gluetun. Conséquence directe, Deluge ne peut pas avoir de section ports : c'est Gluetun qui publie le 8112 pour lui.
  • condition: service_healthy : Deluge attend que Gluetun soit connecté avant de démarrer, grâce au healthcheck intégré à l'image.
  • ${DATA_DIR}:/data : un seul montage pour Sonarr, Radarr et Cross-Seed, la condition des hardlinks. DATA_DIR vient du .env.
  • :ro sur Jellyfin : il n'a besoin que de lire ta bibliothèque, il ne doit pas pouvoir la modifier.
  • ghcr.io/cross-seed/cross-seed:6 : on fixe la version majeure, car le format de configuration change entre les versions.
  • Seerr n'utilise ni PUID ni PGID : il tourne toujours avec l'utilisateur interne node (UID 1000). Le paramètre init: true est demandé par sa documentation pour que le conteneur s'arrête proprement.

Étape 4 : lancer la stack et vérifier l'IP

cd ~/mediastack
docker compose up -d
docker compose ps

Tous les services doivent être running, et Gluetun healthy. Si Gluetun redémarre en boucle, ses logs disent pourquoi (clé invalide, endpoint injoignable...) :

docker compose logs gluetun

Vérifie maintenant que le tunnel fait son travail. Compare l'IP publique de ta seedbox à celle vue depuis la pile réseau de Gluetun :

curl -s https://ifconfig.me; echo
docker run --rm --network container:gluetun curlimages/curl -s https://ifconfig.me; echo

La première commande affiche l'IP de ton FAI, la seconde celle de ton serveur WireGuard. Si les deux sont identiques, arrête tout et corrige avant d'aller plus loin. Si la seconde ne répond pas du tout, passe à la section suivante.

Comment déboguer le tunnel si Gluetun reste « unhealthy » ?

Si Gluetun redémarre en boucle et que Deluge reste bloqué en Created, c'est que le tunnel ne fait pas passer de trafic. Les logs ressemblent alors à ceci :

INFO [wireguard] Connecting to 203.0.113.10:51820
INFO [wireguard] Wireguard setup is complete. Note Wireguard is a silent protocol...
WARN [vpn] restarting VPN because it failed to pass the healthcheck: ... lookup github.com: i/o timeout

Le message « setup is complete » ne prouve rien : WireGuard est silencieux, et Gluetun ne sait pas si l'autre côté répond. Le diagnostic se fait donc sur le serveur WireGuard, en trois commandes, pendant que Gluetun tourne.

1. Le serveur connaît-il le peer ?
sudo wg show wg0

Cherche le bloc avec allowed ips: 10.8.0.2/32 :

  • Le peer n'apparaît pas : il est dans wg0.conf mais pas chargé. Recharge la configuration à chaud, sans couper les autres clients :
    sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
  • Le peer est là, sans latest handshake : les clés ne correspondent pas. Vérifie que WG_SERVER_PUBLIC_KEY est bien la clé publique du serveur (sudo wg show wg0 public-key), que la clé pré-partagée est identique des deux côtés, et que la clé publique enregistrée sur le serveur correspond à ta WG_PRIVATE_KEY (echo 'CLE_PRIVEE' | wg pubkey).
  • Handshake récent, mais transfer bloqué à quelques Ko : les clés sont bonnes, le problème est plus loin. Passe à l'étape suivante.
2. Les paquets arrivent-ils dans le tunnel ?
sudo tcpdump -ni wg0 host 10.8.0.2 -c 20
  • Rien ne s'affiche : WireGuard reçoit les paquets mais les jette sans rien dire. La cause la plus fréquente est une adresse qui ne correspond pas : le serveur n'accepte de ce peer que des paquets venant de son AllowedIPs. Vérifie que WG_ADDRESS dans ton .env est exactement l'IP déclarée sur le serveur, puis recrée Gluetun :
    docker compose up -d --force-recreate gluetun
  • Des requêtes vers 1.1.1.1.53 apparaissent, mais sans réponse : le serveur reçoit le trafic mais ne le fait pas sortir sur internet.
3. Le serveur fait-il sortir le trafic ?

Remplace eth0 par ton interface publique, celle qu'indique ip route | grep default :

sudo tcpdump -ni eth0 port 53 -c 20

Si les requêtes de Gluetun (github.com, cloudflare.com) n'y apparaissent jamais, vérifie le forwarding et le NAT :

sysctl net.ipv4.ip_forward
sudo iptables -t nat -S POSTROUTING
sudo iptables -S FORWARD

Il te faut net.ipv4.ip_forward = 1, une règle MASQUERADE qui couvre l'IP du peer, et une règle FORWARD qui accepte le trafic de wg0 vers l'interface publique.

Quand tout fonctionne, la capture sur wg0 montre les requêtes DNS, leurs réponses, puis les connexions HTTPS du healthcheck dans les deux sens. Gluetun passe alors healthy et Deluge démarre.

Le port est-il déjà pris sur ton serveur ?

Si ton serveur WireGuard redirige déjà des ports vers d'autres machines (une autre seedbox, par exemple), vérifie avant de choisir le tien :

sudo iptables -t nat -S PREROUTING

Si une règle DNAT existe déjà pour le 6881, la tienne, ajoutée après, ne sera jamais atteinte : c'est la première règle qui correspond qui l'emporte. Prends un autre port, et reporte-le dans TORRENT_PORT, dans les règles du serveur et dans Deluge. Tu peux vérifier que Gluetun utilise la bonne valeur dans ses logs :

INFO [firewall] setting allowed input port 6881 through interface tun0...

Étape 5 : configurer Deluge

Ouvre http://IP_DE_LA_SEEDBOX:8112. Le mot de passe par défaut est deluge. Si la fenêtre Connection Manager s'affiche, connecte-toi à l'hôte local proposé. Puis, dans Preferences :

  1. Interface : change le mot de passe immédiatement. Changement du mot de passe dans les préférences de Deluge

  2. Downloads : mets le dossier de téléchargement sur /data/torrents. Dossier de téléchargement de Deluge réglé sur /data/torrents

  3. Plugins : coche Label. Sonarr et Radarr s'en servent pour classer leurs téléchargements. Activation du plugin Label dans Deluge

  4. Network : dans Incoming Port, décoche Use Random Port et mets 6881. Dans Outgoing Ports, laisse en revanche Use Random Ports coché : seul le port entrant doit être fixe, sinon Deluge n'arrive presque plus à se connecter aux seeds. Décoche enfin UPnP et NAT-PMP, la redirection est déjà faite par ton serveur. Préférences réseau de Deluge avec le port entrant fixé et les ports sortants aléatoires

Ajoute un torrent légal (une image ISO de Debian, par exemple), puis vérifie depuis une autre machine que le port répond bien sur l'IP de ton serveur WireGuard :

nc -vz IP_DU_SERVEUR_WIREGUARD 6881

Si la connexion réussit, les pairs peuvent te joindre. Ce port ne changera pas : c'est toi qui l'as choisi.

Sur mes captures, le port est le 6883 : le 6881 était déjà redirigé vers une autre machine sur mon serveur. C'est exactement le cas décrit dans la section de débogage.

Torrent Ubuntu en cours de téléchargement dans Deluge à travers le VPN Liste des pairs connectés dans Deluge

Étape 6 : brancher Prowlarr, Sonarr et Radarr

Ouvre chaque interface une première fois. Sonarr, Radarr et Prowlarr affichent alors un écran Authentication Required :

  • Authentication Method : choisis Forms (Login Page), puis crée ton identifiant.
  • Allowed Hosts : indique les noms sous lesquels on accède à l'application, c'est-à-dire son nom de conteneur (utilisé par les autres conteneurs) et l'adresse que tu tapes dans ton navigateur. Par exemple sonarr,192.168.1.50 pour Sonarr, radarr,192.168.1.50 pour Radarr, prowlarr,192.168.1.50 pour Prowlarr. Tu peux aussi laisser le champ vide pour tout autoriser sur ton réseau local. Ce réglage demande un redémarrage : docker compose restart prowlarr sonarr radarr.

Ne mets pas les noms des autres applications dans ce champ : c'est la liste des adresses de l'application elle-même. Une mauvaise valeur peut te bloquer l'accès depuis ton navigateur.

Dans Sonarr (:8989) : Écran d'accueil de Sonarr

  1. Settings > Media Management : ajoute le dossier racine /data/media/tv. Affiche les options avancées et vérifie que Use Hardlinks instead of Copy est bien coché. Ajout du dossier racine des séries dans Sonarr Option Use Hardlinks instead of Copy activée dans Sonarr

  2. Settings > Download Clients : ajoute Deluge avec l'hôte gluetun (et non deluge, ni une IP qui changerait à chaque recréation du conteneur), le port 8112 et le mot de passe de l'interface web de Deluge. Garde la catégorie proposée, tv-sonarr : elle devient un label dans Deluge. Laisse Post-Import Category vide et Remove Completed décoché, pour que Sonarr ne supprime pas les torrents que tu partages. Clique sur Test. Liste des clients de téléchargement dans Sonarr Configuration du client Deluge dans Sonarr avec l'hôte gluetun Test réussi du client Deluge dans Sonarr

Dans Radarr (:7878) : même chose, avec le dossier racine /data/media/movies et la catégorie radarr. Ajout du dossier racine des films dans Radarr Configuration du client Deluge dans Radarr

Pourquoi gluetun comme hôte ? Parce que Deluge n'a pas de nom sur le réseau Docker : il écoute à l'intérieur du conteneur Gluetun.

Dans Prowlarr (:9696) : Écran d'accueil de Prowlarr

  1. Indexers : ajoute tes indexeurs. Ajout d'un indexeur dans Prowlarr

  2. Settings > Apps : ajoute Sonarr et Radarr. Prowlarr Server : http://prowlarr:9696, Sonarr Server : http://sonarr:8989 (ou http://radarr:7878), et la clé API que tu trouves dans Settings > General de chaque application. Liste des applications connectées dans Prowlarr Ajout de Sonarr dans les applications de Prowlarr Ajout de Radarr dans les applications de Prowlarr

Prowlarr pousse alors tes indexeurs dans Sonarr et Radarr. Tu ne les configures plus qu'à un seul endroit.

Étape 7 : Jellyfin pour regarder ta bibliothèque

Ouvre http://IP_DE_LA_SEEDBOX:8096, suis l'assistant, puis ajoute deux médiathèques :

  • Films : /data/media/movies
  • Séries : /data/media/tv

Assistant de démarrage de Jellyfin Création du compte administrateur Jellyfin Ajout d'une médiathèque dans Jellyfin Médiathèque Films pointant vers /data/media/movies Médiathèque Séries pointant vers /data/media/tv Langue et pays des métadonnées dans Jellyfin Accès distant dans l'assistant Jellyfin Fin de l'assistant de configuration Jellyfin Tableau de bord de Jellyfin

C'est tout. Jellyfin lit directement les dossiers que Sonarr et Radarr remplissent, sans aucune manipulation de ta part. Si ta machine a un GPU Intel ou AMD et que tu veux le transcodage matériel, ajoute devices: - /dev/dri:/dev/dri au service et active l'accélération dans les paramètres de lecture de Jellyfin.

Étape 8 : Seerr pour demander un film ou une série

Page d'accueil de Seerr

Sonarr et Radarr sont parfaits pour gérer une bibliothèque, mais leur interface n'est pas faite pour le reste du foyer. Seerr ajoute une vitrine à la Netflix : on cherche un titre, on clique sur Demander, et la demande part dans Radarr ou Sonarr.

Ouvre http://IP_DE_LA_SEEDBOX:5055 et suis l'assistant :

  1. Serveur multimédia : choisis Jellyfin, avec l'hôte jellyfin et le port 8096, puis connecte-toi avec ton compte administrateur Jellyfin. Choix de Jellyfin comme serveur multimédia dans Seerr Connexion de Seerr au serveur Jellyfin

  2. Bibliothèques : synchronise-les et coche Films et Séries. Seerr saura ainsi ce que tu possèdes déjà. Synchronisation des bibliothèques Jellyfin dans Seerr

  3. Services : ajoute Radarr (hôte radarr, port 7878, clé API, dossier racine /data/media/movies) et Sonarr (hôte sonarr, port 8989, clé API, dossier racine /data/media/tv). Choisis un profil de qualité pour chacun et coche Default Server. Ajout de Radarr et Sonarr dans les services de Seerr

Les utilisateurs de ton Jellyfin peuvent ensuite se connecter à Seerr avec leurs propres identifiants. Pas de compte supplémentaire à créer, et tu gardes la main sur qui peut demander quoi.

Étape 9 : configurer Cross-Seed

Au premier démarrage, Cross-Seed génère son fichier de configuration dans ~/mediastack/cross-seed/config.js. Ouvre-le et modifie les lignes suivantes (ne supprime aucune option, le programme refuserait de démarrer) :

torznab: [
  "http://prowlarr:9696/1/api?apikey=CLE_API_PROWLARR",
  "http://prowlarr:9696/2/api?apikey=CLE_API_PROWLARR",
],
sonarr: ["http://sonarr:8989/?apikey=CLE_API_SONARR"],
radarr: ["http://radarr:7878/?apikey=CLE_API_RADARR"],
torrentClients: ["deluge:http://:MOT_DE_PASSE_DELUGE@gluetun:8112/json"],
useClientTorrents: true,
linkDirs: ["/data/torrents/cross-seed"],
linkType: "hardlink",
action: "inject",

Quelques précisions :

  • torznab : une URL par indexeur, avec son numéro avant /api. Copie l'URL RSS de chaque indexeur dans Prowlarr et remplace l'hôte par prowlarr:9696. L'URL de base de Prowlarr, sans numéro, ne fonctionne pas.
  • torrentClients : pour Deluge, pas de nom d'utilisateur, d'où le : juste avant le mot de passe. S'il contient des caractères spéciaux, encode-les (@ devient %40, par exemple).
  • linkDirs : sous /data/torrents, donc sur le même montage que tes téléchargements. Cross-Seed y crée des hardlinks, et Deluge les voit au même chemin.
  • action: "inject" : les correspondances trouvées sont ajoutées directement dans Deluge.

Par défaut, Cross-Seed vérifie les nouveautés de tes indexeurs toutes les 30 minutes et lance une recherche de fond par jour. Rien à programmer de ton côté. Redémarre-le pour appliquer la configuration :

docker compose restart cross-seed
docker compose logs -f cross-seed

Sur un tracker privé, lis ses règles avant d'activer Cross-Seed : certains limitent le volume de recherches automatiques, et c'est toi qui en es responsable.

Ce qui passe par le VPN, et ce qui n'y passe pas

Soyons précis sur ce que protège cette architecture.

Passe par le VPN : tout le trafic de Deluge, c'est-à-dire les connexions avec les autres pairs, entrantes comme sortantes. C'est là que ton IP est visible par des inconnus, et c'est ce qui intéresse l'ayant droit qui surveille un essaim.

Ne passe pas par le VPN : Prowlarr, Sonarr, Radarr et Cross-Seed quand ils interrogent les indexeurs, Seerr quand il récupère les fiches des films et des séries, et Jellyfin. Ces échanges se font avec des sites, pas avec un essaim de pairs.

En cas de coupure : Gluetun applique son pare-feu en permanence. Si le tunnel tombe, Deluge n'a aucune autre route vers internet. Il ne bascule jamais sur ta connexion normale.

Vérifie aussi que les hardlinks fonctionnent après quelques imports. Cette commande liste les fichiers de ta bibliothèque qui ont plus d'un lien, c'est-à-dire ceux partagés avec /srv/data/torrents :

find /srv/data/media -type f -links +1 | head

Si elle ne renvoie rien alors que des épisodes ont été importés, Sonarr copie au lieu de lier : revois tes montages.

Mettre à jour la stack

cd ~/mediastack
docker compose pull
docker compose up -d
docker image prune -f

Sauvegarde régulièrement ~/mediastack : c'est là que vivent toutes tes configurations, .env compris. Et garde une copie de la configuration de ton serveur WireGuard.

Les pièges classiques

  • Gluetun ne passe jamais « healthy » : peer non chargé sur le serveur, clés inversées, ou WG_ADDRESS différente de l'AllowedIPs déclarée côté serveur. La section de débogage plus haut te permet de trouver lequel en trois commandes.
  • Le tunnel monte, mais rien ne sort : ton serveur WireGuard n'a pas d'IP forwarding ou de règle de masquerade pour ses clients.
  • Le port 6881 reste fermé : port déjà redirigé vers une autre machine sur le serveur, mauvais nom d'interface dans les règles (eth0 au lieu de ens3...), pare-feu de l'hébergeur oublié, FIREWALL_VPN_INPUT_PORTS manquant, ou port différent dans Deluge.
  • Rien ne télécharge, sans aucune erreur : vérifie l'espace réellement disponible dans le conteneur avec docker exec deluge df -h /data/torrents. Si la commande affiche overlay et quelques centaines de Mo, ton dossier de données n'est pas monté : corrige DATA_DIR, puis recrée les conteneurs avec docker compose up -d --force-recreate.
  • Deluge ne se connecte à aucun seed : les Outgoing Ports sont fixés sur un seul port. Coche Use Random Ports, puis redémarre Deluge.
  • Impossible d'ouvrir Sonarr, Radarr ou Prowlarr après le premier lancement : Allowed Hosts ne contient pas l'adresse que tu utilises. Vide le champ ou ajoute-la.
  • Deluge injoignable après une mise à jour de Gluetun : quand Gluetun est recréé, Deluge perd sa pile réseau. Un docker compose restart deluge règle le problème.
  • « Connection refused » dans Sonarr : tu as mis deluge comme hôte au lieu de gluetun.
  • Les fichiers sont copiés, pas liés : les téléchargements et la bibliothèque sont montés séparément, ou sur deux disques différents.
  • Cross-Seed ne trouve pas les fichiers : le chemin vu par Deluge (/data/torrents/...) doit exister à l'identique dans le conteneur Cross-Seed. Il n'y a aucune traduction de chemins entre les deux.
  • Cross-Seed se comporte bizarrement après plusieurs jours : la documentation signale un problème de désynchronisation entre Deluge sous Docker et useClientTorrents. Un redémarrage de Deluge est la première chose à tester.
  • Seerr refuse de démarrer (« permission denied ») : il tourne en UID 1000. Si ton utilisateur n'a pas cet UID, donne-lui le dossier : sudo chown -R 1000:1000 ~/mediastack/seerr.
  • Interfaces exposées sur internet : ne redirige aucun port de l'interface web, ni sur ta box ni sur ton serveur WireGuard. Si tu utilises UFW chez toi, rappelle-toi que Docker contourne ses règles pour les ports qu'il publie. Pour un accès à distance, connecte-toi à ton réseau par WireGuard, avec un peer séparé.

FAQ

Un VPN perso, est-ce que ça me rend anonyme ?

Non. Les pairs voient l'IP de ton serveur et non celle de ton FAI, mais ce serveur est loué à ton nom. En cas de signalement, c'est ton hébergeur qui le reçoit, et il sait qui tu es. Un VPN, personnel ou commercial, ne change rien au statut des contenus : cette stack sert à automatiser une bibliothèque que tu as le droit de télécharger et de partager.

Faut-il ouvrir un port sur ma box ?

Non. La seule connexion qui sort de chez toi est celle du tunnel WireGuard, initiée par Gluetun. Les pairs se connectent au port 6881 de ton serveur, qui les renvoie dans le tunnel.

Pourquoi ne pas mettre toute la stack derrière le VPN ?

Parce que ça complique le réseau pour un gain limité. Les services derrière Gluetun ne peuvent plus joindre les autres conteneurs sans régler la variable FIREWALL_OUTBOUND_SUBNETS, et Jellyfin comme Seerr ont tout intérêt à rester sur ton réseau local. Si ton FAI bloque l'accès à un indexeur, tu peux déplacer Prowlarr derrière Gluetun de la même manière que Deluge, mais traite ça comme une évolution, pas comme la base.

Je peux utiliser qBittorrent ou Transmission à la place de Deluge ?

Oui. Remplace l'image, garde network_mode: service:gluetun, publie son port d'interface sur Gluetun, règle son port d'écoute sur 6881, et adapte la ligne torrentClients de Cross-Seed, qui prend en charge les deux.

Conclusion

Avec un seul fichier Compose, tu as une stack qui cherche, télécharge, range, partage et diffuse sans que tu aies à y toucher, et même une interface pour que tes proches fassent leurs demandes eux-mêmes. Le trafic BitTorrent sort uniquement par ton tunnel WireGuard, avec un port fixe que tu maîtrises de bout en bout, et la structure /srv/data permet aux hardlinks de faire leur travail : un fichier, une copie, partagé et regardable en même temps.

Et toi, tu fais tourner quoi derrière ton VPN ?

Sources

Cet article vous a plu ?

Avec un compte gratuit, vous aimez, vous commentez et vous recevez des suggestions « Pour vous ». Connexion en un clic avec Google ou GitHub, sans mot de passe.

Commentaires

Connectez-vous en un clic pour rejoindre la discussion.

Chargement des commentaires…

Masquer ce commentaire ?

Il ne sera plus visible du public. Son texte est conservé et vous pourrez le rétablir. Ses réponses restent visibles.

Signaler ce commentaire

Pourquoi ?