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:
- Unauthenticated requests shared a small anonymous quota
- 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 watchtowermanually whilewatchtower-prowlarris running — Watchtower's multi-instance detection will cause one to kill the other. Usedocker restart watchtower watchtower-prowlarrto 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: trueto remove orphaned services.