11 KiB
type: reference title: *arr Stack description: The *arr automation stack (Prowlarr, Sonarr, Radarr, Lidarr, Readarr, qBittorrent) — FrankBook-Pro quadlets and the Dankserver compose deployment (reactivated 2026-09-04): topology, wiring, auth posture, and gotchas. tags: [arr-stack, prowlarr, sonarr, radarr, lidarr, readarr, qbittorrent, frankbook-pro, dankserver] timestamp: 2026-09-04T00:00:00Z
*arr Stack
The *arr suite is a collection of automated media management tools for Usenet and BitTorrent. The stack runs in two places: FrankBook-Pro — the Mac mini that fronts this household's downloads — as quadlet units inside its podman machine (the documented wiring below), and on Dankserver as a compose stack behind six Caddy vhosts (reactivated 2026-09-04; see below). It provides automatic downloading and library management that Copyparty alone does not cover: quality profile enforcement, series/movie discovery, metadata enrichment, and automated file organization.
The core chain is: Prowlarr (indexer API aggregator) → Sonarr/Radarr/Lidarr/Readarr (media-specific managers) → qBittorrent (download client, sharing the Gluetun WireGuard tunnel netns) → Jellyfin (media server scan → auto-refresh).
Service Overview
| Service | Role | Image | Port | Min RAM |
|---|---|---|---|---|
| Prowlarr | Indexer management | lscr.io/linuxserver/prowlarr:latest |
9696 | 256 MB |
| Radarr | Movie management | lscr.io/linuxserver/radarr:latest |
7878 | 512 MB |
| Sonarr | TV show management | lscr.io/linuxserver/sonarr:latest |
8989 | 512 MB |
| Lidarr | Music management | lscr.io/linuxserver/lidarr:latest |
8686 | 256 MB |
| Readarr | Ebook/audiobook management | lscr.io/linuxserver/readarr:amd64-1.0.980 (pinned — LSIO ships no untagged latest for readarr) |
8787 | 256 MB |
| qBittorrent | Torrent download client | lscr.io/linuxserver/qbittorrent:4.6.7 (pinned; see qBittorrent) |
8080 (published by the gluetun unit) | 512 MB |
Jackett and Whisparr were evaluated as optional extras but never deployed (no unit, no vhost, no live container).
Deployment shape (current)
Each service is a quadlet .container unit in this repo under
frankbook-pro/<svc>/quadlet/, installed inside the podman machine at
/home/core/.config/containers/systemd/<svc>.service by the frankbook-pro
handler in scripts/deploy-remote.sh. Every unit runs with
PUID=0 PGID=0 TZ=America/Los_Angeles UMASK_SET=022, mounts its config root at
/Volumes/COMPLETE/<svc>-data:/config, publishes its API port on the host, and
runs Restart=always with TimeoutStartSec=900.
qBittorrent is special: it shares the Gluetun container's network namespace
(Network=gluetun.container) so torrent egress never touches the residential
IP. A netns-sharer publishes no ports of its own — qB's WebUI (8080) and
BitTorrent port (6881 TCP+UDP) are published on the gluetun unit instead.
Dankserver compose deployment (reactivated 2026-09-04)
The original Dankserver compose stack (dankserver/compose.yaml,
/opt/<svc>/config data roots) was committed 2026-08-29 but never booted:
the config dirs were created root-owned (root:root 755), and every app
crashed at logger init (UnauthorizedAccessException: Access to the path '/config/Sentry' is denied) before binding any port — which read as a dead
port, not a boot crash, until podman logs <svc> was checked.
Fix and restart (2026-09-04): sudo chown -R 1000:1000 /opt/{prowlarr,qbittorrent,radarr,sonarr,lidarr,readarr}/config, then
podman-compose -f dankserver/compose.yaml up -d for the six services. All
six now serve 200 on their ports and through the Caddy vhosts.
Rootless identity model (verified, do not "fix" it)
- On Dankserver's podman 5.7.0 rootless, a container's default user namespace
maps container root (PUID=0) → host uid 1000 (dank) and container uid
N → host
100000 + N - 1. Verified via/proc/<pid>/uid_map(0 1000 1/1 100000 65536) across plex, tautulli, jellyfin, calibre-web, copyparty, romm, and the arr stack. This is why PUID=0 services write asdankand why data roots are owned1000:1000. - Never add
userns_mode: hostto compose services here. podman-compose 1.5.0 runs every compose service inside a pod, and podman rejects the combination (--userns and --pod cannot be set together) — the stack then fails to recreate with no recourse. The default mapping already gives the fleet identity; PUID=0 +1000:1000-owned data roots is the whole recipe. - qBittorrent's first boot (PUID=0, empty config) prints a one-time WebUI
admin password in
podman logs qbittorrent— persist it via the PBKDF2 recipe in qBittorrent before restarting the container.
Differences from the FrankBook-Pro deployment
- No gluetun on Dankserver: this qBittorrent's torrent egress leaves via the residential IP (the FrankBook qB is the VPN-gated one). Route privacy-sensitive grabs through FrankBook-Pro.
- Empty config: the Dankserver arrs have no indexers, no qB download clients, and no quality profiles configured — the Chain wiring section below describes FrankBook-Pro. Configure test-first per that section if wiring this stack up.
- Six vhosts:
prowlarr/sonarr/radarr/lidarr/readarr/qbittorrent .franksdank.cloudare exempt from the Anubis chain (own auth) with a documented exemption reason per the guard policy. - Homepage: Sonarr sits in the user section; Prowlarr/Radarr/Readarr/Lidarr/ qBittorrent sit in admin.
Historical note
The compose file briefly carried userns_mode: host on all six services
(2026-09-04, reverted same day in e454159) — an incorrect fix for the boot
crash that podman-compose 1.5.0 could not apply anyway (pod/userns conflict
above). The real fix was the chown; see the identity model section.
Access / auth posture
No *arr app sits behind the Anubis guard chain — the stack is exempt by
construction (its own auth), with an exemption comment in the Caddyfile per
vhost. FrankBook-Pro's stack is reached directly by IP:port on the LAN
(http://<frankbook-pro>:8989 etc.); the Dankserver stack additionally has
six TLS vhosts (<svc>.franksdank.cloud) that terminate on Caddy on Dankclaw
and route to Dankserver's published ports.
Auth-config history (verified live 2026-08-29): all five arrs shipped with
AuthenticationMethod=None plus AuthenticationRequired=Enabled. On the
v1 (.NET) apps that combo is self-contradictory — UI Save is permanently
disabled and any settings save throws DryIoc.ScopedItemException inside
AuthorizationMiddleware (500 sprawl on every page after restart). The valid
representation of "no auth" in v1 is absence of the <AuthenticationRequired>
key, not the literal string Disabled — readarr's config (key absent) booted
clean while its three siblings (key=Disabled) 500-sprawled, which is how it
was caught. Fix applied live: key deleted on sonarr/radarr/lidarr/prowlarr,
containers restarted, all five verified serving. Posture = zero-auth arrs on
LAN and WAN; the Anubis wall guards homepage/search/hermes/prometheus/romm/files
but not the arrs — first-account-wins is the current race condition if that
ever flips to External auth (needs an IdP first; auth method choice remains an
operator decision).
Chain wiring (verified live 2026-08-30)
The download chain is wired and test-first verified: indexers → Prowlarr → per-arr qBittorrent clients (category = app name) → import.
- qB webUI auth is ON (
admin+ generated password; verify before writing it into any config — it was generated at first boot, not a default). - Per-arr QBittorrent download clients exist on sonarr/radarr/lidarr (enabled) and readarr (parked: its qB proxy fails auth live even with correct creds — known defect of the abandoned upstream; do not enable until the image changes). Each was tested before enabling; failures stay parked with a reason, never enabled blind.
- Prowlarr runs v2.5.2 where apps are first-class rows (
Applicationstable): four Application rows (sonarr/radarr/lidarr/readarr) now carry each arr's real baseUrl+apiKey withSyncLevel=FullSync. Before this, the table was empty and every sync attempt threwNullConfiginGetIndexerMappings()— visible inpodman logs prowlarr, not in any UI. - Indexers are test-before-enable: EZTV and 1337x stay disabled with reason "blocked by CloudFlare Protection" (this host's egress ASN is CF-blocked at those trackers); Nyaa.si, sukebei, RuTor, Byrutor, YTS passed live tests and are enabled. A failed test = parked with reason, never enabled blind.
- Prowlarr v2 sync model: indexers stay in Prowlarr; arrs search through it and hand off to their own qB download client (category routing). Mappings materialize at grab time — zero mappings right after wiring is expected, not a fault.
Disk layout (current)
/Volumes/COMPLETE <- completed-media volume (Mac volume)
├── sonarr-data:/config <- per-app config roots, one per unit
├── radarr-data:/config
├── lidarr-data:/config
├── readarr-data:/config
├── qbittorrent-data:/config
└── <movie/tv/music/books roots> <- arr import roots live here
/Volumes/INCOMPLETE <- qB in-flight downloads (same path inside qB)
qB mounts both volumes at their identical host paths
(Volume=/Volumes/COMPLETE:/Volumes/COMPLETE,
Volume=/Volumes/INCOMPLETE:/Volumes/INCOMPLETE), so a category's download path
is the same string on every container that touches it — no remapping between
download and import.
Gotchas
- Timezone: every unit runs
TZ=America/Los_Angeles; Sonarr's scheduler is timezone-sensitive — keep them uniform. - File permissions:
UMASK_SET=022on qBittorrent; arr apps run PUID=0 so they can read/move qB downloads. - Network conflicts: *arr apps use many ports. On the machine VM, only the API ports (8989/7878/8686/8787/9696) and gluetun's published 8080 + 6881 are host-published; netns-sharers (qB) publish none of their own.
- Prowlarr API key rotation: If rotated, update it in every arr app (
Applicationstable rows carry the real keys). - qBittorrent WebUI password: generated at first boot, not a default — persist it via the PBKDF2 conf recipe in qBittorrent before any restart.
- Readarr stability: abandoned upstream (retired May 2026); its qB download client is parked pending a successor image. Do not rely on it for mission-critical collections.
Best Practices
- Use tags to route content to specific download clients or categories.
- Enable the Blocklist in arr apps to prevent re-downloading failed torrents.
- Set download limits in qBittorrent to avoid bandwidth saturation.
- Back up arr configs regularly (they are small SQLite DBs + JSON) — each lives at
/Volumes/COMPLETE/<svc>-data/config. - Monitor Prowlarr indexer health — broken indexers hide new content.
- Start with default quality profiles. Add custom formats gradually.