Skip to content

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"
Look for Jellyfin processes under the "Processes" section when transcoding is active.

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

  1. Verify the GPU is visible inside the LXC:

    ssh [email protected] "pct exec 114 -- nvidia-smi"
    
    If nvidia-smi fails, the LXC passthrough or host driver is broken.

  2. Check that the Docker nvidia runtime is configured:

    ssh [email protected] "pct exec 114 -- docker info" | grep -i nvidia
    

  3. Check Jellyfin logs for hardware acceleration errors:

    ssh [email protected] "pct exec 114 -- docker logs jellyfin" | grep -i "hardware\|nvenc\|nvidia"
    

  4. 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

  1. Check if the GPU is under load:

    ssh [email protected] "pct exec 114 -- nvidia-smi"
    
    The 750 Ti supports ~2-3 concurrent 1080p H.264 transcodes. Beyond that, performance degrades.

  2. Check Jellyfin logs for transcoding errors:

  3. Grafana → Explore → Loki → {container="jellyfin"} |= "transcode"

  4. 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/** or ansible/playbooks/jellyfin.yml

Credentials

  • Vault path: secret/jellyfinapi_key
  • SSO via PocketID (shared homelab OIDC client)

Gotchas

  • SSO-Auth v4: always use DefaultAuthenticationProvider, never the old SSOAuthenticationProviderPlugin class (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