Files
homelab-configs/jellyfin/jellyfin-services.md
T

87 lines
2.9 KiB
Markdown

# 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
---
## Stacks (Portainer)
| Stack | 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 | — |
Media volume: `/mnt/media` → mounted as `/data` in radarr/sonarr.
---
## Auto-Updates — Cron + Watchtower run-once
Watchtower is NOT run as a persistent container. Instead, cron jobs on the jellyfin
host invoke Watchtower in `--run-once` mode, one container at a time, at staggered
3-minute intervals. This serializes all lscr.io registry checks and eliminates the
CDN burst rate limit errors that occurred with concurrent requests.
### Schedule (jgitta crontab on jellyfin)
| Time | Container | Command |
|---|---|---|
| 4:00 AM | radarr | `watchtower-update.sh radarr` |
| 4:03 AM | sonarr | `watchtower-update.sh sonarr` |
| 4:06 AM | prowlarr | `watchtower-update.sh prowlarr` |
| 4:09 AM | flaresolverr + others | `watchtower-update.sh` (no scope) |
### Update script
Location: `/usr/local/bin/watchtower-update.sh` on jellyfin host.
To view: `ssh jellyfin cat /usr/local/bin/watchtower-update.sh`
To edit cron: `ssh jellyfin crontab -e`
Each run spins up a temporary `containrrr/watchtower` container with `--run-once`,
checks for updates, sends an email notification if anything changed, and exits.
The container is automatically removed after each run (`--rm`).
### Scope labels
radarr, sonarr, and prowlarr each carry a `com.centurylinklabs.watchtower.scope=<name>`
label in the compose file. This tells Watchtower to only check the matching container
when that scope is set. flaresolverr has no scope label and is handled by the
unscoped (4:09 AM) run.
---
## Registry Authentication
Credentials are stored in `/home/jgitta/.docker/config.json` on the jellyfin host.
The update script mounts this file into each Watchtower container.
Both `lscr.io` and `ghcr.io` are authenticated (lscr.io proxies to ghcr.io for the
actual registry API, so both need credentials).
Token: no-expiry `read:packages` GitHub PAT — see credentials.md.
To re-authenticate (e.g. after rotating token):
```bash
ssh jellyfin
echo <new_token> | docker login lscr.io -u jgitta --password-stdin
echo <new_token> | docker login ghcr.io -u jgitta --password-stdin
```
No script or compose changes needed — the config.json is read at runtime.