Jellyfin — Runbook
Routine Tasks
Check health
curl http://192.168.1.114:8096/health
# Expected: "Healthy"
Check GPU usage
ssh [email protected] "pct exec 114 -- nvidia-smi"
Restart Jellyfin
ssh [email protected] "pct exec 114 -- docker restart jellyfin"
Logs
| Log | Contents | Location | Loki query | Format |
|---|---|---|---|---|
| Application | Stream events, transcoding jobs, library scans, GPU usage, errors | Docker (LXC 114) stdout | {job="jellyfin", container="jellyfin"} |
Plain text |
Notes:
- SSH fallback: ssh [email protected] "docker logs jellyfin --tail 100"
- For transcoding issues, cross-reference with GPU status: ssh [email protected] "nvidia-smi"
Troubleshooting
GPU not detected by Jellyfin
-
Verify the GPU is visible inside the LXC:
Ifssh [email protected] "pct exec 114 -- nvidia-smi"nvidia-smifails, the LXC passthrough or host driver is broken. -
Check that the Docker nvidia runtime is configured:
ssh [email protected] "pct exec 114 -- docker info" | grep -i nvidia -
Check Jellyfin logs for hardware acceleration errors:
ssh [email protected] "pct exec 114 -- docker logs jellyfin" | grep -i "hardware\|nvenc\|nvidia" -
If the host GPU setup needs to be redone, trigger the Jellyfin Host GPU Setup workflow (workflow_dispatch) and then redeploy Jellyfin.
Transcoding fails or stutters
-
Check if the GPU is under load:
The 750 Ti supports ~2-3 concurrent 1080p H.264 transcodes. Beyond that, performance degrades.ssh [email protected] "pct exec 114 -- nvidia-smi" -
Check Jellyfin logs for transcoding errors:
-
Grafana → Explore → Loki →
{container="jellyfin"} |= "transcode" -
Verify transcoding settings in Administration → Playback → Transcoding — hardware acceleration should be set to NVIDIA NVENC with only H.264 enabled.
HEVC / VP9 / AV1 content slow to transcode
This is expected. The GTX 750 Ti does not support hardware decoding or encoding for HEVC, VP9, or AV1. These codecs fall back to CPU transcoding, which is significantly slower.
Options:
- Prefer direct play — set client quality to "Original" to avoid transcoding entirely (requires sufficient bandwidth)
- Pre-transcode to H.264 — convert problematic files ahead of time with ffmpeg so they can be hardware-transcoded or direct-played
- Accept CPU fallback — 6 cores can handle 1-2 concurrent CPU transcodes at reasonable speed
Remote streaming performance
For users streaming outside the LAN:
- Set a remote streaming bitrate limit in Administration → Playback to avoid saturating upload bandwidth
- Prefer H.264 content for remote users — it hardware-transcodes efficiently if the client needs a lower bitrate
- If buffering occurs, check whether the transcode is CPU (HEVC/VP9/AV1 source) or GPU (H.264 source) via nvidia-smi and Loki logs
Shokofin VFS
After Shokofin creates its VFS symlink structure under /config/Shokofin/VFS/, series may show up in the UI but with 0 episodes. With the realtime pipeline configured (see setup.md "Realtime anime pipeline"), this should resolve itself within seconds. If it doesn't, force a scan:
curl -s -X POST -H "Authorization: MediaBrowser Token=$JELLYFIN_API_KEY" \
http://192.168.1.114:8096/Library/Refresh
Always trigger a library scan after first-time Shokofin configuration or after adding new shows to Shoko Server that were imported pre-SignalR.
Troubleshooting: new episode not showing up
The Seanime → Shoko → Shokofin → Jellyfin chain has multiple failure points. Walk the chain in this order:
| Check | Where | Command |
|---|---|---|
| 1. File on disk | chizuru | ls /mnt/seedbox/normal/seasonal/<show>/ |
| 2. Shoko sees the file (and matched to a series) | Shoko API | curl -H "apikey: $SHOKO_KEY" "http://192.168.1.116:8111/api/v3/ImportFolder/5/File" \| jq '.List[].Locations[0].RelativePath' |
| 3. Shokofin SignalR connection | Jellyfin logs | pct exec 114 -- docker logs --since 1h jellyfin \| grep SignalRConnectionManager — look for Connected to Shoko Server |
| 4. Per-library event flags | Shokofin plugin config | curl -H "X-Emby-Token: $JF_KEY" $JF/Plugins/5216ccbf-d24a-4eb3-8a7e-7da4230b7052/Configuration \| jq '.Libraries[] \| {Name, IsFileEventsEnabled, IsRefreshEventsEnabled}' — both must be true |
| 5. VFS symlink generated | Jellyfin LXC | pct exec 114 -- find /opt/jellyfin/config/Shokofin/VFS/<library-uuid>/ -iname '*<series>*' |
| 6. Jellyfin sees the episode | Jellyfin API | curl -H "X-Emby-Token: $JF_KEY" "$JF/Shows/<series-id>/Episodes" |
Most common cause: step 4 — IsRefreshEventsEnabled is false on the library. Shokofin makes the symlink (step 5 passes) but Jellyfin doesn't get a refresh trigger and the new file waits for the next scheduled scan. The jellyfin.yml playbook flips this on for all three Shokofin libraries.
If SignalR is connected but events aren't flowing, restart Jellyfin's Shokofin plugin connection by re-saving the Shokofin config (any POST to /Plugins/.../Configuration reconnects):
curl -H "X-Emby-Token: $JF_KEY" \
"$JF/Plugins/5216ccbf-d24a-4eb3-8a7e-7da4230b7052/Configuration" \
| curl -X POST -H "X-Emby-Token: $JF_KEY" -H "Content-Type: application/json" \
"$JF/Plugins/5216ccbf-d24a-4eb3-8a7e-7da4230b7052/Configuration" --data-binary @-
Automation
Deployment
- Deploy via Forgejo Actions workflow
.forgejo/workflows/jellyfin.yml - Trigger: push to
services/jellyfin/**oransible/playbooks/jellyfin.yml
Credentials
- Vault path:
secret/jellyfin→api_key - SSO via PocketID (shared homelab OIDC client)
Gotchas
- SSO-Auth v4: always use
DefaultAuthenticationProvider, never the oldSSOAuthenticationProviderPluginclass (see reference.md) - After Shokofin config changes, always trigger
POST /Library/Refresh - Privileged LXC — no uid remapping needed for bind mounts
- GPU passthrough: device nodes (
/dev/nvidia*) must be bind-mounted into the LXC config