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.
This commit is contained in:
Frank
2026-09-03 16:17:14 -07:00
parent 5a083eee6c
commit 8212d4451a
68 changed files with 1053 additions and 1207 deletions
+84 -369
View File
@@ -1,8 +1,24 @@
---
type: reference
title: *arr Stack
description: The *arr automation stack (Prowlarr, Sonarr, Radarr, Lidarr, Readarr, qBittorrent) on FrankBook-Pro — topology, wiring, auth posture, and gotchas.
tags: [arr-stack, prowlarr, sonarr, radarr, lidarr, readarr, qbittorrent, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# *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 *arr suite is a collection of automated media management tools for Usenet
and BitTorrent. The stack runs on **FrankBook-Pro** — the Mac mini that fronts
this household's downloads — as quadlet units inside its podman machine, not on
Dankserver. 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) → **Jellyfin** (media server scan → auto-refresh).
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
@@ -12,334 +28,59 @@ The core chain is: **Prowlarr** (indexer API aggregator) → **Sonarr/Radarr/Lid
| 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 |
| 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 |
**Minimum total RAM:** ~3 GB for full stack (headroom recommended).
Jackett and Whisparr were evaluated as optional extras but never deployed
(no unit, no vhost, no live container).
## Architecture
## Deployment shape (current)
```mermaid
graph LR
I[Indexers<br/>Torrents, Usenet] --> P[Prowlarr<br/>Indexer Manager]
P --> S[Sonarr<br/>TV]
P --> R[Radarr<br/>Movies]
P --> L[Lidarr<br/>Music]
P --> Rd[Readarr<br/>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]
```
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`.
## Docker Compose
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.
```yaml
networks:
appnet:
driver: bridge
## Historical design-era compose (retired)
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]
The original design deployed the stack on Dankserver via
`dankserver/compose.yaml` with `/opt/<svc>/config` data roots and
`TZ=America/New_York`. That layout is retired; `dankserver/compose.yaml` is
kept as reference only. Reality (unit files above) wins: data roots are the
Mac's `/Volumes/COMPLETE` (completed media) and `/Volumes/INCOMPLETE`
(in-flight downloads) volumes, mounted into qB at identical paths, and every
unit runs `TZ=America/Los_Angeles`.
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]
## Access / auth posture
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]
There is **no Caddy vhost for any arr app** — the *arr stack is exempt by
construction from the Anubis guard chain (its own auth). The stack is reached
directly by IP:port on the LAN (`http://<frankbook-pro>:8989` etc.).
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]
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).
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` (`<ApiKey>`); they are local-only
secrets and stop being secret the moment a vhost serves the API unauthenticated.
### Chain wiring (verified live 2026-08-29)
## 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**.
@@ -364,72 +105,46 @@ per-arr qBittorrent clients (category = app name) → import**.
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 `<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 — 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
## Disk layout (current)
```
/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
/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 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.
- **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).
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.
7. Back up arr configs: `./radarr/config`, `./sonarr/config`, etc.
## See Also
- [[Dankserver Stack]]
- [[Sonarr]] · [[Radarr]] · [[Lidarr]] · [[Readarr]] · [[Prowlarr]] · [[qBittorrent]] — per-service pages
- [[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)
@@ -2,14 +2,16 @@
Calibre-Web is a web application that browses, reads, and downloads eBooks from an existing Calibre library database. It serves as the web frontend for the Calibre e-book manager.
**Host:** Dankserver — **Hostname:** `books.dankserver.local` — **Docs:** https://github.com/linuxserver/docker-calibre-web
**Host:** Dankserver — **Access:** `books.franksdank.cloud` (unguarded Caddy
vhost — Calibre-Web serves its own login; direct proxy to
`192.168.0.235:8083`) — **Docs:** https://github.com/linuxserver/docker-calibre-web
## Facts
| Item | Value |
|------|-------|
| Latest version | 0.6.25-ls365 (live); 0.6.27-ls397 upstream |
| Image | `lscr.io/linuxserver/calibre-web:latest` (live: 0.6.25-ls365) |
| Running image | `lscr.io/linuxserver/calibre-web:0.6.27-ls397` — **pinned** (verified from the live quadlet unit; the pin was chosen so the cutover changed zero bits of application code) |
| Network | `pasta` (the default rootless-podman userslide network); only 8083 is host-published |
| Base OS | Ubuntu Noble (24.04 LTS, since 2024-12-05) |
| Port | 8083 (host) → 8083 (container) |
| Default login | `admin` / `admin123` |
@@ -34,7 +36,7 @@ Additional packages added: `libxfixes3` and `libasound2t64` to support epub-to-p
|----------|-------|-------------|
| `PUID` | `1000` | User ID |
| `PGID` | `1000` | Group ID |
| `TZ` | `Etc/UTC` | Timezone |
| `TZ` | `America/Vancouver` (live unit value — not `Etc/UTC`) |
| `DOCKER_MODS` | `linuxserver/mods:universal-calibre` | Optional: enables ebook conversion tools |
| `OAUTHLIB_RELAX_TOKEN_SCOPE` | `1` | Required for Google OAuth |
+9 -4
View File
@@ -25,16 +25,21 @@ Calibre 9.2.1 is the version on the live host (9.9.0 is the upstream latest). Ma
## Library Directory
| Purpose | Host path (convention) |
|---------|------------------------|
| Calibre library | `/opt/calibre-web/books` (bind-mounted to `/books` in the Calibre-Web container) |
The live Calibre-Web container bind-mounts the library at `/books`:
| Purpose | Host path (live) |
|---------|------------------|
| Calibre library | `/home/dank/Containers/calibre-web/books` → `/books` in the Calibre-Web container |
(The older `/opt/calibre-web/books` path was the compose-era location and is
gone; the quadlet unit mounts the Containers data root.)
## What the Container Needs
- Read/write access to the library directory. Calibre-Web edits metadata through the database file.
- Ownership matching `PUID=1000` / `PGID=1000` (the `dank` user on Dankserver).
## Optional: Calibre Content Server
## Optional: Calibre Content Server (not running)
Calibre includes a built-in HTTP content server (`calibre-server`) that can serve the library via OPDS feeds and a mobile-friendly web interface. This enables:
- OPDS feeds for mobile reading apps (FBReader, Moon+ Reader)
+16 -13
View File
@@ -2,14 +2,16 @@
Copyparty is a lightweight file server and media streamer that runs as a single Python binary. It handles large payloads, media streaming, resumable uploads, directory browsing, and WebDAV from one process.
**Host:** Dankserver — **Hostname:** `files.dankserver.local` — **Docs:** https://github.com/9001/copyparty
**Host:** Dankserver — **Access:** `files.franksdank.cloud` guarded by the
Anubis → Authentik chain ([[Caddy]] routes it post-auth to
`192.168.0.235:3923`); the published port itself stays unauthenticated on the
LAN — **Docs:** https://github.com/9001/copyparty
## Facts
| Item | Value |
|------|-------|
| Latest version | v1.20.21 (2026-08) |
| Image | `ghcr.io/9001/copyparty-ac:latest` (ac = full edition; the bare `copyparty:latest` tag does not exist — ghcr answers 403 on the token request for nonexistent repos) |
| Running image | `ghcr.io/9001/copyparty-ac:latest` (ac = full edition; the bare `copyparty:latest` tag does not exist — ghcr answers 403 on the token request for nonexistent repos). Version at research time (2026-08): v1.20.21 |
| Port | 3923 (host) → 3923 (container) |
| Volume | bind mount the shared root directory |
| Auth | Per-directory `.cpy` config files, HTTP basic auth, `dirkeys`, `filekeys` |
@@ -72,13 +74,12 @@ The `iv` edition is no longer available for arm32/armv6. Use `dj` for maximum co
## Volumes
Live deployment: the whole LUKS array is served at URL `/w` (the ac edition
takes its volume roots from argv plus `chdir: /w` in `/z/initcfg`, not from a
`/data` convention). Access is **read-only since 2026-08-31** (`-v /w::r:ra,dank`
— see [[#Command-line interface (ac edition)]] above). The compose bind mount is
`/mnt/entropy:/w` (host path → container path). Note the bind itself is still
rw on disk — copyparty enforces read-only at the application layer, so local
services (Jellyfin, Calibre) keep writing into the array directly.
Live deployment: the whole LUKS array is served at URL `/w` via
`Volume=/mnt/entropy:/w:ro` in the quadlet unit ([[Entropy Volume]]) —
read-only **at the mount level**, so no containerized consumer can write into
the array through this path regardless of account permissions. The ac edition
takes its volume roots from argv (`-v /w::r:ra,dank`), not from a `/data`
convention; see [[#Command-line interface (ac edition)]] above.
## Docker Compose (reference — retired 2026-09-01; live deployment is the quadlet unit below)
@@ -146,13 +147,15 @@ Upgrade to v1.20.19+ to cover all known CVEs. The dirkey fix (v1.20.17) matters
- **Large transfers:** Disable TLS inside the app and let Caddy terminate TLS. This avoids double encryption overhead.
- **Dedicated share directory:** Do not give Copyparty the whole disk. Mount only the intended share path.
- **Whole-disk share (live choice):** this deployment *does* serve the whole
91 T array from `/w`. Since 2026-08-31 the volume is application-level
read-only for every account, so no remote write path exists. But note:
~91 T array from `/w`. The volume is mounted `:ro` in the unit AND the
permission flags grant anonymous only `r`, so no remote write path exists at
all now. But note:
**LAN-direct `:3923` is unauthenticated browse/download** (anonymous holds
`r`) — Caddy + Anubis + authentik guard only `files.franksdank.cloud`, not
the published port. Tighten to `-v /w::r,dank` if anonymous LAN reads become
unacceptable. (Before 2026-08-31, `-v /w::A` made the array world-writable
on the LAN — see [[#Command-line interface (ac edition)]].)
on the LAN — see [[#Command-line interface (ac edition)]] — and the mount
was rw.)
- **Upload tokens scoped per folder:** Prevents accidental global write access.
- **CSP nonces (v1.20.17+):** Security hardening for JavaScript execution. May break third-party JS plugins (`--js-browser` / `--html-head`).
- **Custom thumbnail extractors (v1.20.19+):** New `thumbex` framework. Requires configuration in `initcfg`.
@@ -1,45 +1,40 @@
# Dankserver Stack
Dankserver (192.168.0.235) runs the media and library stack. Every service is reachable by hostname through the Caddy reverse proxy on the host.
Dankserver (192.168.0.235) runs the media and library stack. Public access
goes through the Caddy reverse proxy on Dankclaw; the *arr stack is exempt by
construction — it has no vhost and is reached directly by IP:port on the LAN.
```mermaid
graph LR
U[User] --> C[Caddy :80/:443]
C --> R[RomM]
C --> P[Copyparty]
C --> W[Calibre-Web]
C --> J[Jellyfin]
C --> S[Sonarr]
C --> D[Radarr]
C --> L[Lidarr]
C --> A[Readarr]
C --> O[Prowlarr]
W --> L2[Calibre library]
U[User] --> C[Caddy :80/:443 on Dankclaw]
C -->|guarded: Anubis + Authentik| R[RomM :8085]
C -->|guarded| P[Copyparty :3923]
C -->|unguarded| W[Calibre-Web :8083]
C -->|unguarded| J[Jellyfin :8096]
C -->|exempt by construction| X[Plex :32400]
U -->|direct IP:port, no vhost| S[ARR stack + Prowlarr + qBittorrent on FrankBook-Pro]
```
## Services
| Service | Hostname | Port | Docs |
|---------|----------|------|------|
| RomM | romm.dankserver.local | 8085 | [[RomM]] |
| Copyparty | files.dankserver.local | 3923 | [[Copyparty]] |
| Calibre-Web | books.dankserver.local | 8083 | [[Calibre-Web]] |
| Jellyfin | jellyfin.dankserver.local | 8096 | [[Jellyfin]] |
| Sonarr | sonarr.dankserver.local | 8989 | [[ARR-Stack]] |
| Radarr | radarr.dankserver.local | 7878 | [[ARR-Stack]] |
| Lidarr | lidarr.dankserver.local | 8686 | [[ARR-Stack]] |
| Readarr | readarr.dankserver.local | 8787 | [[ARR-Stack]] |
| Prowlarr | prowlarr.dankserver.local | 9696 | [[ARR-Stack]] |
| qBittorrent | qbittorrent.dankserver.local | 8080 | [[ARR-Stack]] |
| Service | Public route | Port | Docs |
|---------|--------------|------|------|
| RomM | `romm.franksdank.cloud` (guarded: Anubis → Authentik; post-auth route → 192.168.0.235:8085) | 8085 | [[RomM]] |
| Copyparty | `files.franksdank.cloud` (guarded; post-auth route → 192.168.0.235:3923) | 3923 | [[Copyparty]] |
| Calibre-Web | `books.franksdank.cloud` (unguarded — own login) | 8083 | [[Calibre-Web]] |
| Jellyfin | `jelly.franksdank.cloud` (unguarded — own auth) | 8096 | [[Jellyfin]] |
| Plex | `plex.franksdank.cloud` (+ legacy plain-HTTP `plex.franksdank.casa`) | 32400 | [[Plex]] |
## Optional Services
The *arr stack (Sonarr 8989, Radarr 7878, Lidarr 8686, Readarr 8787,
Prowlarr 9696) and qBittorrent (8080, behind the [[Gluetun]] tunnel netns)
run on **FrankBook-Pro**, not on this host — see [[ARR-Stack]]; none of them
have a Caddy vhost.
These services are included in the compose file but may be disabled.
## Never-deployed services
| Service | Hostname | Port | Docs |
|---------|----------|------|------|
| Jackett | jackett.dankserver.local | 9117 | [[ARR-Stack]] |
| Whisparr | whisparr.dankserver.local | 6969 | [[ARR-Stack]] |
Jackett and Whisparr were evaluated during the ARR-stack design phase and
never deployed (no unit, no vhost, no live container). Prowlarr covers the
indexer-aggregation role — see [[Prowlarr]].
## Removed Services
+49 -207
View File
@@ -1,226 +1,68 @@
---
type: reference
title: Jellyfin
description: Jellyfin media server on Dankserver — quadlet unit, entropy-volume data layout, ports, client access, and hardware-transcode status.
tags: [jellyfin, dankserver, media-server]
timestamp: 2026-09-03T00:00:00Z
---
# Jellyfin
Jellyfin is a free, open-source media server and suite of applications for organizing, managing, and streaming personal media collections (movies, TV shows, music, photos) to networked devices. It is a community fork of Emby, written in C# (.NET), and uses FFmpeg for transcoding.
Jellyfin is the free media server on Dankserver — a podman quadlet service
(`jellyfin.service`, generated from `dankserver/jellyfin/quadlet/jellyfin.container`)
serving music, movies, and shows over LAN. It replaced the earlier research
brief; it runs alongside [[Plex]], which remains the primary server for some
clients.
**Host:** Dankserver — **Hostname:** `jellyfin.dankserver.local` — **Docs:** https://jellyfin.org
**Host:** Dankserver (192.168.0.235) — **Access:** `http://192.168.0.235:8096`,
public vhost `jelly.franksdank.cloud` behind the [[Anubis]] guard chain →
[[Authentik]] outpost (post-auth route proxies `localhost:8096`).
## Facts
## Facts (verified from the live unit, 2026-09-03)
| Item | Value |
|------|-------|
| Latest version | Check https://jellyfin.org/downloads |
| Image | `jellyfin/jellyfin:latest` (Docker Hub) or `ghcr.io/jellyfin/jellyfin` |
| Base | Debian-based |
| Primary port | 8096 (HTTP, web UI + API) |
| HTTPS port | 8920 (optional) |
| Client discovery | 7359/udp |
| DLNA | 1900/udp (optional) |
| GPU passthrough | `/dev/dri` (Intel QSV / AMD VA-API) or NVIDIA NVENC |
| License | MIT |
| Unit | `~/.config/containers/systemd/jellyfin.container` → `jellyfin.service` (user manager), `Restart=always`, `RestartSec=5` |
| Image | `ghcr.io/jellyfin/jellyfin:latest` |
| Network | `dankserver_appnet`; publishes 8096/tcp, 8920/tcp, 7359/udp, 1900/udp |
| Data roots | `/home/dank/Containers/jellyfin/config:/config`, `/home/dank/Containers/jellyfin/cache:/cache` |
| Media | `Volume=/mnt/entropy:/data:ro` — the whole [[Entropy Volume]] mounted read-only; nothing automated writes to it |
| GPU | none — no `AddDevice`; CPU decode only (jellyfin-ffmpeg path) |
| Env quirks | `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`, `LD_PRELOAD=libjemalloc.so.2`, `MALLOC_TRIM_THRESHOLD_=131072` kept verbatim from the compose-era container (zero-drift cutover) |
| Health | `curl -f http://localhost:8096/health` every 30 s |
## Architecture
## Data layout
```mermaid
graph LR
U[User] --> C[Caddy :80/:443]
C --> J[Jellyfin :8096]
J --> Lib[/media/movies]
J --> Lib2[/media/shows]
J --> Lib3[/media/music]
J --> Conf[Jellyfin config]
J --> Cache[Jellyfin cache]
```
/home/dank/Containers/jellyfin/config -> /config (JELLYFIN_DATA_DIR)
/home/dank/Containers/jellyfin/cache -> /cache (JELLYFIN_CACHE_DIR, XDG_CACHE_HOME)
/mnt/entropy -> /data (read-only media volume)
```
## Docker Compose (reference — retired 2026-09-01; live mounts use `/home/dank/Containers/jellyfin`, see Deployment below)
Library folders point at `/data` paths; because the mount uses the same
absolute path as on the host, library paths survive container recreation.
```yaml
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
user: "1000:1000"
ports:
- "8096:8096/tcp"
- "8920:8920/tcp"
- "7359:7359/udp"
- "1900:1900/udp"
environment:
- JELLYFIN_PublishedServerUrl=http://192.168.0.235:8096
- DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true
volumes:
- /opt/jellyfin/config:/config
- /opt/jellyfin/cache:/cache
- /mnt/media/movies:/data/movies:ro
- /mnt/media/shows:/data/shows:ro
devices:
- /dev/dri:/dev/dri
group_add:
- "992" # Host render group GID — verify with `getent group render`
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
```
## Ports & access
## Caddy Configuration
| Port | Proto | Purpose |
|------|-------|---------|
| 8096 | TCP | Web UI + API (`JELLYFIN_PublishedServerUrl=http://192.168.0.235:8096`) |
| 8920 | TCP | DLNA/UPnP root device control |
| 7359 | UDP | service discovery (client auto-discovery) |
| 1900 | UDP | SSDP discovery |
```caddy
jellyfin.dankserver.local {
reverse_proxy 127.0.0.1:8096
Client access paths and the public-exposure policy (auth wall required before
any public vhost; Plex-style claim tokens do not exist here — Jellyfin uses its
own users/API keys) follow the fleet [[Authentik]]/[[Anubis]] chain pattern.
transport http {
versions h2c 1.1
}
}
```
## Configuration workflow
Set **Base URL** to `/` (or the path you configure) in Jellyfin Dashboard → Networking. Add `127.0.0.1` to **Known Proxies**.
## Storage
| Mount | Purpose | Size Estimate |
|-------|---------|---------------|
| `/home/dank/Containers/jellyfin/config` | SQLite database, settings, cached images | 10–100 GB |
| `/home/dank/Containers/jellyfin/cache` | Transcoding segments, extracted posters | Self-cleaning per session |
| `/mnt/entropy/Movies`, `/mnt/entropy/TVShows` | Media, **read-only binds** (`:ro`) — no automated write path | — |
The database must be on local storage (SSD/NVMe preferred). Media libraries can be on NFS/mergerfs. If media is on NFS, set `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`.
## Deployment (Quadlet)
Since 2026-09-01: `dankserver/jellyfin/quadlet/jellyfin.container` →
`jellyfin.service` (generator-enabled), deployed with
`./scripts/deploy.sh dankserver --service jellyfin`. GPU passthrough uses
`AddDevice=/dev/dri` — plain `Device=` is **not** a quadlet key and makes the
v1 generator reject the whole unit (learned live: "unsupported key 'Device'
in group 'Container'"). Entropy mounts stay `:ro`; the container runs as
root inside the user namespace, so the render-node ACLs apply via the device
cgroup regardless of `GroupAdd=keep-groups`.
## Authentication
- **Built-in auth:** per-user libraries, parental controls, max bitrate
limits. The local `dank` admin account is built-in; its password is
Frank's bootstrap value — never recorded in this repo. (2026-09-01: a
corrupted session-archive reading briefly suggested the password had
changed; verified offline against the stored PBKDF2-SHA512 hash, the
bootstrap value still authenticates.)
- **LDAP via Authentik (live 2026-09-01):** the LDAP-Auth plugin
authenticates users against the authentik LDAP outpost on Dankclaw — see
[[Authentik]] for the outpost unit, provider, and RBAC objects.
### LDAP-Auth plugin (v23.0.0.0, live 2026-09-01)
- **Install from the GitHub releases zip** — `repo.jellyfin.org`'s stable
manifest path 404s here, so fetch
`jellyfin/jellyfin-plugin-ldapauth` release asset
`ldap-authentication_23.0.0.0.zip` from GitHub and unzip into
`config/plugins/LDAP-Auth_23.0.0.0/`. The config lives at
`config/plugins/configurations/LDAP-Auth.xml` — write it directly, then
restart the service.
- **Live config:** server `192.168.0.143:389`, base DN
`dc=franksdank,dc=cloud`, bind DN
`cn=svc-jellyfin-ldap,ou=users,dc=franksdank,dc=cloud`, bind password =
the authentik outpost token (secret — the XML lives on the host only,
never in the repo), search filter
`(memberOf=cn=jellyfin_users,ou=groups,dc=franksdank,dc=cloud)`, admin
filter on `cn=jellyfin_admins,ou=groups,…`, `CreateUsersFromLdap=true`
(auto-create on first login).
- **Attribute separator is a comma.** `LdapSearchAttributes` is
`Split(',')` in the plugin source — a semicolon-separated list
(`uid;cn;mail;displayName`) parses as ONE bogus attribute, the search
silently matches nothing, and the log says `Found no users matching X`.
Use `uid,cn,mail,displayName`.
- **Username attribute is `cn`.** Authentik 2026.x maps `uid` to an opaque
per-user hash, not the username, so `uid=<name>` never matches. Set
`LdapUsernameAttribute=cn` (authentik exposes the username as `cn`).
- **Verify with a throwaway user:** create a test member of
`jellyfin_users` in authentik, then
`POST /Users/AuthenticateByName` with headers `Content-Type:
application/json` **and** `X-Emby-Authorization: MediaBrowser
Client="…", Device="…", DeviceId="…", Version="10.11.0"` — both are
required in 10.11 (missing `X-Emby-Authorization` → 400). 200 + non-admin
policy = LDAP path works. Delete the test user from authentik **and**
Jellyfin afterward.
- **Plugin load lines only show in `journalctl --user -u jellyfin.service`**
(`Loaded plugin: LDAP-Auth 23.0.0.0`) — `podman logs` does not carry the
loader lines in this image.
## Metadata Scraping
| Provider | Content | Type |
|----------|---------|------|
| TheMovieDb (TMDb) | Movies, TV shows, posters, fan art, overviews | Default |
| OMDb API | Movies only, English metadata | Default |
| TheTVDB | TV shows, episodes | Plugin |
| fanart.tv | Extra artwork, banners, thumbnails | Plugin |
| AniDB | Anime metadata | Plugin |
| MusicBrainz | Music metadata | Plugin |
| Open Subtitles | Subtitle downloads | Plugin |
| Intro Skipper | TV show intro detection | Plugin |
## Clients
| Platform | App | Quality |
|----------|-----|---------|
| Web | Jellyfin Web | Excellent |
| Android | Jellyfin (Google Play / F-Droid) | Excellent |
| Android TV / Fire TV | Jellyfin | Good |
| iOS / iPadOS | Swiftfin (third-party) | Good |
| Desktop | Jellyfin Media Player (mpv-based) | Excellent |
| Roku | Jellyfin (Channel Store) | Good |
| Kodi | Jellyfin addon | Excellent |
| Samsung/LG Smart TVs | TV browser | Fair |
## Jellyfin vs Plex
| Feature | Jellyfin | Plex |
|---------|----------|------|
| Cost | Free, no tiers | Free core; Plex Pass ($5/mo or $120 lifetime) |
| License | MIT | Proprietary |
| Account required | No | Yes |
| Hardware transcoding | Free | Plex Pass required |
| Mobile sync | Free | Plex Pass |
| Live TV / DVR | Free | Plex Pass |
| Client apps | Good, improving | Excellent, widest device support |
Run Jellyfin alongside Plex for an incremental migration. Point Jellyfin at the same media folders as Plex. Watch history and metadata will not transfer cleanly.
## Resource Requirements
- **Idle / Direct Play:** ~200–500 MB RAM, <5% CPU. Negligible.
- **1080p transcode (CPU):** 40–60% CPU on a modern multi-core CPU.
- **1080p transcode (with QSV):** 3–5% CPU.
- **4K HDR transcode (with QSV):** 5–15% CPU.
- **Minimum host RAM:** 4 GB. 8 GB comfortable for transcoding.
## Deployment Checklist
1. Verify GPU on host: `ls -l /dev/dri/`, `lspci | grep -i vga`, `getent group render`.
2. Install media drivers: `sudo apt install intel-media-va-driver-non-free vainfo intel-gpu-tools`.
3. Create directories: `/home/dank/Containers/jellyfin/config`, `/home/dank/Containers/jellyfin/cache`.
4. Write compose file, verify device and GID.
5. Deploy: `podman compose up -d`.
6. Test GPU passthrough: `podman exec jellyfin /usr/lib/jellyfin-ffmpeg/vainfo --display drm --device /dev/dri/renderD128`.
7. Configure Caddy reverse proxy. Set Known Proxies in Jellyfin.
8. Add media libraries in Dashboard.
9. Install plugins: Open Subtitles, fanart.tv, Intro Skipper.
10. Test playback on Android/Chrome.
## Gotchas
- **GPU passthrough silently failing:** `/dev/dri` must be passed and the correct render node used. Verify with `vainfo` inside the container.
- **Wrong render group GID:** Common GID is 992, but varies per host. Run `getent group render` to find it.
- **Subtitle burn-in triggers full transcode:** PGS (image-based) subtitles force full video transcoding. SRT/ASS (text-based) play fine.
- **Database on NFS:** SQLite on network storage causes slow startup and corruption risk. Keep `/config` on local storage.
- **Client bitrate limit too low:** Many clients default to 20 Mbps. Set Max Streaming Bitrate to Original or Maximum.
- **Naming convention mismatches:** Non-standard folder/file names cause missing metadata. Use `Movie Title (Year)` for movies and `Show Name/Season 01/Show Name S01E01.mkv` for TV.
1. Create libraries pointing at `/data` folders (movies, shows, audio).
2. Set metadata/language preferences (`LANG=en_US.UTF-8` pinned in the unit).
3. Configure clients to use `http://192.168.0.235:8096` or the guarded vhost.
## See Also
- [[Dankserver Stack]]
- [[ARR-Stack]] — automated download into Jellyfin libraries
- [[Copyparty]] — alternative file viewer for existing collections
- Plex — existing media server on this host
- [[Plex]] — co-resident media server on the same volume
- [[Entropy Volume]] — the 91 T array mounted read-only at `/data`
- [[Caddy]] · [[Anubis]] · [[Authentik]] — edge chain for `jelly.franksdank.cloud`
+6 -6
View File
@@ -1,10 +1,10 @@
# Plex
Plex Media Server is the **containerized media server on Dankserver**: a
podman quadlet (`plex.service`) under the systemd **user** generator,
generated from `dankserver/plex/quadlet/plex.container`. It is the only media
server actually listening on this host. Jellyfin, by contrast, is a research
brief on this host, not a running service (see [[Jellyfin]]).
Plex Media Server is a **containerized media server on Dankserver**: a podman
quadlet (`plex.service`) under the systemd **user** generator, generated from
`dankserver/plex/quadlet/plex.container`. It shares the host with a second live
media server, [[Jellyfin]] (`jellyfin.service`, listening on 8096); both serve
from the same read-only Entropy Volume mount.
**Host:** Dankserver — **Ports:** 32400/tcp (PMS web + API), 32400 is the
only port PMS needs for client streaming; LAN clients connect directly,
@@ -21,7 +21,7 @@ public traffic can be fronted by Caddy like every other vhost.
| Identity | fleet rootless model — container root maps to the `dank` user (1000:1000); the deployer chowns the data root on first cutover |
| Port | `32400/tcp` published on `0.0.0.0` — zero client reconfiguration |
| Network | `dankserver_appnet`, alias `plex`; Tautulli reaches it at `http://plex:32400` |
| GPU | none by decision — CPU decode only; the RTX 3090 stays dedicated to `frank-default-small` |
| GPU | none by decision — CPU decode only; the RTX 3090 stays dedicated to `frank-small` |
| Deploy | `./scripts/deploy.sh dankserver --service plex` |
| Docs | https://support.plex.tv |
+15 -12
View File
@@ -2,14 +2,15 @@
RomM (ROM Manager) is a self-hosted ROM library manager and player. It scans a ROM collection, enriches it with metadata and cover art from multiple providers, and serves it through a clean web interface. RomM also supports browser-based emulation via EmulatorJS and native emulator streaming.
**Host:** Dankserver — **Hostname:** `romm.dankserver.local` — **Docs:** https://docs.romm.app
**Host:** Dankserver — **Access:** `romm.franksdank.cloud` guarded by the
Anubis → Authentik chain ([[Caddy]] routes it post-auth to
`192.168.0.235:8085`) — **Docs:** https://docs.romm.app
## Facts
| Item | Value |
|------|-------|
| Latest version | 5.2.0 (released 2026-07-29) |
| Image | `ghcr.io/rommapp/romm:latest` |
| Running image | `ghcr.io/rommapp/romm:latest` (rolling tag — no pin; the version claims on this page were true at research time; verify against the live container before relying on them) |
| Port | 8085 (host) → 8080 (container) |
| Database | MariaDB (default); PostgreSQL optional |
| Cache / Queue | Redis or Valkey (required for task queue) |
@@ -21,7 +22,9 @@ RomM (ROM Manager) is a self-hosted ROM library manager and player. It scans a R
## Architecture
RomM 5.2.0 requires three supporting services. The compose stack includes `romm`, `mariadb`, and `redis` (or `valkey`).
Current RomM releases require MariaDB plus Redis (or Valkey) for the task
queue; this deployment runs **redis** (`REDIS_HOST=redis` in the live unit),
not valkey.
```mermaid
graph LR
@@ -68,7 +71,7 @@ These are set via environment variables or in `config.yml`:
| `MOBYGAMES_API_KEY` | MobyGames | Paid hobbyist API. Note: RomM recommends Screenscraper as free alternative. |
| `RETROACHIEVEMENTS_API_KEY` | RetroAchievements | Hash-based achievements via bundled RAHasher binary. |
| `STEAMGRIDDB_API_KEY` | SteamGridDB | Custom cover art via the search cover button. |
| `HASHEOUS_API_ENABLED` | Hasheous | Set to `true`. Free, hash-based, proxies IGDB data. Fast scans. |
| `HASHEOUS_API_ENABLED` | Hasheous | Set to `true` in the live unit env file. Free, hash-based, proxies IGDB data. Fast scans. |
| `PLAYMATCH_API_ENABLED` | Playmatch | Set to `true`. Multi-provider hash matching (IGDB, SS, SteamGridDB, RA). |
### Config File
@@ -117,15 +120,15 @@ Since 2026-09-01 the trio runs as repo-tracked quadlet units, deployed with
## Volumes
The live unit mounts one root bind — `Volume=/home/dank/Containers/romm:/romm`
— so `/romm/config`, `/romm/library`, `/romm/resources`, and `/romm/assets`
all resolve inside a single host-side data root:
| Container path | Host path |
|----------------|-----------|
| `/romm/library` | `/home/dank/Containers/romm/library` |
| `/romm/resources` | `/home/dank/Containers/romm/resources` |
| `/romm/config` | `/home/dank/Containers/romm/config` |
| `/var/lib/mysql` | `/home/dank/Containers/romm/mariadb` (mapped subuid owner — never chown) |
| `/data` (redis) | `/home/dank/Containers/romm/redis` (mapped subuid owner) |
The library volume contains ROM files and subdirectories per platform. The resources volume stores cached artwork, metadata, and the asset cache. The config volume holds `config.yml` and other configuration.
| `/romm/*` (single root bind) | `/home/dank/Containers/romm/*` |
| `/var/lib/mysql` (romm-mariadb) | `/home/dank/Containers/romm/mariadb` (mapped subuid owner — never chown) |
| `/data` (romm-redis) | `/home/dank/Containers/romm/redis` (mapped subuid owner) |
The cutover rescued RomM's anonymous `/romm` volume (assets, plus empty
mountpoint stubs that shadowed nothing) into the data root before first unit
+39 -23
View File
@@ -1,8 +1,19 @@
---
type: reference
title: Lidarr
description: Lidarr music manager in the *arr stack on FrankBook-Pro — live quadlet deployment, ports, volumes, and wiring facts.
tags: [lidarr, arr-stack, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# Lidarr
Lidarr manages music albums in the *arr stack on Dankserver. It automatically searches, downloads, and organizes albums from indexers into the Jellyfin music library.
Lidarr manages music albums in the *arr stack. The stack runs on
**FrankBook-Pro** (the Mac mini) as quadlet units inside its podman machine —
not on Dankserver. It searches, downloads, and organizes albums from indexers
into the Jellyfin music library.
**Host:** Dankserver — **Hostname:** `lidarr.dankserver.local` — **Docs:** https://wiki.servarr.com/lidarr
**Host:** FrankBook-Pro (podman machine) — **Access:** direct port `8686` (no reverse-proxy vhost; the *arr stack is exempt by construction from the Anubis guard chain) — **Docs:** https://wiki.servarr.com/lidarr
## Facts
@@ -10,36 +21,41 @@ Lidarr manages music albums in the *arr stack on Dankserver. It automatically se
|------|-------|
| Image | `lscr.io/linuxserver/lidarr:latest` |
| Port | 8686 (host) → 8686 (container) |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Config root | `/Volumes/COMPLETE/lidarr-data:/config` |
| Min RAM | 256 MB |
| Metadata | MusicBrainz |
## Docker Compose
## Deployment (current)
```yaml
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]
Quadlet unit `lidarr.container` from this repo (`frankbook-pro/lidarr/quadlet/`)
is installed inside the podman machine at
`/home/core/.config/containers/systemd/lidarr.service` by the frankbook-pro
handler in `scripts/deploy-remote.sh`. Unit shape:
```ini
[Container]
ContainerName=lidarr
Image=lscr.io/linuxserver/lidarr:latest
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/lidarr-data:/config
PublishPort=8686:8686
[Service]
TimeoutStartSec=900
Restart=always
```
## Configuration
1. Add Prowlarr as the indexer source (prefer torrent-based indexers for FLAC).
2. Add qBittorrent as the download client with the `music` category.
1. Add Prowlarr as the indexer source (prefer torrent-based indexers for FLAC —
the Nyaa feed carries music categories; see [[Prowlarr]]).
2. Add qBittorrent as the download client with the `music` category ([[qBittorrent]]).
3. Set quality profile (Lossless → High Bitrate → Standard).
4. Set root folder to `/music`.
4. Set the root folder to the music root under the COMPLETE volume.
5. Add artists and verify downloads.
## Gotchas
+62 -27
View File
@@ -1,47 +1,82 @@
---
type: reference
title: MariaDB (RomM Backend)
description: MariaDB 11 database backend for RomM on Dankserver — quadlet unit, network alias, env-file secret handling, and backup path.
tags: [mariadb, romm, dankserver]
timestamp: 2026-09-03T00:00:00Z
---
# MariaDB (RomM Backend)
MariaDB 11 provides the database backend for RomM on Dankserver. RomM 5.2.0 requires MariaDB (or PostgreSQL) for its data store.
MariaDB 11 provides the database backend for [[RomM]] on Dankserver. RomM's
current release requires MariaDB (or PostgreSQL) for its data store; this
deployment uses MariaDB.
**Host:** Dankserver — **Docs:** https://mariadb.com/kb/en/docker-mariadb-information/
**Host:** Dankserver — container name `romm-mariadb`, unit
`romm-mariadb.service` (user-manager quadlet from
`dankserver/romm/quadlet/romm-mariadb.container`).
## Facts
## Facts (verified from the live unit)
| Item | Value |
|------|-------|
| Image | `docker.io/mariadb:11` |
| Port | 3306 (internal, within `appnet`) |
| Database name | `romm` |
| Data volume | `/opt/romm/mariadb` |
| Image | `docker.io/library/mariadb:11` |
| Port | 3306 — internal only; the unit publishes **no** port, so the DB is unreachable from the host network by construction |
| Network | `dankserver_appnet` with `alias=mariadb` — RomM reaches it via `DB_HOST=mariadb` |
| Database / user | `romm` / `romm` |
| Data volume | `/home/dank/Containers/romm/mariadb:/var/lib/mysql` |
| Secrets | supplied via `EnvironmentFile=/home/dank/Containers/romm/env` (mode 0600, host-local — `MARIADB_ROOT_PASSWORD` and friends never enter the repo) |
## Docker Compose
## Unit shape (as deployed)
```yaml
mariadb:
image: docker.io/mariadb:11
container_name: romm-mariadb
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: "${MARIADB_ROOT_PASSWORD}"
MARIADB_DATABASE: romm
MARIADB_USER: "${ROMM_DB_USER}"
MARIADB_PASSWORD: "${ROMM_DB_PASSWD}"
volumes:
- /opt/romm/mariadb:/var/lib/mysql
healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
```ini
[Unit]
Description=RomM mariadb container
After=network.target
Wants=romm-redis.service
[Container]
ContainerName=romm-mariadb
Image=docker.io/library/mariadb:11
Network=dankserver_appnet:alias=mariadb
EnvironmentFile=/home/dank/Containers/romm/env
Environment=MARIADB_DATABASE=romm
Environment=MARIADB_USER=romm
Volume=/home/dank/Containers/romm/mariadb:/var/lib/mysql
HealthCmd=mariadb-admin ping -h localhost
HealthInterval=30s
HealthTimeout=10s
HealthRetries=3
[Service]
Restart=always
RestartSec=5
```
`MARIADB_ROOT_PASSWORD` and `MARIADB_PASSWORD` are read from the env file at
container-create time (`EnvironmentFile=` appears in both `[Container]` and
`[Service]` so `podman generate systemd`-style regeneration keeps them). The
root password is a real secret — per repo secret rules it lives only in the
host-local env file, never in git.
## Boot order
RomM declares `Wants=romm-mariadb.service` + `After=romm-mariadb.service`, so
a cold boot starts the database first; the health ping (`mariadb-admin ping`)
is what marks the unit healthy for anything gating on it.
## Backup
Back up the volume directory periodically:
Back up the volume directory periodically (stop-free: rsync of a quiet DB dir):
```bash
sudo rsync -a /opt/romm/mariadb /mnt/backup/romm-mariadb/
sudo rsync -a /home/dank/Containers/romm/mariadb /mnt/backup/romm-mariadb/
```
For consistency-critical dumps prefer `mariadb-dump` over file copy; the data
dir copy is only safe while RomM traffic is idle.
## See Also
- [[RomM]] — service that uses this database
- [[redis]] — the sibling cache/queue container on the same network
+54 -39
View File
@@ -1,20 +1,55 @@
---
type: reference
title: Prowlarr
description: Prowlarr indexer-management layer for the *arr stack on FrankBook-Pro — feed routes, sync model, API surface, and verified live gotchas.
tags: [prowlarr, arr-stack, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# Prowlarr
Prowlarr is the indexer management layer for the *arr stack. It hosts the
indexer definitions and exposes them as Torznab feeds and downloads;
Sonarr, Radarr, Lidarr, and Readarr query those feeds and receive results.
**Host:** Dankserver — **Hostname:** `prowlarr.dankserver.local` — **Docs:** https://wiki.servarr.com/prowlarr
**Host:** FrankBook-Pro (podman machine) — **Access:** direct port `9696` (no
reverse-proxy vhost; the *arr stack is exempt by construction from the Anubis
guard chain) — **Docs:** https://wiki.servarr.com/prowlarr
## Facts
| Item | Value |
|------|-------|
| Image | `lscr.io/linuxserver/prowlarr:latest` |
| Image | `lscr.io/linuxserver/prowlarr:latest` (runs v2.5.2 line) |
| Port | 9696 (host) → 9696 (container) |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Config root | `/Volumes/COMPLETE/prowlarr-data:/config` |
| Min RAM | 256 MB |
| Auth | Forms (web UI) + API key (`/api/v1/...` and feed routes) |
## Deployment (current)
Quadlet unit `prowlarr.container` from this repo
(`frankbook-pro/prowlarr/quadlet/`), installed inside the podman machine at
`/home/core/.config/containers/systemd/prowlarr.service` by the frankbook-pro
handler in `scripts/deploy-remote.sh`:
```ini
[Container]
ContainerName=prowlarr
Image=lscr.io/linuxserver/prowlarr:latest
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/prowlarr-data:/config
PublishPort=9696:9696
[Service]
TimeoutStartSec=900
Restart=always
```
## Live wiring (verified end-to-end 2026-08-30)
Indexer queries flow **through** Prowlarr; a grab hands off to the querying
@@ -35,35 +70,17 @@ http://prowlarr:9696/{indexerId}/download?link=...&file=... (torrent handoff)
The global API key is in `config.xml` (`<ApiKey>`); per-application keys are
only used by Prowlarr's own sync calls, NOT by the feeds. The arr download
clients authenticate to qBittorrent with the permanent WebUI password stored
in `/home/dank/.qbpass` (mode 600) on Dankserver.
in `/home/dank/.qbpass` (mode 600, owner `dank`) on Dankserver — see
[[qBittorrent]] for why that password must be pinned in the conf file.
**Readarr caveat:** Readarr is abandoned upstream (last release 0.4.x); its
qBittorrent client cannot authenticate against qB 5 (`Authentication
failure` on the connection test while Sonarr/Lidarr/Radarr pass with the
same credentials). The books pipeline is parked pending a Readarr successor;
the indexer row itself is healthy.
**Readarr caveat:** Readarr is abandoned upstream; its qBittorrent client
cannot authenticate against newer qB Web-API versions (`Authentication
failure` on the connection test while Sonarr/Lidarr/Radarr pass with the same
credentials — root cause: qB ≥ 5's 204 empty-body login success, fixed fleet-
wide by pinning qB to 4.6.7). The books pipeline is parked pending a Readarr
successor; the indexer row itself is healthy.
## Docker Compose
```yaml
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]
```
## Gotchas (all verified live 2026-08-30)
## Gotchas (verified live 2026-08-30)
- **The feed route is `/{indexerId}/api`.** There is no `/torznab/{id}/feed`
— that path (and any other unmatched path) hits the Forms-auth catch-all
@@ -92,12 +109,13 @@ prowlarr:
- Instance name/`InstanceName=Prowlarr` in `config.xml` does NOT prefix the
API/feed routes on this build (they answer at root; the `/Prowlarr/...`
prefix 302s to the SPA).
- **Application auto-sync cannot manage these rows** (they carry
`SyncLevel=2`): `POST /api/v1/applications/test` fails with
`Unable to cast object of type 'NzbDrone.Core.Thing…'` — Prowlarr v1
cannot deserialize Radarr 6.3's newer response types. The manual Torznab
rows above are therefore the real integration; sync neither duplicates
nor clobbers them.
- **Application auto-sync model (v2):** Applications are first-class rows in
the `Applications` table; each of the four app rows (sonarr/radarr/lidarr/
readarr) carries the real baseUrl+apiKey with `SyncLevel=FullSync`. The
earlier v1-era failure where `POST /api/v1/applications/test` threw
`Unable to cast object of type 'NzbDrone.Core.Thing…'` (v1 could not
deserialize Radarr 6.3's newer response types) is historical — it was the
reason the manual Torznab rows were created in the first place.
- Prowlarr runs `AuthenticationMethod=Forms` + `AuthenticationRequired=Enabled`
healthily — the DryIoc crash combo was `None`+`Enabled` only (see
[[radarr]] auth notes).
@@ -105,7 +123,4 @@ prowlarr:
## See Also
- [[ARR-Stack]] — full *arr stack documentation
- [[radarr]]
- [[sonarr]]
- [[lidarr]]
- [[readarr]]
- [[Radarr]] · [[Sonarr]] · [[Lidarr]] · [[Readarr]] · [[qBittorrent]]
+66 -38
View File
@@ -1,27 +1,44 @@
---
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 on Dankserver. It receives download requests from Sonarr, Radarr, Lidarr, and Readarr via category tagging.
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:** Dankserver — **Hostname:** `qbittorrent.dankserver.local` — **Docs:** https://www.qbittorrent.org
**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:latest` |
| Port | 8080 (host) → 8080 (container) |
| Min RAM | 512 MB |
| Auth | Web UI username/password; LAN mode available |
| 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 |
## Configuration
## Why the 4.6.7 pin exists (2026-09-01)
1. Keep Web UI authentication ON (LAN bypass not needed — the arr apps
authenticate with the API) and give them a permanent password.
2. Create download categories per app (`radarr`, `sonarr`, `lidarr`,
`readarr`): the arr download clients route category-per-app, and Prowlarr
feeds hand grabs to each app's own client.
3. Set max active downloads: Settings → Downloads (recommended: 5 global,
3 per category).
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)
@@ -46,31 +63,34 @@ val = (base64.b64encode(salt) + b":" + base64.b64encode(h)).decode()
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.md` reference it. (The classic
`@ByteArray(salt+hash)` form is WRONG for qb5: login 401s on it; verify()
base64-decodes the two halves separately.)
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.)
## Docker Compose
## Deployment (current)
```yaml
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]
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)
@@ -81,9 +101,17 @@ qbittorrent:
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.
- Monitor bandwidth limits to avoid saturating the network.
## See Also
- [[ARR-Stack]] — full *arr stack documentation
- [[Gluetun]] — the WireGuard tunnel this container shares
+42 -25
View File
@@ -1,52 +1,69 @@
---
type: reference
title: Radarr
description: Radarr movie manager in the *arr stack on FrankBook-Pro — live quadlet deployment, ports, volumes, and wiring facts.
tags: [radarr, arr-stack, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# Radarr
Radarr manages movies in the *arr stack on Dankserver. It automatically searches, downloads, and organizes movies from indexers into the Jellyfin media library.
Radarr manages movies in the *arr stack. The stack runs on **FrankBook-Pro**
(the Mac mini) as quadlet units inside its podman machine — not on Dankserver.
It searches, downloads, and organizes movies from indexers into the Jellyfin
movie library.
**Host:** Dankserver — **Hostname:** `radarr.dankserver.local` — **Docs:** https://wiki.servarr.com/radarr
**Host:** FrankBook-Pro (podman machine) — **Access:** direct port `7878` (no reverse-proxy vhost; the *arr stack is exempt by construction from the Anubis guard chain) — **Docs:** https://wiki.servarr.com/radarr
## Facts
| Item | Value |
|------|-------|
| Image | `lscr.io/linuxserver/radarr:latest` (v6 stable) |
| Image | `lscr.io/linuxserver/radarr:latest` (v6 line; v6.3.x verified live 2026-08-30) |
| Port | 7878 (host) → 7878 (container) |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Config root | `/Volumes/COMPLETE/radarr-data:/config` |
| Min RAM | 512 MB |
| Metadata | TMDB / TheMovieDB API |
## Docker Compose
## Deployment (current)
```yaml
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]
Quadlet unit `radarr.container` from this repo (`frankbook-pro/radarr/quadlet/`)
is installed inside the podman machine at
`/home/core/.config/containers/systemd/radarr.service` by the frankbook-pro
handler in `scripts/deploy-remote.sh`. Unit shape:
```ini
[Container]
ContainerName=radarr
Image=lscr.io/linuxserver/radarr:latest
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/radarr-data:/config
PublishPort=7878:7878
[Service]
TimeoutStartSec=900
Restart=always
```
## Configuration
1. Add Prowlarr as the indexer source.
2. Add qBittorrent as the download client with the `movies` category.
1. Add Prowlarr as the indexer source (the YTS indexer row feeds Radarr — see [[Prowlarr]]).
2. Add qBittorrent as the download client with the `movies` category ([[qBittorrent]]).
3. Set quality profile and cutoff quality.
4. Set root folder to `/movies`.
4. Set the root folder to the movies root under the COMPLETE volume (Radarr
imports from where [[qBittorrent]] drops completed downloads; first verified
grab+import was a 2160p Dark Knight test on 2026-08-30).
5. Add movies and verify downloads.
## Gotchas
- Radarr v6 is the current stable (v6.3.x as of 2026-08). `:latest` points to the current release.
- Movie renaming produces `Title (Year)` format. Jellyfin scanner handles this.
- File permissions: qBittorrent downloads as PUID; arr apps must read/move files.
- File permissions: qBittorrent downloads as PUID=0; arr apps must read/move files.
## See Also
+56 -30
View File
@@ -1,55 +1,81 @@
---
type: reference
title: Readarr
description: Readarr ebook/audiobook manager in the *arr stack on FrankBook-Pro — retired upstream, pinned image, parked qB download client.
tags: [readarr, arr-stack, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# Readarr
> **RETIRED (May 2026).** The Servarr Team retired Readarr: its metadata became unusable and the community migration to Open Library stalled. There is no upstream development. Alternatives: the Bookshelf community fork, or the Calibre content server. See [Readarr (Retired)](https://wiki.servarr.com/readarr).
> **RETIRED UPSTREAM (May 2026).** The Servarr Team retired Readarr: its
> metadata became unusable and the community migration to Open Library stalled.
> There is no upstream development. Alternatives: the Bookshelf community fork,
> or the Calibre content server. This instance still runs on a pinned image
> tag; see below.
Readarr manages ebooks and audiobooks in the *arr stack on Dankserver. It automatically searches, downloads, and organizes books into the Calibre library.
Readarr manages ebooks and audiobooks in the *arr stack. The stack runs on
**FrankBook-Pro** (the Mac mini) as quadlet units inside its podman machine —
not on Dankserver. It organizes books into the Calibre library that
[[Calibre-Web]] serves.
**Host:** Dankserver — **Hostname:** `readarr.dankserver.local` — **Docs:** https://wiki.servarr.com/readarr
**Host:** FrankBook-Pro (podman machine) — **Access:** direct port `8787` (no reverse-proxy vhost; the *arr stack is exempt by construction from the Anubis guard chain) — **Docs:** https://wiki.servarr.com/readarr
## Facts
| Item | Value |
|------|-------|
| Image | `lscr.io/linuxserver/readarr:latest` (v0.2.x development) |
| Image | `lscr.io/linuxserver/readarr:amd64-1.0.980` — **pinned tag** (LSIO ships no untagged `latest` for readarr; the tag was pinned after a manifest probe) |
| Port | 8787 (host) → 8787 (container) |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Config root | `/Volumes/COMPLETE/readarr-data:/config` |
| Min RAM | 256 MB |
| Metadata | Goodreads / StoryGraph |
| Metadata | Goodreads / StoryGraph (provider status at retirement) |
## Docker Compose
## Deployment (current)
```yaml
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]
Quadlet unit `readarr.container` from this repo (`frankbook-pro/readarr/quadlet/`)
is installed inside the podman machine at
`/home/core/.config/containers/systemd/readarr.service` by the frankbook-pro
handler in `scripts/deploy-remote.sh`. Unit shape:
```ini
[Container]
ContainerName=readarr
Image=lscr.io/linuxserver/readarr:amd64-1.0.980
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/readarr-data:/config
PublishPort=8787:8787
[Service]
TimeoutStartSec=900
Restart=always
```
## Configuration
1. Add Prowlarr as the indexer source (torrent indexers with book content).
2. Add qBittorrent as the download client with the `books` category.
1. Add Prowlarr as the indexer source (torrent indexers with book content;
the Byrutor row feeds Readarr — see [[Prowlarr]]).
2. The qBittorrent download client is **parked**: Readarr's abandoned client
fails auth against newer qB Web-API versions (see [[qBittorrent]] for the
4.6.7 pin rationale); re-enable only after the image changes. Until then,
books are imported manually or via Calibre tooling.
3. Set author profile (format preference: epub, mobi, etc.).
4. Set quality profile for audiobooks (320 kbps MP3, 128 kbps, lossless).
5. Set root folder to `/books`.
6. Add authors and verify downloads.
5. Set the root folder to the books root under the COMPLETE volume.
## Gotchas
- Readarr is in development (~v0.2.x). Some features may be incomplete.
- Calibre integration is folder-based, not API-based. Readarr drops files into `/books` which Calibre-Web serves.
- Not all indexers have all formats (epub, mobi, pdf). Goodreads/StoryGraph metadata helps match editions.
- Readarr is abandoned upstream (retired May 2026; last line was 0.4.x before
the final tags). The pinned `amd64-1.0.980` tag exists precisely because
`:latest` does not exist for this image on LSIO.
- Calibre integration is folder-based, not API-based. Readarr drops files into
the books root which [[Calibre-Web]] serves.
- Not all indexers carry all formats (epub, mobi, pdf). Goodreads/StoryGraph
metadata helps match editions while those providers still respond.
## See Also
+48 -21
View File
@@ -1,40 +1,67 @@
---
type: reference
title: Redis (RomM Cache / Queue)
description: Redis 7-alpine cache and task-queue backend for RomM on Dankserver — quadlet unit, network alias topology, and backup path.
tags: [redis, romm, dankserver]
timestamp: 2026-09-03T00:00:00Z
---
# Redis (RomM Cache / Queue)
Redis 7 provides the caching and task queue backend for RomM on Dankserver. RomM 5.2.0 requires Redis (or Valkey) for its task queue.
Redis 7 provides the caching and task-queue backend for [[RomM]] on Dankserver.
RomM requires Redis (or Valkey) for its task queue; this deployment runs
stock `redis:7-alpine` — no Valkey here, unlike the SearXNG cache on
Dankclaw ([[Valkey]]).
**Host:** Dankserver — **Docs:** https://redis.io/docs/
**Host:** Dankserver — container name `romm-redis`, unit `romm-redis.service`
(user-manager quadlet from `dankserver/romm/quadlet/romm-redis.container`).
## Facts
## Facts (verified from the live unit)
| Item | Value |
|------|-------|
| Image | `docker.io/redis:7-alpine` |
| Port | 6379 (internal, within `appnet`) |
| Data volume | `/opt/romm/redis` |
| Auth | None (local-only, no password needed) |
| Image | `docker.io/library/redis:7-alpine` |
| Port | 6379 — internal only; no `PublishPort`, so it is unreachable from the host network by construction |
| Network | `dankserver_appnet` with `alias=redis` — that alias is what makes `REDIS_HOST=redis` in the RomM unit resolve |
| Data volume | `/home/dank/Containers/romm/redis:/data` |
| Auth | None — safe precisely because the container is not published anywhere; only appnet members can reach it |
## Docker Compose
## Unit shape (as deployed)
```yaml
redis:
image: docker.io/redis:7-alpine
container_name: romm-redis
restart: unless-stopped
volumes:
- /opt/romm/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
```ini
[Container]
ContainerName=romm-redis
Image=docker.io/library/redis:7-alpine
Network=dankserver_appnet:alias=redis
Volume=/home/dank/Containers/romm/redis:/data
HealthCmd=redis-cli ping
HealthInterval=10s
HealthTimeout=5s
HealthRetries=3
[Service]
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
```
No `EnvironmentFile` — there is nothing secret in this unit; the auth-less
posture is enforced by network topology (alias-only reachability), not by a
password. If a future client ever needs Redis from outside the appnet, add
`PublishPort` **and** a `requirepass` in the same change — never just the port.
## Backup
```bash
sudo rsync -a /opt/romm/redis /mnt/backup/romm-redis/
sudo rsync -a /home/dank/Containers/romm/redis /mnt/backup/romm-redis/
```
Redis here is cache + queue state; losing it costs queued jobs and cache warm,
not the library (the database lives in [[MariaDB]]).
## See Also
- [[RomM]] — service that uses this cache
- [[Valkey]] — the SearXNG-side equivalent on Dankclaw
+43 -26
View File
@@ -1,53 +1,70 @@
---
type: reference
title: Sonarr
description: Sonarr TV manager in the *arr stack on FrankBook-Pro — live quadlet deployment, ports, volumes, and wiring facts.
tags: [sonarr, arr-stack, frankbook-pro]
timestamp: 2026-09-03T00:00:00Z
---
# Sonarr
Sonarr manages TV shows in the *arr stack on Dankserver. It automatically searches, downloads, and organizes episodes from indexers into the Jellyfin media library.
Sonarr manages TV shows in the *arr stack. The stack runs on **FrankBook-Pro**
(the Mac mini) as quadlet units inside its podman machine — not on Dankserver.
It searches, downloads, and organizes episodes from indexers into the Jellyfin
TV library.
**Host:** Dankserver — **Hostname:** `sonarr.dankserver.local` — **Docs:** https://wiki.servarr.com/sonarr
**Host:** FrankBook-Pro (podman machine) — **Access:** direct port `8989` (no reverse-proxy vhost; the *arr stack is exempt by construction from the Anubis guard chain) — **Docs:** https://wiki.servarr.com/sonarr
## Facts
| Item | Value |
|------|-------|
| Image | `lscr.io/linuxserver/sonarr:latest` (v4 stable) |
| Image | `lscr.io/linuxserver/sonarr:latest` (v4 line) |
| Port | 8989 (host) → 8989 (container) |
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
| Config root | `/Volumes/COMPLETE/sonarr-data:/config` |
| Min RAM | 512 MB |
| Metadata | TMDB / TVDB |
## Docker Compose
## Deployment (current)
```yaml
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]
Quadlet unit `sonarr.container` from this repo (`frankbook-pro/sonarr/quadlet/`)
is installed inside the podman machine at
`/home/core/.config/containers/systemd/sonarr.service` by the frankbook-pro
handler in `scripts/deploy-remote.sh`. Unit shape:
```ini
[Container]
ContainerName=sonarr
Image=lscr.io/linuxserver/sonarr:latest
Environment=PUID=0
Environment=PGID=0
Environment=TZ=America/Los_Angeles
Environment=UMASK_SET=022
Volume=/Volumes/COMPLETE/sonarr-data:/config
PublishPort=8989:8989
[Service]
TimeoutStartSec=900
Restart=always
```
## Configuration
1. Add Prowlarr as the indexer source.
2. Add qBittorrent as the download client with the `tv` category.
1. Add Prowlarr as the indexer source (see [[Prowlarr]] — the feed route is `/{indexerId}/api`).
2. Add qBittorrent as the download client with the `tv` category ([[qBittorrent]]).
3. Set quality profile, cutoff, and minimum/maximum episode sizes.
4. Set root folder to `/tv`.
5. Add series and verify downloads.
4. Set the root folder to the TV root under the COMPLETE volume (Sonarr imports
from where [[qBittorrent]] drops completed downloads).
5. Add a test series and verify the grab → download → import chain end-to-end.
## Gotchas
- Sonarr v4 is the current stable. The v3 branch uses the `:v3` tag.
- Anime has different quality profiles and naming conventions than Standard series.
- Missing episodes: set "Unmonitor Missing Episodes" to auto-unmonitor old episodes.
- Timezone-sensitive scheduler: ensure all containers use the same TZ.
- Timezone-sensitive scheduler: every unit in the stack runs
`TZ=America/Los_Angeles`; keep them uniform.
## See Also