Files
serverAppConfigs/serverAppConfigs-docs/Dankserver/Jellyfin.md
T
Frank 272ef6f0b0 docs: record quadlet standardization of dankserver media + dankclaw data roots
Phase 1/2 of the podman standardization plan: service pages updated to the
quadlet reality (units, Containers data roots, env-file secret convention,
the v1-generator findings each caught live), Storage Policy documents the
two rootless ownership regimes and the merge-move migration, Deployment
Flow's service-manager table no longer claims dankserver media is bare
inventory-only. Bundle change log entry added.
2026-09-01 13:57:33 -07:00

7.4 KiB
Raw Blame History

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.

Host: Dankserver — Hostname: jellyfin.dankserver.local — Docs: https://jellyfin.org

Facts

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

Architecture

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]

Docker Compose (reference — retired 2026-09-01; live mounts use /home/dank/Containers/jellyfin, see Deployment below)

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

Caddy Configuration

jellyfin.dankserver.local {
    reverse_proxy 127.0.0.1:8096

    transport http {
        versions h2c 1.1
    }
}

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 (default): Per-user libraries, parental controls, max bitrate limits. Manage via Dashboard → Users.
  • Caddy basic auth: Single login layer. Does not integrate with per-user Jellyfin permissions.
  • LDAP/OIDC: Available via plugins (LDAP-Auth, Jellyfin SSO). Integrates with Authentik, Keycloak, etc.

For Dankserver, built-in auth is recommended. Use Caddy basic auth on top for external access.

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.

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