Files
homelab-configs/jellyfin/qbittorrent/docker-compose.yml
T

85 lines
3.3 KiB
YAML

services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=openvpn
- OPENVPN_USER=${OPENVPN_USER}
- OPENVPN_PASSWORD=${OPENVPN_PASSWORD}
- FIREWALL_OUTBOUND_SUBNETS=192.168.88.0/24
- FIREWALL_INPUT_PORTS=8080
- TZ=America/Chicago
ports:
- 8080:8080
- 6881:6881
- 6881:6881/udp
volumes:
- /srv/docker/gluetun:/gluetun
restart: unless-stopped
healthcheck:
test: ["CMD", "sh", "-c", "wget -qO- https://ipinfo.io/ip || exit 1"]
interval: 60s
timeout: 20s
retries: 3
start_period: 30s
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
environment:
- PUID=1000
- PGID=1000
- TZ=America/Chicago
- WEBUI_PORT=8080
volumes:
- /srv/docker/qbittorrent/config:/config
- /mnt/media:/mnt/media
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
# NOTE (2026-08-07): Replaces the native qbittorrent-nox systemd service on the
# jellyfin host, which was bound to a desktop ProtonVPN app's tunnel interface
# (proton0). When that GUI-managed tunnel dropped, there was no way to reconnect
# without an interactive desktop session, silently killing all downloads.
#
# qbittorrent now runs with network_mode: service:gluetun, so it shares gluetun's
# network namespace entirely. If the VPN drops, qbittorrent has zero network
# access (fails closed, never falls back to the real IP) until gluetun
# auto-reconnects on its own -- no GUI or manual intervention needed.
#
# OPENVPN_USER / OPENVPN_PASSWORD are the ProtonVPN OpenVPN/IKEv2 credentials
# (see API Codes.md), NOT the regular Proton account login. Set them in a .env
# file alongside this compose file (not committed) or export them before
# `docker compose up -d`.
#
# qbittorrent's media volume is mounted at /mnt/media (matching the original
# native install), NOT /data, so it lines up with Sonarr/Radarr's existing
# Remote Path Mapping (host 192.168.88.10: /mnt/media/ -> /data/) and the
# pre-existing categories (tv-sonarr, movies-radarr) needed zero path rewriting
# when migrating resume data.
#
# NOTE (2026-09-03): Added com.centurylinklabs.watchtower.monitor-only=true to
# both services. A Watchtower instance was newly deployed on the jellyfin host
# on 2026-09-01 (previously only siklos had one), and its first successful
# gluetun update on 2026-09-02 broke qbittorrent: Watchtower recreates the
# dependent container (qbittorrent) BEFORE recreating the linked target
# (gluetun), so qbittorrent got wired to gluetun's old, about-to-be-removed
# container ID and lost all network access until manually fixed. monitor-only
# keeps the nightly email notification (so Joe still hears about new images)
# but stops Watchtower from auto-restarting this pair. Update manually with
# `cd /srv/docker/qbittorrent && docker compose pull && docker compose up -d`
# (compose handles the gluetun-then-qbittorrent order correctly; watchtower
# does not).