# *arr Stack
The *arr suite is a collection of automated media management tools for Usenet and BitTorrent. For Dankserver — which runs Copyparty, Calibre-Web, RomM, and Jellyfin — the *arr stack 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) → **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:latest` | 8787 | 256 MB |
| qBittorrent | Torrent download client | `lscr.io/linuxserver/qbittorrent:latest` | 8080 | 512 MB |
| Jackett | Indexer proxy (optional) | `lscr.io/linuxserver/jackett:latest` | 9117 | 256 MB |
| Whisparr | NSFW movie management (optional) | `lscr.io/linuxserver/whisparr:latest` | 6969 | 256 MB |
**Minimum total RAM:** ~3 GB for full stack (headroom recommended).
## Architecture
```mermaid
graph LR
I[Indexers
Torrents, Usenet] --> P[Prowlarr
Indexer Manager]
P --> S[Sonarr
TV]
P --> R[Radarr
Movies]
P --> L[Lidarr
Music]
P --> Rd[Readarr
Books]
S --> Q[qBittorrent]
R --> Q
L --> Q
Rd --> Q
Q --> M1[/tv/]
Q --> M2[/movies/]
Q --> M3[/music/]
Q --> M4[/books/]
M1 --> J[Jellyfin]
M2 --> J
M3 --> J
M4 --> CW[Calibre-Web]
```
## Docker Compose
```yaml
networks:
appnet:
driver: bridge
services:
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/prowlarr/config:/config
- /mnt/media/downloads:/downloads:ro
ports:
- "9696:9696"
networks: [appnet]
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/radarr/config:/config
- /mnt/media/movies:/movies
- /mnt/media/downloads:/downloads
ports:
- "7878:7878"
networks: [appnet]
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/sonarr/config:/config
- /mnt/media/tv:/tv
- /mnt/media/downloads:/downloads
ports:
- "8989:8989"
networks: [appnet]
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/lidarr/config:/config
- /mnt/media/music:/music
- /mnt/media/downloads:/downloads
ports:
- "8686:8686"
networks: [appnet]
readarr:
image: lscr.io/linuxserver/readarr:latest
container_name: readarr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/readarr/config:/config
- /mnt/media/books:/books
- /mnt/media/downloads:/downloads
ports:
- "8787:8787"
networks: [appnet]
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/qbittorrent/config:/config
- /mnt/media/downloads:/downloads
- /mnt/media/downloads/incomplete:/downloads/incomplete
ports:
- "8080:8080"
networks: [appnet]
# Optional: Jackett for niche indexers
jackett:
image: lscr.io/linuxserver/jackett:latest
container_name: jackett
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/jackett/config:/config
- /opt/jackett/torrences:/torrences
ports:
- "9117:9117"
networks: [appnet]
# Optional: Whisparr for adult content
whisparr:
image: lscr.io/linuxserver/whisparr:latest
container_name: whisparr
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- UMASK_SET=022
volumes:
- /opt/whisparr/config:/config
- /mnt/media/whisparr-movies:/whisparr-movies
- /mnt/media/downloads:/downloads
ports:
- "6969:6969"
networks: [appnet]
```
## Caddy Configuration
```caddy
radarr.dankserver.local {
reverse_proxy localhost:7878
}
sonarr.dankserver.local {
reverse_proxy localhost:8989
}
lidarr.dankserver.local {
reverse_proxy localhost:8686
}
readarr.dankserver.local {
reverse_proxy localhost:8787
}
prowlarr.dankserver.local {
reverse_proxy localhost:9696
}
qbittorrent.dankserver.local {
reverse_proxy localhost:8080
}
# Optional: Jackett
jackett.dankserver.local {
reverse_proxy localhost:9117
}
# Optional: Whisparr
whisparr.dankserver.local {
reverse_proxy localhost:6969
}
```
## Configuration Steps
### Phase 1: Download Client
1. Deploy qBittorrent first (or Transmission).
2. In the qBittorrent Web UI, set **No auth mode** for LAN (Settings → Web UI).
3. Create download categories: `movies`, `tv`, `music`, `books`.
4. Change the default Web UI password.
### Phase 2: Indexer Management
1. Deploy Prowlarr.
2. Add indexers (3–5 recommended for testing). Use the **Test** button on each.
3. Add qBittorrent as a download client in Prowlarr.
4. Verify search from the Prowlarr web UI.
### Phase 3: Arr Apps (one at a time)
1. Deploy Sonarr first (TV shows are the most common use case).
2. Add Prowlarr as the indexer source (Settings → Indexers → Add Prowlarr).
3. Add qBittorrent as the download client with categories.
4. Add a test series. Wait for the episode to download. Verify in `/tv/`.
5. Repeat for Radarr, Lidarr, Readarr.
### Phase 4: Media Server Integration
1. Configure Jellyfin libraries pointing to `/movies`, `/tv`, `/music`, `/books`.
2. Set **On Download** notifications in each arr app → Jellyfin webhook URL.
3. Test end-to-end: Add a movie in Radarr → download → verify in Jellyfin.
## Quality Profiles
Each arr app uses a hierarchical quality system:
1. **Quality Profile** — Defines tiers (e.g., 2160p → 1080p BluRay → 720p).
2. **Cutoff** — The quality target. Downloads at or above cutoff are accepted.
3. **Custom Formats** — Additional filters (e.g., "Has HDR", "Is Director's Cut").
4. **Search on Grab** — When content becomes available, all indexers are queried.
### Radarr (Movies)
```
Profile: Movies 4K
Tiers:
1. 2160p BluRay REMUX
2. 2160p BluRay
3. 2160p WEB-DL
4. 1080p BluRay
5. 1080p WEB-DL
6. 720p
Cutoff: 1080p BluRay
```
### Sonarr (TV)
```
Profile: TV - Standard
Tiers: 2160p → 1080p → 720p
Min Size: 500 MB
Max Size: 50 GB
Cutoff: 1080p
Language Profile: English audio preferred
```
### Lidarr (Music)
```
Profile: Music - Standard
Tiers: Lossless (FLAC) → 320 kbps → 192 kbps
Cutoff: 320 kbps (or Lossless)
```
## Metadata Sources
| App | Primary Source | Secondary |
|-----|---------------|-----------|
| Radarr | TMDB | TheMovieDB API |
| Sonarr | TMDB | TVDB |
| Lidarr | MusicBrainz | — |
| Readarr | Goodreads, StoryGraph | — |
| Whisparr | XH | TMDB |
## *arr Stack vs Copyparty
| Feature | Copyparty | *arr Stack |
|---------|-----------|------------|
| Auto-download from torrents | Basic (RSS + scripts) | Native, integrated |
| Quality profiles | No | Multi-tier, custom formats |
| Series/movie tracking | No | Watch lists, air dates |
| Manual library management | Yes | Yes, metadata-driven |
| Multi-user watchlists | No | Yes |
| Complexity | Low | Medium |
| Resource usage | Minimal | Moderate |
Use Copyparty for curated file collections. Use *arr for automatic downloads. Use both together for maximum functionality — point *arr download directories at Copyparty's watched folders.
## Live State (as-built, verified 2026-08-29)
Deployed shape differs from the design compose above — reality wins:
- Mounts are the real array paths (`/mnt/entropy/downloads`, case-exact dirs
like `Movies`/`TVShows`), not `/mnt/media/*`; TZ is `America/Los_Angeles`.
- Readarr runs `lscr.io/linuxserver/readarr:amd64-1.0.980` — LSIO ships no
untagged `latest` for readarr; the tag was pinned after a manifest probe.
- Prowlarr's `/config` mount was missing mid-deploy (off-by-one in an edit);
restored in commit `67695cf`.
- **Auth posture: every arr runs `AuthenticationMethod=None` +
`AuthenticationRequired=Enabled`** — verified live via each config.xml and by
probing `/api/v3/system/status` unauthenticated (401 on all five, local and
through the vhost). Net effect: a login screen with **zero users defined**;
whoever creates the first account owns the instance. Decide the auth method
(`None` behind the Anubis wall vs `External` via authentik) *before* WAN
exposure — first-account-wins is the current race condition.
- API keys live in each `/config/config.xml` (``); they are local-only
secrets and stop being secret the moment a vhost serves the API unauthenticated.
### Chain wiring (verified live 2026-08-29)
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.
**Chain complete (same day):** all four arr roots live — Sonarr `/tv`,
Radarr `/movies` (201), and Lidarr `/music` + Readarr `/books` created via
API after the v1 validator layers peeled back one at a time
(`defaultQualityProfileId`/`defaultMetadataProfileId` must reference an
*existing* profile id on v1 apps; v1 has no `/api/v1/profile` endpoint —
probe endpoints by status code, empty body = absent).
**Auth-config pitfall (found same day):** 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 `` 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 — if WAN exposure of `/sonarr`, `/radarr` etc. is
undesired, guard them in the Anubis policy or flip auth to `External`
(needs an IdP first; auth method choice remains an operator decision).
## Disk Layout
```
/mnt/media/
├── movies/ → Radarr root
├── tv/ → Sonarr root
├── music/ → Lidarr root
├── books/ → Readarr root
├── whisparr-movies/ → Whisparr root
├── downloads/ → qBittorrent download dir
│ ├── movies/
│ ├── tv/
│ ├── music/
│ └── books/
└── incomplete/ → qBittorrent incomplete downloads
```
## Gotchas
- **Timezone mismatch:** All containers should use the same TZ. Sonarr's scheduler is timezone-sensitive.
- **File permissions:** Set `umask=022` in qBittorrent. Ensure arr apps can read/move downloaded files.
- **Network conflicts:** *arr apps use many ports. Check for conflicts with existing Dankserver services.
- **Prowlarr API key rotation:** If rotated, update it in every arr app.
- **qBittorrent Web UI password:** Change default on first run.
- **Readarr stability:** Readarr is still in development. Do not rely on it for mission-critical collections.
- **Whisparr separate Jellyfin library:** Keep adult content separate from regular media.
- **RSS feed in qBittorrent:** Can auto-add torrents matching a pattern. Useful for podcast-like series.
## 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).
5. Monitor Prowlarr indexer health — broken indexers hide new content.
6. Start with default quality profiles. Add custom formats gradually.
7. Back up arr configs: `./radarr/config`, `./sonarr/config`, etc.
## See Also
- [[Dankserver Stack]]
- [[Jellyfin]] — media server that consumes *arr downloads
- [[Copyparty]] — alternative file viewer
- [[Calibre-Web]] — manual ebook management (Readarr auto-downloads into the same library)
- [Sonarr Wiki](https://wiki.servarr.com/sonarr)
- [Radarr Wiki](https://wiki.servarr.com/radarr)