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 versiondoit 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.2dans 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 -uetid -g(généralement1000).
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/datasous/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 ; %iest remplacé automatiquement par le nom de l'interface WireGuard ;- les
PostDownretirent 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_IPattend 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 variablesWIREGUARD_*.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
6881dansports: 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 dePresharedKey, 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 sectionports: 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_DIRvient du.env.:rosur 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
PUIDniPGID: il tourne toujours avec l'utilisateur internenode(UID 1000). Le paramètreinit: trueest 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.confmais 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 queWG_SERVER_PUBLIC_KEYest 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 à taWG_PRIVATE_KEY(echo 'CLE_PRIVEE' | wg pubkey). - Handshake récent, mais
transferbloqué à 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 queWG_ADDRESSdans ton.envest 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.53apparaissent, 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 :
Interface : change le mot de passe immédiatement.

Downloads : mets le dossier de téléchargement sur
/data/torrents.
Plugins : coche Label. Sonarr et Radarr s'en servent pour classer leurs téléchargements.

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.

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.

É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.50pour Sonarr,radarr,192.168.1.50pour Radarr,prowlarr,192.168.1.50pour 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) :

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é.

Settings > Download Clients : ajoute Deluge avec l'hôte
gluetun(et nondeluge, ni une IP qui changerait à chaque recréation du conteneur), le port8112et 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.

Dans Radarr (:7878) : même chose, avec le dossier racine /data/media/movies et la catégorie 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) :

Indexers : ajoute tes indexeurs.

Settings > Apps : ajoute Sonarr et Radarr. Prowlarr Server :
http://prowlarr:9696, Sonarr Server :http://sonarr:8989(ouhttp://radarr:7878), et la clé API que tu trouves dans Settings > General de chaque application.

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

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

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 :
Serveur multimédia : choisis Jellyfin, avec l'hôte
jellyfinet le port8096, puis connecte-toi avec ton compte administrateur Jellyfin.

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

Services : ajoute Radarr (hôte
radarr, port7878, clé API, dossier racine/data/media/movies) et Sonarr (hôtesonarr, port8989, clé API, dossier racine/data/media/tv). Choisis un profil de qualité pour chacun et coche Default Server.
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 parprowlarr: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_ADDRESSdifférente de l'AllowedIPsdé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 (
eth0au lieu deens3...), pare-feu de l'hébergeur oublié,FIREWALL_VPN_INPUT_PORTSmanquant, 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 afficheoverlayet quelques centaines de Mo, ton dossier de données n'est pas monté : corrigeDATA_DIR, puis recrée les conteneurs avecdocker 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 delugerègle le problème. - « Connection refused » dans Sonarr : tu as mis
delugecomme hôte au lieu degluetun. - 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 ?

Commentaires
Connectez-vous en un clic pour rejoindre la discussion.