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

3.6 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
  • 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
watchtower-prowlarr containrrr/watchtower see below

Media volume: /mnt/media on jellyfin host → mounted as /data in radarr/sonarr.


Watchtower — Split-Instance Setup

Two Watchtower instances run in the arr-stack to stagger registry checks and avoid rate limiting lscr.io/ghcr.io with too many concurrent requests.

Instance Schedule Scope Manages
watchtower 4:00 AM (none) radarr, sonarr, flaresolverr, portainer-agent
watchtower-prowlarr 4:05 AM prowlarr prowlarr only

The scope works via Docker labels: prowlarr has com.centurylinklabs.watchtower.scope=prowlarr — this tells the main watchtower to skip it, and tells watchtower-prowlarr to manage it.

Both instances mount /home/jgitta/.docker/config.json:/config.json:ro for authenticated registry pulls.


Registry Authentication (fixed 2026-05-20)

Problem: Watchtower was hitting lscr.io/ghcr.io rate limits and timeouts with unauthenticated requests. lscr.io is a proxy that redirects to ghcr.io for the actual registry API — credentials for lscr.io alone were not enough; ghcr.io also needed auth. Two separate issues were compounding:

  1. Unauthenticated requests shared a small anonymous quota
  2. Two concurrent Watchtower instances were doubling all requests at 4 AM

Fix:

Three sets of credentials were configured — both as the jgitta user (for Watchtower's own registry digest checks) and as root (for the Docker daemon's actual image pulls):

# As jgitta (Watchtower reads /home/jgitta/.docker/config.json via volume mount)
echo <token> | docker login lscr.io -u jgitta --password-stdin
echo <token> | docker login ghcr.io -u jgitta --password-stdin

# As root (Docker daemon uses /root/.docker/config.json for docker pull)
echo <token> | sudo docker login lscr.io -u jgitta --password-stdin
echo <token> | sudo docker login ghcr.io -u jgitta --password-stdin

Token used: the no-expiry read:packages GitHub PAT in credentials.md.

If token ever needs rotating: run the four docker login commands above on jellyfin with the new token. No compose changes needed — both config files are read live.


Notes

  • Do NOT run docker restart watchtower manually while watchtower-prowlarr is running — Watchtower's multi-instance detection will cause one to kill the other. Use docker restart watchtower watchtower-prowlarr to restart both together, or let Portainer manage redeployment.
  • Portainer stack 35 is the authoritative runtime config. If it drifts from Gitea, update via Portainer API with prune: true to remove orphaned services.