7.7 KiB
name, description, version, platforms, metadata
| name | description | version | platforms | metadata | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| self-hosted-torrent-client | Deploy and troubleshoot a Dockerized torrent client (qBittorrent) behind a VPN (Gluetun) with private tracker support, including dynamic IP registration via Mousehole for trackers like MyAnonaMouse (MAM). | 1.0.0 |
|
|
Self-Hosted Torrent Client (qBittorrent + Gluetun + Private Trackers)
Deploy and maintain a Dockerized qBittorrent behind a VPN tunnel (Gluetun) with support for private trackers that require IP registration (MAM, etc.).
Architecture
qBittorrent ──┤ │
│ network_mode: │
Mousehole ───┤ service:gluetun │── tun0 (VPN) ── Internet
│ │
Gluetun ──────┤ VPN client │── enp2s0 (host network)
All three containers share Gluetun's network namespace. Only Gluetun declares port mappings — qBittorrent and Mousehole inherit them.
Quickstart
1. Docker Compose
Use a single compose file with three services:
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "8080:8080" # qBittorrent WebUI
- "5010:5010" # Mousehole WebUI (use 0.0.0.0:5010 if accessing from LAN)
environment:
VPN_SERVICE_PROVIDER: "private internet access"
VPN_TYPE: "openvpn"
OPENVPN_PROTOCOL: "tcp"
OPENVPN_USER: "<your-username>"
OPENVPN_PASSWORD: "<your-password>"
SERVER_HOSTNAMES: "nl-amsterdam.privacy.network"
VPN_PORT_FORWARDING: "on"
PORT_FORWARD_ONLY: "true"
FIREWALL_VPN_INPUT_PORTS: "8080,5010"
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./qbittorrent:/config
- /path/to/downloads:/downloads
environment:
- PUID=1000
- PGID=1000
- TZ=America/Chicago
- WEBUI_PORT=8080
restart: unless-stopped
mousehole:
image: tmmrtn/mousehole:latest
container_name: mousehole
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
environment:
TZ: America/Chicago
MOUSEHOLE_AUTH_PASSWORD: <strong-password>
MOUSEHOLE_ALLOWED_HOSTS: localhost,127.0.0.1,[::1],192.168.50.98
volumes:
- mousehole:/var/lib/mousehole
restart: unless-stopped
volumes:
mousehole:
2. Set up qBittorrent
- Access WebUI at
http://<host-ip>:8080 - Go to Settings → Connection
- Set Listening Port to match Gluetun's forwarded port
- Disable UPnP/NAT-PMP
- The port is stored in
Session\PortinqBittorrent.conf - Verify the forwarded port from Gluetun:
docker exec gluetun cat /tmp/gluetun/forwarded_port
3. Set up Mousehole (for private trackers needing dynamic IP)
- Access Mousehole WebUI at
http://<host-ip>:5010 - Log in with the
MOUSEHOLE_AUTH_PASSWORD - On MAM, go to Preferences → Security and create a session:
- IP: Use the IP Mousehole shows you
- IP vs ASN locked: ASN
- Allow Session to set Dynamic Seedbox: Yes
- Label:
mousehole
- Copy the cookie MAM gives you
- Paste it into Mousehole and click Set
Common Private Tracker Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| "Unrecognized host/PassKey" | Browser IP ≠ VPN IP (private tracker mismatch) | Set up Mousehole with MAM Dynamic Seedbox API |
| "Non-Whitelisted client or version" | Client not on tracker's allowed list | Check tracker's allowed clients page; may need to downgrade/pin a version |
| Stalled torrent, no peers | Tracker not being contacted or port not reachable | Verify QBT port matches forwarded port, check tracker reachability: curl -s -o /dev/null -w "%{http_code}" "<tracker-announce-url>" |
| Port mismatch | Gluetun forwarded port ≠ QBT listening port | After Gluetun restart, check docker exec gluetun cat /tmp/gluetun/forwarded_port and update QBT config |
Pitfalls & Gotchas
qBittorrent WebUI: IPv6-Only Binding on linuxserver.io Images
Symptom: qBittorrent logs "WebUI: Now listening on IP: *, port: 8080" but the WebUI is unreachable from the Docker host via IPv4. Connection gets "Connection refused" on the container's Docker IP, even though qBittorrent is running and healthy.
Root cause: On linuxserver.io qBittorrent images (v5.2.x), the start script changes
WebUI\Address=* to WebUI\Address=localhost before launch. qBittorrent 5.2.x then
binds the WebUI only to IPv6 ([::]:8080). The Docker proxy connects via IPv4
(the container's Docker IP), which fails because nothing is listening on IPv4:8080.
Diagnosis:
docker exec qbittorrent sh -c 'cat /proc/net/tcp /proc/net/tcp6' | grep -i "1F90"
/proc/net/tcp(IPv4) — port 8080 (0x1F90) ABSENT/proc/net/tcp6(IPv6) — port 8080 PRESENT on[::]:1F90
Fix: Set WebUI\Address=0.0.0.0 in qBittorrent.conf (instead of *).
The start script's localhost override only triggers on empty or * values.
Then restart qBittorrent.
Port Binding
127.0.0.1:5010:5010binds to localhost ONLY — not accessible from LAN or other containers- Use
0.0.0.0:5010:5010or just5010:5010for LAN access - Default
docker compose restartdoes NOT pick up port binding changes — usedocker compose up -d --force-recreate <service>instead
Mousehole Host Header
- Default
MOUSEHOLE_ALLOWED_HOSTSonly allowslocalhost,127.0.0.1,[::1] - Add your LAN IP or hostname when accessing from another machine
Gluetun Recreates
- When Gluetun is recreated, qBittorrent may auto-detach if using
network_mode: service:gluetun - Docker Compose handles this correctly via container name, not container ID
- The PIA VPN IP WILL change on restart — Mousehole handles re-registration
Gluetun VPN IP Changes
- PIA and other VPN providers assign different IPs on reconnect
- This perfectly normal — Mousehole re-registers the new IP with the tracker
- Check current VPN IP:
docker exec gluetun cat /tmp/gluetun/ip
Stale Containers
- Old, unhealthy gluetun instances (e.g.,
vigorous_lewin) can sit around after config changes - Check for them with
docker ps -aand clean up:docker rm <container>
Port Forwarding Verification
- Check forwarded port:
docker exec gluetun cat /tmp/gluetun/forwarded_port - Check what port QBT is listening on from its logs or config
- Test port is open on VPN interface:
docker exec qbittorrent python3 -c "import socket; s=socket.socket(); s.settimeout(3); print(s.connect_ex(('10.26.x.x', 36590)))"(use actual tun0 IP from QBT logs)
Tracker Connectivity Test
- From inside the container:
docker exec qbittorrent curl -s -o /dev/null -w "%{http_code} (%{time_total}s)" --connect-timeout 15 "<tracker-url>" - A 404 on the announce URL is expected (missing required params) — means the tracker is reachable
- A 200 with "failure reason" means tracker is rejecting the announce specifically — check the reason text
Verification
After setup, confirm:
- Gluetun is healthy:
docker ps --filter name=gluetun --format '{{.Status}}' - Port forwarding is active:
docker exec gluetun cat /tmp/gluetun/forwarded_port - qBittorrent is using the forwarded port from its config
- Mousehole shows OK status after cookie is set
- Add a public tracker torrent to qBittorrent to verify general connectivity
- Add the private tracker torrent and check tracker status via MAM's tracker detail page