Jellyfin¶
Jellyfin is a free, open-source media server available at jellyfin.hdhomelab.com. It is the primary streaming server, pinned to worker-0a for Intel GPU access.
Deployment¶
Jellyfin runs in Kubernetes (media namespace) as a single-replica Deployment. Media is served read-only from the shared emby-media NFS volume.
graph LR
User -->|HTTPS| Gateway[Cilium Gateway]
Gateway --> Jellyfin[Jellyfin :8096]
Jellyfin -->|read-only| Media[(NAS /volume1/media)]
Jellyfin -->|config| LocalPVC[local-path PVC]
CronJob[Hourly CronJob] -->|rsync| LocalPVC
CronJob -->|backup| NFS[(NAS backup PVC)]
-
URL
-
Namespace
media -
Image
jellyfin/jellyfin -
Node
Pinned to
worker-0a(Intel GPU) -
Storage
local-pathconfig + sharedemby-mediaNFS volume -
Config
flux/apps/noah/media/jellyfin/
Storage & Backup¶
Config uses local-path (SQLite performance) with an hourly rsync CronJob to NFS — see SQLite on Local Path for the pattern details and migration procedure.
| Volume | PVC | Storage class | Purpose |
|---|---|---|---|
/config |
jellyfin-config |
local-path |
Database and metadata |
/media |
emby-media |
syno-nfs-retain |
Shared NAS media library (read-only) |
Shared media volume
Jellyfin and Emby share the same emby-media PVC (/volume1/media on the NAS).
kubectl create job --from=cronjob/jellyfin-config-backup jellyfin-config-backup-manual -n media
Client IP Logging¶
Real client IPs are logged automatically — no app-level configuration needed. The fix is at the network layer: the router forwards directly to Traefik's macvlan IP, bypassing Docker's userland proxy. See Client IP Preservation.
Key Configuration¶
| Setting | Value |
|---|---|
TZ |
America/New_York |
runAsUser / runAsGroup |
1034 / 100 |
| CPU request / limit | 1 / 4 |
| Memory request / limit | 3 GiB / 8 GiB |
Hardware transcoding
worker-0a has an Intel GPU. Hardware transcoding is not yet enabled — when ready, add gpu.intel.com/i915: "1" resource request and configure Dashboard → Playback → Hardware acceleration → Intel QSV.
Multi-Language Libraries¶
The media library is split into two language segments, each exposed as a separate Jellyfin virtual folder. The split is implemented via symlinks on the NAS — each language folder is a symlink pointing to the corresponding subdirectory under /volume1/media, mounted into Jellyfin as an additional virtual library.
| Folder | Content |
|---|---|
Chinese (cn) |
Chinese-language films, series, and shows |
English (en) |
English-language films and series |
Access is per-user — users only see the folders they have been granted. This is enforced automatically on every login by jellyfin-librarian based on the user's jellyfin_libraries attribute in Authentik.
jellyfin_libraries value |
Folders visible |
|---|---|
cn |
Chinese only |
en |
English only |
all |
Both |
| (not set) | English (default) |
When inviting a new user, choose the appropriate template from the invitation presets — the jellyfin_libraries attribute is set there and flows through automatically.
Direct play only
All users have transcoding and remuxing disabled — streams must be played directly. This is enforced by jellyfin-librarian on every login alongside the folder policy.
User Onboarding¶
New users are onboarded via an invitation link — no manual Jellyfin configuration needed.
graph LR
A[Admin creates invitation] -->|shares link| B[User registers]
B --> C[Authentik account created]
C -->|LDAP| D[User logs into Jellyfin]
D -->|webhook| E[jellyfin-librarian]
E -->|sets library policy| D
style E fill:#27ae60,stroke:#1e8449,color:#fff
- Admin creates an invitation in Authentik with the appropriate
jellyfin_librariesattribute — see Authentik → User Onboarding - User registers via the invitation link — account is created and added to
jellyfin_user(LDAP) automatically - On first login, jellyfin-librarian reads
jellyfin_librariesfrom Authentik and sets the correct library folders and playback policy
No attribute needed for English access
If jellyfin_libraries is not set, jellyfin-librarian defaults to en. Omit the attribute for English-only users.