2.9 KiB
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):
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.