--- type: reference title: Jellyfin description: "Jellyfin on Dankserver: quadlet unit, entropy-volume data layout, ports, client access, hardware-transcode status." tags: [jellyfin, dankserver, media-server] timestamp: 2026-09-03T00:00:00Z --- # Jellyfin Jellyfin is the free media server on Dankserver — a podman quadlet service (`jellyfin.service`, generated from `dankserver/jellyfin/quadlet/jellyfin.container`) serving music, movies, and shows over LAN. It replaced the earlier research brief; it runs alongside [[Plex]], which remains the primary server for some clients. **Host:** Dankserver (192.168.0.235) — **Access:** `http://192.168.0.235:8096` public vhost `jelly.franksdank.cloud` behind the [[Anubis]] guard chain → [[Authentik]] outpost (post-auth route proxies `localhost:8096`). ## Facts (verified from the live unit, 2026-09-03) | Item | Value | |------|-------| | Unit | `~/.config/containers/systemd/jellyfin.container` → `jellyfin.service` (user manager), `Restart=always`, `RestartSec=5` | | Image | `ghcr.io/jellyfin/jellyfin:latest` | | Network | `dankserver_appnet`; publishes 8096/tcp, 8920/tcp, 7359/udp, 1900/udp | | Data roots | `/home/dank/Containers/jellyfin/config:/config`, `/home/dank/Containers/jellyfin/cache:/cache` | | Media | `Volume=/mnt/entropy:/data:ro` — the whole [[Entropy Volume]] mounted read-only; nothing automated writes to it | | GPU | none — no `AddDevice`; CPU decode only (jellyfin-ffmpeg path) | | Env quirks | `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`, `LD_PRELOAD=libjemalloc.so.2`, `MALLOC_TRIM_THRESHOLD_=131072` kept verbatim from the compose-era container (zero-drift cutover) | | Health | `curl -f http://localhost:8096/health` every 30 s | ## Data layout ``` /home/dank/Containers/jellyfin/config -> /config (JELLYFIN_DATA_DIR) /home/dank/Containers/jellyfin/cache -> /cache (JELLYFIN_CACHE_DIR, XDG_CACHE_HOME) /mnt/entropy -> /data (read-only media volume) ``` Library folders point at `/data` paths; because the mount uses the same absolute path as on the host, library paths survive container recreation. ## Ports & access | Port | Proto | Purpose | |------|-------|---------| | 8096 | TCP | Web UI + API (`JELLYFIN_PublishedServerUrl=http://192.168.0.235:8096`) | | 8920 | TCP | DLNA/UPnP root device control | | 7359 | UDP | service discovery (client auto-discovery) | | 1900 | UDP | SSDP discovery | Client access paths and the public-exposure policy (auth wall required before any public vhost; Plex-style claim tokens do not exist here — Jellyfin uses its own users/API keys) follow the fleet [[Authentik]]/[[Anubis]] chain pattern. ## Authentik LDAP authentication (2026-09-25) Jellyfin has no native external-auth, so it authenticates users through the **LDAP-Auth plugin** (`jellyfin-plugin-ldapauth`) against an [[Authentik]] LDAP provider. A user logs in with their **authentik username + password**; the plugin binds as a service account, searches the directory, and rebinds as the user to verify the password. **authentik side** (on Dankclaw): - LDAP provider `Jellyfin LDAP` (Base DN `dc=franksdank,dc=cloud`, bind/search mode `direct`) + application `Jellyfin`. - LDAP outpost `Jellyfin LDAP Outpost` → container `authentik-outpost-ldap` (quadlet `dankclaw/authentik/quadlet/authentik-outpost-ldap.container`) publishing `389`/`636` on the Dankclaw host for cross-host binds. - Service account `svc-jellyfin-ldap` (DN `cn=svc-jellyfin-ldap,ou=users,dc=franksdank,dc=cloud`). - Access groups `jellyfin_users` (search filter `(memberOf=cn=jellyfin_users,ou=groups,dc=franksdank,dc=cloud)`) and `jellyfin_admins` (admin filter `(cn=jellyfin_admins)`). Add a user to `jellyfin_users` to grant them Jellyfin access; `jellyfin_admins` grants the Jellyfin admin role. **Jellyfin side** (plugin config, set via admin → Plugins → LDAP-Auth): - Server `192.168.0.143:389` (plain LDAP over the trusted LAN), Base DN `dc=franksdank,dc=cloud`, bind user = the service-account DN, search attributes `uid,cn,mail,displayName`, uid attr `uid`, username attr `cn` `CreateUsersFromLdap=true`, all folders enabled. **Gotcha — the bind credential is a PASSWORD, not a token.** The LDAP bind is verified by authentik's default authentication flow (standard-password backend). The service account must have a password set (admin → user → Recovery → Set password); an API/app token is *not* accepted as the bind password and the bind fails with `flow error password: Invalid`. The service-account password is stored only in the Jellyfin plugin config (the `LdapBindPassword` field) — it is never committed to this repo. **Verification (2026-09-25):** `ldapsearch` bind + the `jellyfin_users` search both succeed from Dankclaw; a real user (`alex`, in `jellyfin_users`) logged into the web UI with their authentik password and the plugin created the matching Jellyfin user. **Gotcha — the login endpoint changed in Jellyfin 12.1.** The legacy `POST /Users/Auth/Password` route (and its `LegacyEmbyRouteRewriteMiddleware`) was removed; probing it now returns a silent 401 with no plugin log, which reads like an auth failure but is just a dead route. The web UI and modern clients use `POST /Users/AuthenticateByName` (with the `Authorization: MediaBrowser Client=…, DeviceId=…, Device=…, Version=…` header), which is where the LDAP plugin runs. Verified 2026-09-25: `alex` authenticates over `AuthenticateByName` (HTTP 200; full plugin flow logged — service bind, search, `ldapUid`, user bind, token). ## Configuration workflow 1. Create libraries pointing at `/data` folders (movies, shows, audio). 2. Set metadata/language preferences (`LANG=en_US.UTF-8` pinned in the unit). 3. Configure clients to use `http://192.168.0.235:8096` or the guarded vhost. ## See Also - [[Plex]] — co-resident media server on the same volume - [[Entropy Volume]] — the 91 T array mounted read-only at `/data` - [[Caddy]] · [[Anubis]] · [[Authentik]] — edge chain for `jelly.franksdank.cloud`