Files
serverAppConfigs/serverAppConfigs-docs/Dankserver/ARR-Stack.md
T

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 as dank and why data roots are owned 1000:1000.
  • Never add userns_mode: host to 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.cloud are 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 (Applications table): four Application rows (sonarr/radarr/lidarr/readarr) now carry each arr's real baseUrl+apiKey with SyncLevel=FullSync. Before this, the table was empty and every sync attempt threw NullConfig in GetIndexerMappings() — visible in podman 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=022 on 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 (Applications table 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

  1. Use tags to route content to specific download clients or categories.
  2. Enable the Blocklist in arr apps to prevent re-downloading failed torrents.
  3. Set download limits in qBittorrent to avoid bandwidth saturation.
  4. Back up arr configs regularly (they are small SQLite DBs + JSON) — each lives at /Volumes/COMPLETE/<svc>-data/config.
  5. Monitor Prowlarr indexer health — broken indexers hide new content.
  6. Start with default quality profiles. Add custom formats gradually.

See Also