qBittorrent
qBittorrent is the torrent half of the download layer. Sonarr and Radarr hand it a torrent, it downloads into a category folder, and the *arr apps hardlink the result into the library while the original keeps seeding.
Set this up before Sonarr and Radarr, so their download client config has something to point at.
qBittorrent is an open-source BitTorrent client with a web UI and a proper API. No ads, no crypto miners, no nag screens.
- Web UI and full API, which is how the *arr apps drive it
- Categories — route each download to its own folder automatically
- Sequential download and bandwidth scheduling
- RSS with auto-download rules, if you want to bypass the *arr apps for something
- IP filtering and encryption settings
- Seeding limits by ratio or time, with automatic removal
1. The container
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
environment:
- PUID=99
- PGID=100
- UMASK=022
- TZ=Europe/London
- WEBUI_PORT=8080
- TORRENTING_PORT=6881
volumes:
- /mnt/user/appdata/qbittorrent:/config
- /mnt/user/data/torrents:/data/torrents
ports:
- 8080:8080
- 6881:6881
- 6881:6881/udp
The volume mount is deliberately /data/torrents, not all of /data. qBittorrent only needs its own download tree; Sonarr and Radarr are the ones that need to see both sides to hardlink across.
TORRENTING_PORT must match the listening port you set in the UI later, and both TCP and UDP need forwarding for good peer connectivity.
2. Get the temporary password
Since version 4.6.1 there is no default adminadmin password. A random one is generated on first start and printed once, to the log:
Open http://<host-ip>:8080, log in as admin with that password.
If you missed it or the log rotated, delete /mnt/user/appdata/qbittorrent/qBittorrent/qBittorrent.conf and restart the container to get a fresh one.
3. Change the password immediately
Tools → Options → Web UI:
- Set a real username and password
- Tick Bypass authentication for clients on localhost only if you understand the implication
- Leave Enable clickjacking protection and CSRF protection on
Never expose the qBittorrent WebUI to the internet
It has no rate limiting on login, and an attacker with access can set the download path to anywhere the container can write and use it as a file drop. Keep it on the LAN, behind WireGuard, or behind an authenticating reverse proxy — never a plain port forward.
4. Set the download paths
Tools → Options → Downloads:
| Setting | Value |
|---|---|
| Default Save Path | /data/torrents |
| Keep incomplete torrents in | On, /data/torrents/incomplete |
| Copy .torrent files for finished downloads | Off |
| Pre-allocate disk space for all files | On |
| Append .!qB extension to incomplete files | On |
Pre-allocation reduces fragmentation on the array. The .!qB extension stops the *arr apps trying to import a file that is still downloading.
5. Create categories
This is the step that makes the whole stack work. Right-click in the Categories sidebar → Add category:
| Category | Save path |
|---|---|
tv |
/data/torrents/tv |
movies |
/data/torrents/movies |
music |
/data/torrents/music |
books |
/data/torrents/books |
The category names must match exactly what you enter in Sonarr and Radarr's download client settings. tv and TV are different categories, and a mismatch means the download completes and then nothing happens forever.
6. Seeding limits
Tools → Options → BitTorrent, at the bottom:
| Setting | Suggested |
|---|---|
| When ratio reaches | 2.0 |
| When seeding time reaches | 20160 minutes (14 days) |
| Then | Pause torrent |
Choose Pause, not Remove. Removing the torrent can delete the file, and because the *arr apps hardlinked it, deleting the original is usually harmless — but "usually" is doing a lot of work in that sentence, and a paused torrent costs nothing.
If you use private trackers, check their rules first. Many require a minimum seed time and will penalise you for stopping early.
7. Connection settings
Tools → Options → Connection:
- Port used for incoming connections:
6881, matchingTORRENTING_PORT - Use UPnP / NAT-PMP: off — forward the port manually at the router instead, or leave it closed if you are behind a VPN
- Global maximum connections: 500 is plenty; higher just adds CPU load
Under Speed, set a global upload cap at around 80% of your actual upload bandwidth. Saturating the uplink makes everything else in the house feel broken, and it is the most common "my internet is slow" cause in a homelab.
8. Connect Sonarr and Radarr
In each app: Settings → Download Clients → + → qBittorrent
| Field | Value |
|---|---|
| Host | qbittorrent |
| Port | 8080 |
| Username / Password | What you set in step 3 |
| Category | tv for Sonarr, movies for Radarr |
Container name and internal port — not the host IP.
Running it behind a VPN
If you want the torrent traffic tunnelled, the pattern is gluetun as a network namespace:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
- SERVER_CITIES=Amsterdam
ports:
- 8080:8080
- 6881:6881
- 6881:6881/udp
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
depends_on:
- gluetun
environment:
- PUID=99
- PGID=100
- UMASK=022
- TZ=Europe/London
- WEBUI_PORT=8080
volumes:
- /mnt/user/appdata/qbittorrent:/config
- /mnt/user/data/torrents:/data/torrents
restart: unless-stopped
Note what changed: qBittorrent has no ports: block at all, because it has no network stack — gluetun publishes on its behalf. Sonarr and Radarr must then point at gluetun, not qbittorrent, since that is where the port lives.
Verify the tunnel actually works before trusting it:
That should return the VPN's exit IP, not yours. If it returns your home IP, the tunnel is not up and you should stop downloading until it is.
Updating
qBittorrent occasionally changes the config format
Major version jumps have historically reset WebUI settings or changed defaults. Back up /config before a big upgrade, and read the LinuxServer changelog if the tag has moved a whole version.
Backup
docker compose stop qbittorrent
sudo tar czf /mnt/user/backups/qbittorrent-$(date +%F).tar.gz -C /mnt/user/appdata qbittorrent
docker compose start qbittorrent
/config includes BT_backup, which holds the .torrent and .fastresume files for everything you are seeding. Lose that and you lose your seeding history — which on a private tracker is a real cost.
Troubleshooting
Cannot log in on a fresh install. Temporary password in the logs, step 2.
Torrents stuck at "stalled". No incoming connections. Check the port is forwarded, or if you are on a VPN, that the provider supports port forwarding — many do not, and downloads still work but slowly.
Downloads finish but Sonarr never imports. Category mismatch, nine times out of ten. Compare the category string in qBittorrent against the one in Sonarr's download client settings, character for character.
"Permission denied" writing to the download folder. PUID/PGID. chown -R 99:100 /mnt/user/data/torrents.
WebUI unreachable after adding gluetun. Expected — the port moved to the gluetun container. Reach it at gluetun's address, and update the *arr apps to point at gluetun.
Everything is slow and the array is thrashing. Torrents seeding directly from the array. Either accept it, or move the incomplete folder to a cache pool so only completed files touch the disks.
Ratio is stuck at 0 on a private tracker. The announce is failing. Check the tracker tab on a torrent for the error — usually an expired passkey after you re-added the indexer in Prowlarr.
Where this sits in my lab
qBittorrent runs on the Unraid box with the rest of the stack, categories matching the folder layout, and a global upload cap so nobody in the house notices it exists.
It handles the releases that Usenet does not have — mostly older films and anything niche. SABnzbd does the heavy lifting for everything current, because Usenet is faster and does not need seeding.