docs: rename frank-default-large/small/sightsound to frank-large/small/npu across vault
Mechanical identity rename across the knowledge base, AGENTS.md, README, the
hermes config example, and homepage services.json. No behavior change; served
aliases on the LLM endpoints change with the deploy of commit 69f87c8.
This commit is contained in:
1 parent
5a083eee6c
commit
8212d4451a
68 files changed
+1053
-1207
No files matched your search
@@ -1,226 +1,68 @@
|
||||
---
|
||||
type: reference
|
||||
title: Jellyfin
|
||||
description: Jellyfin media server on Dankserver — quadlet unit, entropy-volume data layout, ports, client access, and hardware-transcode status.
|
||||
tags: [jellyfin, dankserver, media-server]
|
||||
timestamp: 2026-09-03T00:00:00Z
|
||||
---
|
||||
|
||||
# Jellyfin
|
||||
|
||||
Jellyfin is a free, open-source media server and suite of applications for organizing, managing, and streaming personal media collections (movies, TV shows, music, photos) to networked devices. It is a community fork of Emby, written in C# (.NET), and uses FFmpeg for transcoding.
|
||||
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 — **Hostname:** `jellyfin.dankserver.local` — **Docs:** https://jellyfin.org
|
||||
**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
|
||||
## Facts (verified from the live unit, 2026-09-03)
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Latest version | Check https://jellyfin.org/downloads |
|
||||
| Image | `jellyfin/jellyfin:latest` (Docker Hub) or `ghcr.io/jellyfin/jellyfin` |
|
||||
| Base | Debian-based |
|
||||
| Primary port | 8096 (HTTP, web UI + API) |
|
||||
| HTTPS port | 8920 (optional) |
|
||||
| Client discovery | 7359/udp |
|
||||
| DLNA | 1900/udp (optional) |
|
||||
| GPU passthrough | `/dev/dri` (Intel QSV / AMD VA-API) or NVIDIA NVENC |
|
||||
| License | MIT |
|
||||
| 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 |
|
||||
|
||||
## Architecture
|
||||
## Data layout
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
U[User] --> C[Caddy :80/:443]
|
||||
C --> J[Jellyfin :8096]
|
||||
J --> Lib[/media/movies]
|
||||
J --> Lib2[/media/shows]
|
||||
J --> Lib3[/media/music]
|
||||
J --> Conf[Jellyfin config]
|
||||
J --> Cache[Jellyfin cache]
|
||||
```
|
||||
/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)
|
||||
```
|
||||
|
||||
## Docker Compose (reference — retired 2026-09-01; live mounts use `/home/dank/Containers/jellyfin`, see Deployment below)
|
||||
Library folders point at `/data` paths; because the mount uses the same
|
||||
absolute path as on the host, library paths survive container recreation.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
jellyfin:
|
||||
image: jellyfin/jellyfin:latest
|
||||
container_name: jellyfin
|
||||
restart: unless-stopped
|
||||
user: "1000:1000"
|
||||
ports:
|
||||
- "8096:8096/tcp"
|
||||
- "8920:8920/tcp"
|
||||
- "7359:7359/udp"
|
||||
- "1900:1900/udp"
|
||||
environment:
|
||||
- JELLYFIN_PublishedServerUrl=http://192.168.0.235:8096
|
||||
- DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true
|
||||
volumes:
|
||||
- /opt/jellyfin/config:/config
|
||||
- /opt/jellyfin/cache:/cache
|
||||
- /mnt/media/movies:/data/movies:ro
|
||||
- /mnt/media/shows:/data/shows:ro
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
group_add:
|
||||
- "992" # Host render group GID — verify with `getent group render`
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
## Ports & access
|
||||
|
||||
## Caddy Configuration
|
||||
| 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 |
|
||||
|
||||
```caddy
|
||||
jellyfin.dankserver.local {
|
||||
reverse_proxy 127.0.0.1:8096
|
||||
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.
|
||||
|
||||
transport http {
|
||||
versions h2c 1.1
|
||||
}
|
||||
}
|
||||
```
|
||||
## Configuration workflow
|
||||
|
||||
Set **Base URL** to `/` (or the path you configure) in Jellyfin Dashboard → Networking. Add `127.0.0.1` to **Known Proxies**.
|
||||
|
||||
## Storage
|
||||
|
||||
| Mount | Purpose | Size Estimate |
|
||||
|-------|---------|---------------|
|
||||
| `/home/dank/Containers/jellyfin/config` | SQLite database, settings, cached images | 10–100 GB |
|
||||
| `/home/dank/Containers/jellyfin/cache` | Transcoding segments, extracted posters | Self-cleaning per session |
|
||||
| `/mnt/entropy/Movies`, `/mnt/entropy/TVShows` | Media, **read-only binds** (`:ro`) — no automated write path | — |
|
||||
|
||||
The database must be on local storage (SSD/NVMe preferred). Media libraries can be on NFS/mergerfs. If media is on NFS, set `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`.
|
||||
|
||||
## Deployment (Quadlet)
|
||||
|
||||
Since 2026-09-01: `dankserver/jellyfin/quadlet/jellyfin.container` →
|
||||
`jellyfin.service` (generator-enabled), deployed with
|
||||
`./scripts/deploy.sh dankserver --service jellyfin`. GPU passthrough uses
|
||||
`AddDevice=/dev/dri` — plain `Device=` is **not** a quadlet key and makes the
|
||||
v1 generator reject the whole unit (learned live: "unsupported key 'Device'
|
||||
in group 'Container'"). Entropy mounts stay `:ro`; the container runs as
|
||||
root inside the user namespace, so the render-node ACLs apply via the device
|
||||
cgroup regardless of `GroupAdd=keep-groups`.
|
||||
|
||||
## Authentication
|
||||
|
||||
- **Built-in auth:** per-user libraries, parental controls, max bitrate
|
||||
limits. The local `dank` admin account is built-in; its password is
|
||||
Frank's bootstrap value — never recorded in this repo. (2026-09-01: a
|
||||
corrupted session-archive reading briefly suggested the password had
|
||||
changed; verified offline against the stored PBKDF2-SHA512 hash, the
|
||||
bootstrap value still authenticates.)
|
||||
- **LDAP via Authentik (live 2026-09-01):** the LDAP-Auth plugin
|
||||
authenticates users against the authentik LDAP outpost on Dankclaw — see
|
||||
[[Authentik]] for the outpost unit, provider, and RBAC objects.
|
||||
|
||||
### LDAP-Auth plugin (v23.0.0.0, live 2026-09-01)
|
||||
|
||||
- **Install from the GitHub releases zip** — `repo.jellyfin.org`'s stable
|
||||
manifest path 404s here, so fetch
|
||||
`jellyfin/jellyfin-plugin-ldapauth` release asset
|
||||
`ldap-authentication_23.0.0.0.zip` from GitHub and unzip into
|
||||
`config/plugins/LDAP-Auth_23.0.0.0/`. The config lives at
|
||||
`config/plugins/configurations/LDAP-Auth.xml` — write it directly, then
|
||||
restart the service.
|
||||
- **Live config:** server `192.168.0.143:389`, base DN
|
||||
`dc=franksdank,dc=cloud`, bind DN
|
||||
`cn=svc-jellyfin-ldap,ou=users,dc=franksdank,dc=cloud`, bind password =
|
||||
the authentik outpost token (secret — the XML lives on the host only,
|
||||
never in the repo), search filter
|
||||
`(memberOf=cn=jellyfin_users,ou=groups,dc=franksdank,dc=cloud)`, admin
|
||||
filter on `cn=jellyfin_admins,ou=groups,…`, `CreateUsersFromLdap=true`
|
||||
(auto-create on first login).
|
||||
- **Attribute separator is a comma.** `LdapSearchAttributes` is
|
||||
`Split(',')` in the plugin source — a semicolon-separated list
|
||||
(`uid;cn;mail;displayName`) parses as ONE bogus attribute, the search
|
||||
silently matches nothing, and the log says `Found no users matching X`.
|
||||
Use `uid,cn,mail,displayName`.
|
||||
- **Username attribute is `cn`.** Authentik 2026.x maps `uid` to an opaque
|
||||
per-user hash, not the username, so `uid=<name>` never matches. Set
|
||||
`LdapUsernameAttribute=cn` (authentik exposes the username as `cn`).
|
||||
- **Verify with a throwaway user:** create a test member of
|
||||
`jellyfin_users` in authentik, then
|
||||
`POST /Users/AuthenticateByName` with headers `Content-Type:
|
||||
application/json` **and** `X-Emby-Authorization: MediaBrowser
|
||||
Client="…", Device="…", DeviceId="…", Version="10.11.0"` — both are
|
||||
required in 10.11 (missing `X-Emby-Authorization` → 400). 200 + non-admin
|
||||
policy = LDAP path works. Delete the test user from authentik **and**
|
||||
Jellyfin afterward.
|
||||
- **Plugin load lines only show in `journalctl --user -u jellyfin.service`**
|
||||
(`Loaded plugin: LDAP-Auth 23.0.0.0`) — `podman logs` does not carry the
|
||||
loader lines in this image.
|
||||
|
||||
## Metadata Scraping
|
||||
|
||||
| Provider | Content | Type |
|
||||
|----------|---------|------|
|
||||
| TheMovieDb (TMDb) | Movies, TV shows, posters, fan art, overviews | Default |
|
||||
| OMDb API | Movies only, English metadata | Default |
|
||||
| TheTVDB | TV shows, episodes | Plugin |
|
||||
| fanart.tv | Extra artwork, banners, thumbnails | Plugin |
|
||||
| AniDB | Anime metadata | Plugin |
|
||||
| MusicBrainz | Music metadata | Plugin |
|
||||
| Open Subtitles | Subtitle downloads | Plugin |
|
||||
| Intro Skipper | TV show intro detection | Plugin |
|
||||
|
||||
## Clients
|
||||
|
||||
| Platform | App | Quality |
|
||||
|----------|-----|---------|
|
||||
| Web | Jellyfin Web | Excellent |
|
||||
| Android | Jellyfin (Google Play / F-Droid) | Excellent |
|
||||
| Android TV / Fire TV | Jellyfin | Good |
|
||||
| iOS / iPadOS | Swiftfin (third-party) | Good |
|
||||
| Desktop | Jellyfin Media Player (mpv-based) | Excellent |
|
||||
| Roku | Jellyfin (Channel Store) | Good |
|
||||
| Kodi | Jellyfin addon | Excellent |
|
||||
| Samsung/LG Smart TVs | TV browser | Fair |
|
||||
|
||||
## Jellyfin vs Plex
|
||||
|
||||
| Feature | Jellyfin | Plex |
|
||||
|---------|----------|------|
|
||||
| Cost | Free, no tiers | Free core; Plex Pass ($5/mo or $120 lifetime) |
|
||||
| License | MIT | Proprietary |
|
||||
| Account required | No | Yes |
|
||||
| Hardware transcoding | Free | Plex Pass required |
|
||||
| Mobile sync | Free | Plex Pass |
|
||||
| Live TV / DVR | Free | Plex Pass |
|
||||
| Client apps | Good, improving | Excellent, widest device support |
|
||||
|
||||
Run Jellyfin alongside Plex for an incremental migration. Point Jellyfin at the same media folders as Plex. Watch history and metadata will not transfer cleanly.
|
||||
|
||||
## Resource Requirements
|
||||
|
||||
- **Idle / Direct Play:** ~200–500 MB RAM, <5% CPU. Negligible.
|
||||
- **1080p transcode (CPU):** 40–60% CPU on a modern multi-core CPU.
|
||||
- **1080p transcode (with QSV):** 3–5% CPU.
|
||||
- **4K HDR transcode (with QSV):** 5–15% CPU.
|
||||
- **Minimum host RAM:** 4 GB. 8 GB comfortable for transcoding.
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
1. Verify GPU on host: `ls -l /dev/dri/`, `lspci | grep -i vga`, `getent group render`.
|
||||
2. Install media drivers: `sudo apt install intel-media-va-driver-non-free vainfo intel-gpu-tools`.
|
||||
3. Create directories: `/home/dank/Containers/jellyfin/config`, `/home/dank/Containers/jellyfin/cache`.
|
||||
4. Write compose file, verify device and GID.
|
||||
5. Deploy: `podman compose up -d`.
|
||||
6. Test GPU passthrough: `podman exec jellyfin /usr/lib/jellyfin-ffmpeg/vainfo --display drm --device /dev/dri/renderD128`.
|
||||
7. Configure Caddy reverse proxy. Set Known Proxies in Jellyfin.
|
||||
8. Add media libraries in Dashboard.
|
||||
9. Install plugins: Open Subtitles, fanart.tv, Intro Skipper.
|
||||
10. Test playback on Android/Chrome.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **GPU passthrough silently failing:** `/dev/dri` must be passed and the correct render node used. Verify with `vainfo` inside the container.
|
||||
- **Wrong render group GID:** Common GID is 992, but varies per host. Run `getent group render` to find it.
|
||||
- **Subtitle burn-in triggers full transcode:** PGS (image-based) subtitles force full video transcoding. SRT/ASS (text-based) play fine.
|
||||
- **Database on NFS:** SQLite on network storage causes slow startup and corruption risk. Keep `/config` on local storage.
|
||||
- **Client bitrate limit too low:** Many clients default to 20 Mbps. Set Max Streaming Bitrate to Original or Maximum.
|
||||
- **Naming convention mismatches:** Non-standard folder/file names cause missing metadata. Use `Movie Title (Year)` for movies and `Show Name/Season 01/Show Name S01E01.mkv` for TV.
|
||||
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
|
||||
|
||||
- [[Dankserver Stack]]
|
||||
- [[ARR-Stack]] — automated download into Jellyfin libraries
|
||||
- [[Copyparty]] — alternative file viewer for existing collections
|
||||
- Plex — existing media server on this host
|
||||
- [[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`
|
||||
Reference in new issue
Block a user