diff --git a/jellyfin/jellyfin-services.md b/jellyfin/jellyfin-services.md new file mode 100644 index 0000000..ae062d2 --- /dev/null +++ b/jellyfin/jellyfin-services.md @@ -0,0 +1,75 @@ +# Jellyfin Server — Docker Services + +## Host +- **VM / alias**: jellyfin +- **IP**: `192.168.88.10` +- **SSH**: `jgitta@192.168.88.10` (alias: `jellyfin`) +- **Portainer endpoint ID**: 4 +- **Compose files**: managed via Portainer (stack IDs below); source of truth is Gitea `jgitta/homelab-configs` + +--- + +## Stacks + +| Stack | Portainer ID | Gitea Path | +|---|---|---| +| arr-stack | 35 | `jellyfin/arr-stack/docker-compose.yml` | +| portainer-agent | 36 | `jellyfin/portainer-agent/` | +| node-exporter | 44 | `jellyfin/node-exporter/` | + +--- + +## arr-stack Services + +| Container | Image | Port | Config Path | +|---|---|---|---| +| radarr | lscr.io/linuxserver/radarr:latest | 7878 | /srv/docker/radarr/config | +| sonarr | lscr.io/linuxserver/sonarr:latest | 8989 | /srv/docker/sonarr/config | +| prowlarr | lscr.io/linuxserver/prowlarr:latest | 9696 | /srv/docker/prowlarr/config | +| flaresolverr | ghcr.io/flaresolverr/flaresolverr:latest | 8191 | — | +| watchtower | containrrr/watchtower | — | see below | + +Media volume: `/mnt/media` on jellyfin host → mounted as `/data` in radarr/sonarr. + +--- + +## Watchtower + +- Runs inside arr-stack on jellyfin (Portainer stack 35) +- Schedule: `0 0 4 * * *` — runs at 4:00 AM daily +- Notifications: email via Fastmail SMTP to jgitta@jgitta.com +- Cleanup: enabled (removes old images after update) +- Rolling restarts: enabled with 30s timeout, 60s wait before stopping + +### lscr.io Authentication (fix applied 2026-05-11) + +**Problem**: Watchtower was hitting lscr.io's rate limiter on unauthenticated pulls. radarr, +sonarr, and prowlarr all use `lscr.io/linuxserver/` images; when Watchtower checks them +concurrently at 4 AM the burst briefly exceeded the anonymous quota. Error appeared in +email notifications as: +``` +Unable to update container "/radarr": Error response from daemon: toomanyrequests: +retry-after: 835.12µs, allowed: 44000/minute. Proceeding to next. +``` +Observed on: 2026-05-08 and 2026-05-11. + +**Fix**: +1. Logged in to lscr.io on the jellyfin host using the GitHub PAT (credentials.md): + ``` + echo | docker login lscr.io -u jgitta --password-stdin + ``` + Creates `/home/jgitta/.docker/config.json` with lscr.io auth token. + +2. Mounted that config into the Watchtower container (in docker-compose.yml): + ```yaml + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /home/jgitta/.docker/config.json:/config.json:ro + ``` + Watchtower reads `/config.json` inside the container for registry credentials. + +3. Updated Gitea (`jellyfin/arr-stack/docker-compose.yml`) and redeployed via Portainer. + +**If GitHub PAT expires**: Re-run the `docker login lscr.io` command on jellyfin with the +new token. No compose changes needed — the mounted file updates automatically. +The PAT expiry date is tracked in credentials.md / github.com/settings/tokens.