Vous cherchez à construire le Media Server Docker ultime pour 2026 ? Vous êtes au bon endroit.
On a tous commencé pareil : un Raspberry Pi, un tuto trouvé sur un forum datant de 2019, et des dossiers éparpillés entre /home/pi/Downloads et un disque dur externe. Ça marche… jusqu’au jour où ça casse, où le disque est plein à cause des duplications de fichiers, ou pire, où vous recevez un mail de l’ARCOM.
L’année 2026 vient de commencer, il est temps de prendre des bonnes résolutions.
Voici la Partie 1 de ma série sur l’Ultimate Media Stack. L’objectif ? Construire une infrastructure résiliente, sécurisée et automatisée.
Aujourd’hui, on pose les fondations :
- Une gestion des permissions utilisateurs propre (pas de chmod 777).
- Une architecture de dossiers qui permet les Hardlinks.
- Une stack réseau isolée via VPN avec Port Forwarding automatique.
Pré-requis pour ce tuto Media Server
Cette série d’article assume que vous êtes à l’aise avec un terminal Linux. Je ne couvrirai pas l’installation de base de l’OS.
- Un serveur sous Linux (Ubuntu 24.04 LTS recommandé, ou Debian 12).
- Docker et Docker Compose installés et fonctionnels.
⚠️ Attention : N’installez pas le paquet docker.io ou docker-compose présent dans les dépôts par défaut d’Ubuntu (souvent obsolètes ou en version Snap). Utilisez impérativement la procédure d’installation officielle de Docker pour obtenir la dernière version stable. - Un accès SSH ou terminal.
- Un abonnement VPN compatible. Dans ce guide, j’utilise Private Internet Access parce que j’ai un abonnement chez eux, mais c’est totalement adaptable à AirVPN /ProtonVPN.
1. L’Infra Système : Le principe du « Least Privilege »
En tant que dev backend, il y a une chose qui me hérisse le poil dans les tutos de Media Server : l’utilisation de l’utilisateur root ou de votre utilisateur personnel (1000:1000) pour lancer des conteneurs.
Si une faille est découverte dans qBittorrent, l’attaquant se retrouve avec les droits de votre utilisateur principal (qui a souvent les droits sudo).
Nous allons créer un utilisateur système dédié, sans shell (impossible de se connecter avec), qui ne servira qu’à gérer les fichiers médias.
Sur votre serveur (Ubuntu/Debian) :
# Créer le groupe media (GID 2000)
sudo groupadd -g 2000 media
# Créer l'utilisateur media (UID 2000) sans accès login
sudo useradd -u 2000 -g 2000 -s /usr/sbin/nologin -d /nonexistent media
# VOUS ajouter au groupe media (pour pouvoir gérer les fichiers via SSH/SMB)
# Remplacez $USER par votre nom d'utilisateur si besoin
sudo usermod -aG media $USERNote : Déconnectez-vous et reconnectez-vous pour que l’ajout au groupe soit pris en compte.
2. L’Architecture des dossiers
C’est ici que 90% des installations de Media Server échouent à être performantes.
- Mauvaise pratique : Avoir un volume /downloads pour les torrents et un volume /movies pour Plex.
- Conséquence : Quand le téléchargement est fini, le logiciel doit copier le fichier d’un dossier à l’autre. Ça prend du temps, ça use le disque, et ça occupe deux fois la place.
- Bonne pratique : il faut utiliser un seul volume unifié /data.
- Avantage : Le déplacement est instantané via un Hardlink. Le fichier physique reste au même endroit sur le disque, mais il apparaît dans deux dossiers différents.
⚠️ Attention : Pour qu’un hardlink fonctionne, les dossiers source et destination doivent impérativement résider sur le même système de fichiers (partition/disque).
- L’erreur classique : Monter un disque dur externe (USB) spécifiquement sur /data/media.
- Pourquoi c’est grave : Si /data/torrents est sur votre disque système (SSD/NVMe) et /data/media sur votre disque externe, le système ne peut pas créer de lien physique entre deux tables d’inodes différentes. Une copie lente sera faite et doublera l’espace utilisé.
- La solution : Si vous utilisez un disque de stockage externe, montez-le à la racine : /data. Ainsi, les sous-dossiers torrents et media seront physiquement voisins sur le même disque.
Création de la structure
# On crée la structure unifiée
sudo mkdir -p /data/torrents/{downloads,cross-seed}
sudo mkdir -p /data/media/{movies,tv}
# On applique les permissions drastiques
sudo chown -R media:media /data
sudo chmod -R 775 /data
# On s'assure que les prochains fichiers créés appartiennent au groupe media
sudo chmod g+s /data3. La Stack Réseau : Le Choix du VPN
Pour cette stack Media Server, nous avons besoin de trois choses :
- Masquer notre IP.
- Avoir un Port Forwarding (port ouvert) pour être « connectable » et maintenir de bons débits.
- Que ce port se mette à jour automatiquement dans qBittorrent.
Le Dilemme : Wireguard vs OpenVPN
Si Wireguard est plus performant sur le papier, l’intégration du Port Forwarding chez certains fournisseurs (comme Private Internet Access – PIA) est native via OpenVPN.
Sur un processeur moderne (ou même un Raspberry Pi 4/5), la différence de CPU est négligeable pour une connexion fibre domestique. Si votre fournisseur supporte facilement wireguard, je vous encourage à l’utiliser.
4. Le « Bunker » Docker
Nous allons utiliser 3 conteneurs :
- Gluetun : Le tunnel VPN. Il sera le seul à avoir accès à internet.
- qBittorrent : Le client de téléchargement. Il n’aura pas de réseau propre, il utilisera celui de Gluetun. Si Gluetun tombe, qBittorrent est coupé du monde. On a ainsi un killswitch natif pour éviter d’exposer notre adresse ip réelle.
- Qui : Une interface moderne pour qBittorrent. Il remplacera celle native (qui a un peu vieilli) et servira de proxy sécurisé pour la suite.
Le fichier compose.yml
Créez un dossier pour votre stack (ex: /opt/media-stack) et votre fichier compose :
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
# --- Configuration PIA ---
- VPN_SERVICE_PROVIDER=private internet access
- VPN_TYPE=openvpn
- OPENVPN_USER=${PIA_USER} # À mettre dans votre .env
- OPENVPN_PASSWORD=${PIA_PASSWORD}
- SERVER_REGIONS=Netherlands # Proche et optimisé P2P
# --- Port Forwarding Magique ---
- VPN_PORT_FORWARDING=on
# Script "Sidecar" : Met à jour le port dans qBit automatiquement
- VPN_PORT_FORWARDING_UP_COMMAND=/bin/sh -c 'wget -O- --retry-connrefused --post-data "json={\"listen_port\":{{PORT}},\"current_network_interface\":\"{{VPN_INTERFACE}}\",\"random_port\":false,\"upnp\":false}" [http://127.0.0.1:8080/api/v2/app/setPreferences](http://127.0.0.1:8080/api/v2/app/setPreferences) 2>&1'
# Le nettoyage en cas d'arrêt (Optionnel mais propre)
- VPN_PORT_FORWARDING_DOWN_COMMAND=/bin/sh -c 'wget -O- --retry-connrefused --post-data "json={\"listen_port\":0,\"current_network_interface\":\"lo\"}" [http://127.0.0.1:8080/api/v2/app/setPreferences](http://127.0.0.1:8080/api/v2/app/setPreferences) 2>&1'
ports:
- 8080:8080 # Accès qBittorrent UI
- 7476:7476 # Accès Qui UI
# Pas de port Torrent exposé ici, tout passe par le tunnel !
volumes:
- ${CONFIG_DIR}/gluetun:/gluetun
restart: always
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
# Isolation Réseau Totale : Utilise le réseau de Gluetun
network_mode: service:gluetun
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
- WEBUI_PORT=8080
volumes:
- ${CONFIG_DIR}/qbittorrent:/config
- ${DATA_DIR}/torrents/downloads:/data/torrents/downloads # Mapping vital pour les hardlinks
restart: always
depends_on:
- gluetun
qui:
image: ghcr.io/autobrr/qui:latest
container_name: qui
# On le met aussi derrière le VPN (Sidecar) pour simplifier la connexion (localhost)
# Ce n'est pas obligatoire pour la sécurité, mais pratique pour la configuration.
network_mode: service:gluetun
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
# Configuration de la connexion à qBit (Localhost car même réseau)
- QUI__HOST=0.0.0.0
- QUI__PORT=7476
volumes:
- ${CONFIG_DIR}/qui:/config
restart: always
N’oubliez pas votre fichier .env à côté :
# Permissions (User Media = 2000)
PUID=2000
PGID=2000
TZ=Europe/Paris
# Chemins (Portabilité)
CONFIG_DIR=./
DATA_DIR=/data
# Credentials VPN
PIA_USER=p1234567
PIA_PASSWORD=votre_mot_de_passe5. Premier Démarrage & Initialisation
Lancez la stack :
docker compose up -dRécupération du mot de passe
Attention, depuis les versions récentes, qBittorrent ne définit plus adminadmin comme mot de passe par défaut. Il génère un mot de passe temporaire au premier lancement. Pour le récupérer, regardez les logs :
docker logs qbittorrentCherchez la ligne : A temporary password is provided for this session: XXXXXXX.
Configuration de qBittorrent
- Accédez à qBittorrent :
http://VOTRE_IP:8080. - Connectez-vous avec admin et le mot de passe temporaire.
- Allez dans Options > Web UI.
- Cochez « Bypass authentication for clients on localhost ». (Indispensable pour Gluetun et les futurs outils).
- Changez le mot de passe administrateur par un mot de passe fort.
- Allez dans Options > Downloads.
- C’est l’étape la plus importante : Changez le Default Save Path de
/downloadsvers/data/torrents/downloads. - Pourquoi ? Si vous laissez /downloads, vos fichiers seront stockés à l’intérieur du conteneur (ou mal mappés), et les hardlinks ne pourront pas fonctionner avec Sonarr/Radarr.
- C’est l’étape la plus importante : Changez le Default Save Path de
- Sauvegardez (bouton tout en bas).
- Redémarrez le conteneur Gluetun pour qu’il applique le port forwarding avec la nouvelle auth :
docker restart gluetun.
6. Configuration de Qui
Maintenant que le moteur (qBit) tourne, connectons le volant (Qui).
- Accédez à
http://VOTRE_IP:7476. - Créez votre compte administrateur (c’est ce compte qui sécurisera l’accès).
- Une fois connecté, vous arrivez sur le dashboard vide. Cliquez sur Settings (roue dentée) > Instances > Add Instance.
- Remplissez les champs :
- Name : qBittorrent
- Host : localhost (Car Qui et qBit partagent le même réseau via Gluetun)
- Port : 8080
- Username : admin
- Password : Le mot de passe que vous venez de définir.
Une fois validé, Qui va se connecter. Vous avez désormais une interface moderne pour piloter vos téléchargements.

7. Sécurisation Finale
Même si le network_mode: service:gluetun force le trafic via le VPN, nous allons ajouter une ceinture de sécurité supplémentaire dans qBittorrent.
- Retournez dans l’interface classique de qBittorrent (
http://VOTRE_IP:8080), car Qui ne permet pas encore de modifier ces réglages avancés. - Allez dans Options > Advanced.
- Cherchez Network Interface.
- Sélectionnez tun0 (l’interface du VPN).
- Validez.
Ceci garantit que si l’interface VPN tun0 disparaît à l’intérieur du conteneur (lors d’un crash openvpn par exemple), qBittorrent cessera immédiatement tout trafic, car il ne verra plus d’interface réseau valide. C’est un Killswitch.
8. Vérification de l’étanchéité
Ne faites jamais confiance à une config sans la tester.
Test 1 : L’IP publique
Nous allons demander à qBittorrent de télécharger son IP publique vue depuis l’intérieur du tunnel.
docker exec -it qbittorrent curl https://api.ipify.orgSi l’IP est une IP néerlandaise (ou du pays choisi) : C’est bon.
Test 2 : Le Killswitch (Arrêt d’urgence)
Pour vérifier que qBittorrent dépend bien du VPN pour vivre :
- Arrêtez le conteneur VPN :
docker stop gluetun. - Essayez d’exécuter la commande précédente.
- Elle devrait maintenant retourner une erreur :
curl: (6) Could not resolve host: api.ipify.org (Could not contact DNS servers)
Et après ?
Votre infrastructure est prête. Vous téléchargez de manière sécurisée, vos dossiers sont prêts pour les hardlinks, et votre port forwarding se gère tout seul.
Dans la Partie 2, nous installerons le « Cerveau » de votre Media Server : Prowlarr, Sonarr et Radarr, et nous verrons comment Qui va sécuriser leurs communications.

Ping : Tuto Media Server 2026 : Sonarr, Radarr, Prowlarr & Recyclarr- LabDuGeek.fr
Ping : Guide Media Server Docker 2026 (Partie 3) : Plex, Seerr et Transcodage - LabDuGeek.fr