diff --git a/.env.example b/.env.example index 27d0979..6518437 100644 --- a/.env.example +++ b/.env.example @@ -36,6 +36,13 @@ TRANSMISSION_RPC_PASSWORD= # Host port to expose Grafana on. Defaults to 3000 if unset. GRAFANA_PORT=3000 +# ============ Decluttarr (queue cleanup for Radarr/Sonarr) ============ +# These keys come from Radarr's Settings -> General -> API Key (and Sonarr's +# equivalent) AFTER the stack is running. Decluttarr starts fine with them +# blank — it just skips any service whose key isn't set. +RADARR_API_KEY= +SONARR_API_KEY= + # ============ Tracearr (required — stack will refuse to start if blank) ============ # Use strong random strings, e.g. `openssl rand -base64 32` DB_PASSWORD= diff --git a/CLAUDE.md b/CLAUDE.md index 1ae4bfe..06db776 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,8 +24,8 @@ There is no test suite. After changing `docker-compose.yml`, always run `docker **Network isolation matters.** Services are split across four bridge networks and one host-mode service. A service can only reach another if they share a network — adding a new service requires picking the right one (or declaring multiple): - `monitoring_network` — tautulli, grafana, telegraf, watchtower, portainer, prometheus, cadvisor, node-exporter -- `media_network` — seerr, radarr, sonarr, prowlarr, bazarr -- `download_network` — transmission, watchlistarr, cleanarr, requestrr, radarr, sonarr +- `media_network` — seerr, radarr, sonarr, prowlarr, bazarr, flaresolverr, maintainerr, checkrr +- `download_network` — transmission, watchlistarr, cleanarr, requestrr, decluttarr, radarr, sonarr - `tracearr-network` — tracearr, timescale (PostgreSQL), redis - **host network** — plex only (required for proper streaming/discovery) @@ -62,5 +62,6 @@ The mounted config directory is `plex-meta-manager/config/`. Its structure is re 1. **Hard-required** (stack won't start): `DB_PASSWORD`, `JWT_SECRET`, `COOKIE_SECRET` (Tracearr); `OPENVPN_PROVIDER`, `OPENVPN_CONFIG`, `OPENVPN_USERNAME`, `OPENVPN_PASSWORD` (Transmission VPN). All use the `${VAR:?must be set}` fail-fast form. 2. **Effectively required for the feature to work**: `PUID`/`PGID`/`TZ`/`USERDIR` (everything), `PLEX_CLAIM` (first-boot only), `GRAFANA_PORT` (defaults to 3000 if unset), `LOCAL_NETWORK` (Transmission, defaults to `192.168.0.0/16`), `PMM_*` (Kometa), `TRANSMISSION_RPC_USERNAME`/`TRANSMISSION_RPC_PASSWORD` (optional web UI auth). +3. **Populated after first boot**: `RADARR_API_KEY`, `SONARR_API_KEY` (Decluttarr). These come from the Radarr/Sonarr UIs after the stack is up — Decluttarr starts fine with them blank and skips any service whose key isn't set. **Note:** this is a real exception to the "every var in `.env.example` is consumed by compose" rule above — flag this chicken-and-egg to users on first-boot questions, since the *arr API keys are only obtainable post-`up`. -If a user mentions an env var not in this list (e.g. `RADARR_API_KEY`, `DOCKER_INFLUXDB_*`, `PLEX_TOKEN`), it's from an older version of the stack — not consumed today. +If a user mentions an env var not in this list (e.g. `DOCKER_INFLUXDB_*`, `PLEX_TOKEN`, `EMAIL`/`PASSWORD`), it's from an older version of the stack — not consumed today. diff --git a/README.md b/README.md index 1b55110..89253f8 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,11 @@ A ready-to-use [Kometa](https://kometa.wiki/) (Plex Meta Manager) configuration | [Sonarr](https://sonarr.tv/) | TV show management and downloading | `8989` | | [Prowlarr](https://prowlarr.com/) | Indexer manager that feeds Radarr/Sonarr | `9696` | | [Bazarr](https://www.bazarr.media/) | Subtitle management for Radarr/Sonarr libraries | `6767` | +| [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) | Cloudflare bypass proxy for Prowlarr indexers | `8191` | +| [Maintainerr](https://github.com/jorenn92/Maintainerr) | Rule-based Plex library cleanup (auto-remove watched/aged items) | `6246` | | [Watchlistarr](https://github.com/nylonee/watchlistarr) | Syncs Plex watchlist to Radarr/Sonarr | N/A | +| [Decluttarr](https://github.com/ManiMatter/decluttarr) | Removes stalled / failed downloads from \*arr queues | N/A | +| [Checkrr](https://github.com/aetaric/checkrr) | Scans media files for codec / corruption issues | `8585` | | [Cleanarr](https://github.com/se1exin/Cleanarr) | Finds and removes duplicate content | N/A | | [Requestrr](https://github.com/darkalfx/requestrr) | Discord bot for content requests | `4545` | @@ -174,8 +178,8 @@ These services pair well with this stack but are not included in the default `do Services are isolated into separate Docker networks: - **`monitoring_network`** - Tautulli, Grafana, Telegraf, Watchtower, Portainer, Prometheus, cAdvisor, node-exporter -- **`media_network`** - Seerr, Radarr, Sonarr, Prowlarr, Bazarr -- **`download_network`** - Transmission, Watchlistarr, Cleanarr, Requestrr, Radarr, Sonarr +- **`media_network`** - Seerr, Radarr, Sonarr, Prowlarr, Bazarr, FlareSolverr, Maintainerr, Checkrr +- **`download_network`** - Transmission, Watchlistarr, Cleanarr, Requestrr, Decluttarr, Radarr, Sonarr - **`tracearr-network`** - Tracearr, TimescaleDB, Redis Plex runs in host network mode for optimal streaming performance. Radarr and Sonarr are attached to both `media_network` (so Seerr, Prowlarr, and Bazarr can reach them) and `download_network` (so Watchlistarr and Transmission can reach them). diff --git a/docker-compose.yml b/docker-compose.yml index 9192be4..766fae5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -225,6 +225,36 @@ services: - ${USERDIR}/plex/media:/media restart: unless-stopped + # Cloudflare bypass proxy. Prowlarr (and any other indexer client) + # routes requests through this to reach indexer sites behind Cloudflare. + flaresolverr: + container_name: flaresolverr + image: ghcr.io/flaresolverr/flaresolverr:latest + networks: + - media_network + ports: + - "8191:8191" + environment: + - LOG_LEVEL=info + - TZ=${TZ} + restart: unless-stopped + + # Rule-based Plex library maintenance (auto-remove watched / aged items). + maintainerr: + container_name: maintainerr + image: ghcr.io/jorenn92/maintainerr:latest + networks: + - media_network + ports: + - "6246:6246" + environment: + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + volumes: + - ${USERDIR}/maintainerr/data:/opt/data + restart: unless-stopped + # ============ MEDIA CENTER ============ watchlistarr: container_name: watchlistarr @@ -268,7 +298,48 @@ services: - ${USERDIR}/transmission/data:/data restart: unless-stopped - # ============ ERROR MONITORING ============ + # ============ LIBRARY MAINTENANCE ============ + # Cleans stalled/failed downloads out of Radarr/Sonarr queues. Skips any + # service whose API key is blank, so it's safe to leave keys empty on + # first boot and fill them in after the *arrs are running. + # Pinned to v1: v2 (Nov 2025) requires both base_url and api_key in + # structured RADARR/SONARR blocks, which breaks the blank-keys-on-first-boot + # workflow this service is configured around. Re-evaluate if v2 ever + # gains a "skip instances with empty api_key" behavior. + decluttarr: + container_name: decluttarr + image: ghcr.io/manimatter/decluttarr:v1.50.2 + networks: + - download_network + environment: + - TZ=${TZ} + - LOG_LEVEL=INFO + - RADARR_URL=http://radarr:7878 + - RADARR_KEY=${RADARR_API_KEY:-} + - SONARR_URL=http://sonarr:8989 + - SONARR_KEY=${SONARR_API_KEY:-} + - REMOVE_TIMER=10 + - REMOVE_FAILED=True + - REMOVE_STALLED=True + - REMOVE_MISSING_FILES=True + - REMOVE_ORPHANS=True + restart: unless-stopped + + # Scans media files for codec / corruption issues. Web UI on :8585. + checkrr: + container_name: checkrr + image: ghcr.io/aetaric/checkrr:latest + networks: + - media_network + ports: + - "8585:8585" + environment: + - TZ=${TZ} + volumes: + - ${USERDIR}/checkrr/config:/etc/checkrr + - ${USERDIR}/plex/media:/media:ro + restart: unless-stopped + cleanarr: container_name: cleanarr image: cleanarr/cleanarr