qBittorrent over Mullvad (Saltbox + Gluetun on saltierpoop)¶
Replace legacy binhex/qbittorrentvpn (PIA WireGuard in a restart loop) with the standard Saltbox qbittorrent role sharing the existing Gluetun instance used by JDownloader2.
Prerequisite: JDownloader2 over Mullvad — Gluetun already deployed with Mullvad WireGuard in Saltbox inventory.
Why Gluetun instead of qbittorrentvpn?¶
| Approach | Pros | Cons |
|---|---|---|
| binhex qbittorrentvpn | All-in-one image | Separate VPN config (PIA WG file); not Saltbox-native; currently broken (missing WG Endpoint) |
| Saltbox qbittorrent + Gluetun | One Mullvad config; same pattern as JD2; sb install lifecycle |
Must avoid port clashes inside Gluetun namespace; Traefik/DNS optional |
Saltbox docs: Gluetun — route other containers, qBittorrent.
Port / collision notes¶
Multiple apps can share one Gluetun instance if listening ports do not clash inside the Gluetun network namespace. Saltbox publishes app ports through Gluetun.
- qBittorrent web UI defaults to 8080 (
qbittorrent_role_web_port). - JDownloader2 uses MyJDownloader (no Traefik hostname required); web UI ports differ.
- Before install:
docker exec gluetun wget -qO- https://ifconfig.me/ip— confirm Mullvad egress.
If you add Traefik hostnames for both apps, set distinct ports or use
gluetun_firewall_input_ports per Saltbox Gluetun docs.
Migration steps¶
1. Inventory (sb edit inventory)¶
Add to /srv/git/saltbox/inventories/host_vars/localhost.yml:
# Route qBittorrent through existing Gluetun (same Mullvad session as JD2)
qbittorrent_docker_network_mode: "container:gluetun"
gluetun_firewall_input_ports: "8080,6881"
# Required on Gluetun — Cloudflare IPv6 DNS validation fails (same as JD2)
qbittorrent_role_dns_enabled: false
*arr apps reach qBittorrent at qbittorrent:8080 on the Docker network. Traefik
hostname qbittorrent.realemail.app is not auto-provisioned when DNS role is disabled.
Validate:
2. Stop legacy qbittorrentvpn¶
docker stop qbittorrentvpn
docker rm qbittorrentvpn # after confirming no data you need in /opt/qbittorrentvpn
Legacy config (if any) under /opt/qbittorrentvpn/qBittorrent/ — import categories /
qBittorrent.conf into Saltbox paths after sb install qbittorrent if you want to
preserve state.
3. Install Saltbox qBittorrent¶
4. Point *arr stack at Gluetun (not qbittorrent)¶
With qbittorrent_docker_network_mode: "container:gluetun", the qBittorrent
container shares Gluetun's network namespace. It is not registered as
qbittorrent on the Saltbox Docker network, so Sonarr/Radarr cannot reach
http://qbittorrent:8080.
Use internal Docker hostname gluetun, port 8080, HTTP (no TLS):
| Field | Value |
|---|---|
| Host | gluetun |
| Port | 8080 |
| SSL | off |
| Username / password | Saltbox accounts.yml / qBittorrent Web UI |
Verified from the Sonarr container: curl http://gluetun:8080 → qBittorrent UI.
Do not use https://qbittorrent.realemail.app for arr download clients —
Traefik routes that hostname through Authentik forward-auth* (302 to login).
Humans use the FQDN in a browser; API clients need the direct gluetun:8080 path.
Helper to bulk-update DB download clients (already pointed at legacy
qbittorrentvpn / qfloodvpn):
Then docker restart sonarr radarr. Remove duplicate stale qBittorrent entries
in each app's Settings → Download Clients if you only need one.
5. Verify VPN egress¶
# Mullvad IP from Gluetun namespace (same as JD2)
docker exec gluetun wget -qO- https://ifconfig.me/ip
# qBittorrent should match when adding a test torrent + checking peer IP in logs
# Kill switch: stop gluetun — qbit must not download
docker stop gluetun
docker exec qbittorrent wget -qO- --timeout=5 https://ifconfig.me || echo "blocked OK"
docker start gluetun
6. Remove sandbox qbittorrentvpn from Saltbox (optional)¶
If qbittorrentvpn was installed via sandbox:
Rollback¶
Keep /opt/qbittorrentvpn backup until new qBittorrent is proven. Re-enable legacy
container only if you restore a working PIA WireGuard wg0.conf with valid Endpoint.