Self-Hosted Media Stack, Part 3: qBittorrent Behind a VPN, Plus SABnzbd
Part 3 of Media Stack from Scratch. Part 2 left
Sonarr and Radarr with a wanted list and no way to act on it — this part gives them
muscle. You'll want the folder layout from Part 1 in place: everything below assumes
/Media with downloads/torrents/ and downloads/usenet/ inside it.
The download clients are the only part of the stack that talks to the outside world, and the torrent client goes behind a VPN in a way that fails closed: if the tunnel drops, downloads stop. Not "might leak for a few seconds" — stop.
Summary
- Pick one, or run both — qBittorrent and SABnzbd are two independent sections below, not a package deal. Skip to whichever you actually want.
- One container image handles qBittorrent and the VPN:
binhex/arch-qbittorrentvpn. The tunnel lives inside the container, so nothing else on the host is routed through it. - If the VPN is down, the container has no route out. That's the fail-closed part.
- SABnzbd needs no VPN — Usenet is TLS to a provider you pay, not a swarm of peers.
- Both clients download into
/Media/downloads/...so imports are hardlinks, not copies.
Torrents, Usenet, or both?
Sonarr and Radarr don't care how a file arrives — they just need a download client to hand releases to. That means this choice is genuinely yours, not a stack requirement:
- Only qBittorrent — skip straight to that section, then to "Introduce them to Sonarr and Radarr" and connect qBittorrent alone. Free, but content availability depends entirely on what indexers turn up and how well-seeded it is.
- Only SABnzbd — skip to that section instead. Usenet is a paid provider subscription (plus usually a separate indexer subscription), but retention is measured in years and speed doesn't depend on other users seeding.
- Both, which is what I run — the *arrs try Usenet and torrents as separate options per release and use whichever scores higher, so you get Usenet's reliability with torrents filling the gaps. This is the setup the rest of this part walks through.
Whichever you pick, the folder layout and hardlink logic from Part 1 apply identically — nothing below changes based on this choice except which section you read.
qBittorrent + VPN in one container
services:
qbittorrent:
# Pin the version. VPN images break on 'latest' more than any other kind of
# container - when networking inside the image changes, the symptom is a client
# that starts but never connects, which looks exactly like a VPN account problem.
image: binhex/arch-qbittorrentvpn:5.2.2-1-01
container_name: qbittorrent
restart: unless-stopped
environment:
- VPN_ENABLED=yes
- VPN_PROV=custom # 'custom' = bring your own .ovpn file
- VPN_CLIENT=openvpn
- LAN_NETWORK=192.168.1.0/24 # YOUR local subnet - see below
- NAME_SERVERS=1.1.1.1,1.0.0.1
- WEBUI_PORT=8085
- PUID=1001
- PGID=100
- TZ=Europe/Amsterdam
volumes:
- /path/to/appdata/qbittorrent:/config
- /path/to/Media:/Media
ports:
- "8085:8085"
devices:
- /dev/net/tun # the container builds the tunnel itself
cap_add:
- NET_ADMIN # and needs this to manage its own routing
Drop your provider's .ovpn file (and credentials file, if separate) into
/path/to/appdata/qbittorrent/openvpn/ before first start. The container picks it up,
builds the tunnel, and only then starts qBittorrent.
📄 Using Surfshark? Here's my actual compose file, masked — the real thing from my NAS with private values replaced, including where the Surfshark
.ovpnand service credentials go. Works the same for any provider that hands you an OpenVPN config.
The two settings everyone gets wrong:
LAN_NETWORKmust match your actual LAN subnet. This is the route exception that lets you reach the web UI while everything else goes through the tunnel. Get it wrong and the UI is unreachable — which looks like the container is broken when it's actually doing its job.- The web UI port must match
WEBUI_PORTon both sides of the mapping. The VPN image rewrites its firewall rules aroundWEBUI_PORT; mapping a different host port to it is fine, but changing the UI port only in qBittorrent's settings and not in the environment locks you out on next restart.
Verify the tunnel before moving on — from inside the container, the exit IP must be your VPN provider's, not your home IP:
docker exec qbittorrent curl -s ifconfig.me
In qBittorrent's settings, set the default save path to /Media/downloads/torrents/.
SABnzbd
Usenet needs no tunnel, so this one is plain:
sabnzbd:
image: linuxserver/sabnzbd:4.5.5
container_name: sabnzbd
restart: unless-stopped
environment:
- PUID=1001
- PGID=100
- TZ=Europe/Amsterdam
volumes:
- /path/to/appdata/sabnzbd:/config
- /path/to/Media:/Media
ports:
- "8080:8080"
First-run wizard: enter your provider's server, port 563 (TLS), and your credentials.
Then in Folders, set temporary download to /Media/downloads/usenet/incomplete
and completed to /Media/downloads/usenet/. Same reasoning as the torrent side: keep
it inside the /Media mount so the *arrs' imports are hardlinks.
Introduce them to Sonarr and Radarr
This is the payoff of doing Part 2 first — the *arrs are waiting, so the wiring is one settings page, done for whichever client(s) you set up:
In both Sonarr and Radarr → Settings → Download Clients → Add:
- qBittorrent (if you set it up) — host
qbittorrent, port8085, plus the web UI username and password. Container names resolve because everything shares the compose network. - SABnzbd (if you set it up) — host
sabnzbd, port8080, plus its API key (SABnzbd → Config → General).
Adding both is what lets the *arrs pick whichever scores higher per release, rather than being locked to one source. Hit Test on each — green checkmarks or fix it now, while the moving parts are few. From this moment on you stop opening the downloader UIs: the *arrs drive them, and the *arr queue is where progress lives.
Checkpoint
Whichever client(s) you set up:
- qBittorrent reachable on its web UI, exit IP = VPN provider's IP, a manually-added
torrent landing in
/Media/downloads/torrents/ - SABnzbd connected to your provider, test download landing in
/Media/downloads/usenet/ - that client green under Download Clients in Sonarr and Radarr
The wanted list from Part 2 still sits idle — the *arrs can now fetch, but they still can't search. Eyes next.
Next: Part 4 — Prowlarr, one place for all your indexers. Previous: Part 2 — Sonarr and Radarr.
If this saved you some time, a coffee keeps the lights on and the posts coming.
☕ Buy me a coffee