Files
serverAppConfigs/serverAppConfigs-docs/Dankserver/qbittorrent.md
T
Frank 8212d4451a docs: rename frank-default-large/small/sightsound to frank-large/small/npu across vault
Mechanical identity rename across the knowledge base, AGENTS.md, README, the
hermes config example, and homepage services.json. No behavior change; served
aliases on the LLM endpoints change with the deploy of commit 69f87c8.
2026-09-03 16:17:14 -07:00

118 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: reference
title: qBittorrent
description: qBittorrent download client for the *arr stack — pinned 4.6.7 on FrankBook-Pro, shares the Gluetun WireGuard netns, PBKDF2 WebUI credential recipe.
tags: [qbittorrent, arr-stack, frankbook-pro, gluetun]
timestamp: 2026-09-03T00:00:00Z
---
# qBittorrent
qBittorrent is the torrent download client for the *arr stack. The stack runs on
**FrankBook-Pro** (the Mac mini) as quadlet units inside its podman machine —
not on Dankserver. It receives download requests from Sonarr, Radarr, Lidarr,
and Readarr via category tagging.
**Host:** FrankBook-Pro (podman machine) — **Access:** WebUI on port `8080`,
published by the [[Gluetun]] unit because qBittorrent shares that container's
network namespace (a netns-sharer publishes no ports of its own).
## Facts
| Item | Value |
|------|-------|
| Image | `lscr.io/linuxserver/qbittorrent:4.6.7` — **pinned**, see the pin rationale below |
| Network | `Network=gluetun.container` (torrent egress exits via the WireGuard/ProtonVPN tunnel) |
| Ports | 8080 (WebUI) and 6881 TCP+UDP published on the gluetun unit |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Volumes | `/Volumes/COMPLETE/qbittorrent-data:/config`, plus `/Volumes/COMPLETE` and `/Volumes/INCOMPLETE` mounted at identical paths |
| Auth | Web UI username/password (`admin` + generated password); LAN bypass not used |
## Why the 4.6.7 pin exists (2026-09-01)
Readarr's abandoned qBittorrent client rejects qB ≥ 5's **204 empty-body login
success** as `Authentication failure` — a runtime break, not just an API-create
issue. The fleet pins `lscr.io/linuxserver/qbittorrent:4.6.7` while Readarr is
in the stack. **Never unpin to `latest` while Readarr is in the fleet.**
Second trap on any version flip: qB 5.x's shutdown conf rewrite leaves a
`WebUI\Password_PBKDF2=@ByteArray(...)` line that 4.6.7 rejects — every login
then returns "invalid credentials" (the arrs included). Fix = rewrite the same
conf line with the same password while qB is stopped.
## Persistent WebUI credentials (verified 2026-08-30)
qBittorrent 5 mints a **new temporary password at every start** unless a
password is stored in `qBittorrent.conf`. That rotation silently breaks every
arr download client after each reboot. The Web-API has no password endpoint
any more (`/api/v2/app/setPassword` was removed), and the linuxserver image
has no `WEBUI_AUTH` handling — so persistence must be written to the conf
directly, in qb's own `salt:hash` format (`src/base/utils/password.cpp`:
PBKDF2-HMAC-SHA512, 100 000 iterations, 16-byte salt = 4×uint32, then
`base64(salt) + ":" + base64(hash)` as the raw value):
```python
import base64, hashlib, os
salt = os.urandom(16)
h = hashlib.pbkdf2_hmac("sha512", password.encode(), salt, 100000, 64)
val = (base64.b64encode(salt) + b":" + base64.b64encode(h)).decode()
# with the container STOPPED, in [Preferences]:
# WebUI\Username=admin
# WebUI\Password_PBKDF2=<val>
```
Then `podman start qbittorrent` and confirm login survives a
`podman restart` — the temp-password rotation stops the moment stored
credentials verify. The chosen value lives host-local in `/home/dank/.qbpass`
(mode 600, owner `dank`) — never in git; every arr download client and
[[Prowlarr]] reference it. (The classic `@ByteArray(salt+hash)` form is WRONG
for qb5: login 401s on it; verify() base64-decodes the two halves separately.)
## Deployment (current)
Quadlet unit `qbittorrent.container` from this repo
(`frankbook-pro/qbittorrent/quadlet/`), installed inside the podman machine at
`/home/core/.config/containers/systemd/qbittorrent.service` by the
frankbook-pro handler in `scripts/deploy-remote.sh`:
```ini
[Container]
ContainerName=qbittorrent
Image=lscr.io/linuxserver/qbittorrent:4.6.7
Network=gluetun.container
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/qbittorrent-data:/config
Volume=/Volumes/COMPLETE:/Volumes/COMPLETE
Volume=/Volumes/INCOMPLETE:/Volumes/INCOMPLETE
[Service]
TimeoutStartSec=900
Restart=always
```
## Gotchas (verified live)
- Temporary-password rotation on every boot is the default state of an
untouched qb5 — see the persistence recipe above; without it, all arr
download clients silently fail queue adds ("Couldn't add release … to
download queue" in the arr logs) after any restart.
- API login from `curl` needs a `Referer: http://<host>:<port>/` header
(CSRF protection) or POSTs get rejected even with a valid SID.
- Cookie-name quirk by version: qB 5.x names its WebUI session cookie
`QBT_SID_<port>`; 4.6.7 uses plain `SID`. Any automation must parse the
cookie generically (e.g. `grep -oE '[A-Za-z_]*SID[A-Za-z0-9_]*=[^;[:space:]]*'`)
instead of hard-coding a port-suffixed name, or it fails with 403 cascades
that never reach qB as failed logins.
- Egress verification for this stack must run **inside the gluetun container**
(`podman exec gluetun wget -qO- https://ipinfo.io/json`); running the same
probe one ssh-context out returns the residential IP — a false leak alarm.
- Ensure umask is set to `022` so downloaded files are readable by arr apps.
## See Also
- [[ARR-Stack]] — full *arr stack documentation
- [[Gluetun]] — the WireGuard tunnel this container shares