Install via Docker / Podman

# docker-compose.yml
services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    user: 1000:1000           # match your media files' ownership
    ports:
      - "8096:8096"           # HTTP
      - "8920:8920"           # HTTPS (optional, usually proxied)
      - "7359:7359/udp"       # Client discovery (LAN)
      - "1900:1900/udp"       # DLNA

    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/media/movies:/media/movies:ro
      - /mnt/media/tv:/media/tv:ro
      - /mnt/media/music:/media/music:ro

    # Hardware acceleration via /dev/dri (Intel iGPU / AMD)
    devices:
      - /dev/dri:/dev/dri

    # Or NVIDIA — uncomment if applicable
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - capabilities: [gpu]
    # runtime: nvidia

Mounting media read-only (:ro) is good hygiene — Jellyfin doesn't write to the media itself, only to its cache + metadata in /config.

First-run setup

Browse to http://<host>:8096/:

  1. Pick language.
  2. Create an admin user. Strong password — Jellyfin won't enforce it.
  3. Add libraries:
    • Type: Movies → Folder: /media/movies → Metadata: prefer TheMovieDb
    • Type: Shows → Folder: /media/tv → Metadata: prefer TheMovieDb / TheTVDB
    • Type: Music → Folder: /media/music → Metadata: MusicBrainz
  4. Remote access: disable for now (default port 8096 over plain HTTP isn't safe to expose); set up the reverse proxy below.

First library scan takes a while — Jellyfin fetches posters, cast info, plot summaries from external metadata providers.

Media library file naming

Jellyfin scrapes metadata by parsing the filename. The conventions:

# Movies: one folder per movie, optional year disambiguates
/media/movies/
    The Matrix (1999)/
        The Matrix (1999).mkv
        The Matrix (1999).en.srt
    Dune Part Two (2024)/
        Dune Part Two (2024).mkv

# TV shows: Show / Season / Episode with SxxExx
/media/tv/
    Severance/
        Season 01/
            Severance - S01E01 - Good News About Hell.mkv
            Severance - S01E02 - Half Loop.mkv

# Music: Artist / Album / Track
/media/music/
    Radiohead/
        OK Computer (1997)/
            01 - Airbag.flac
            02 - Paranoid Android.flac

Off-spec names mostly still get matched, but the canonical layout makes mismatches rare.

Reverse proxy with HTTPS

# Caddy
media.example.com {
    reverse_proxy 127.0.0.1:8096

    # Jellyfin uses WebSockets for live state
    @ws {
        path /socket
        header Connection *Upgrade*
        header Upgrade websocket
    }
    handle @ws {
        reverse_proxy 127.0.0.1:8096
    }
}

In Jellyfin: Dashboard → Networking → "Published Server URLs" → add https://media.example.com. This affects how mobile/TV clients construct download URLs.

Hardware-accelerated transcoding

Transcoding (re-encoding a 4K HEVC source on the fly to 1080p H.264 for a phone that can't direct-play it) is CPU-expensive. Hardware acceleration delegates this to the GPU's video encoder/decoder block:

  • Intel iGPU — cheapest hardware option; QuickSync handles H.264, H.265, AV1 (gen 11+). Mount /dev/dri; Dashboard → Playback → Hardware acceleration: VA-API or Intel QSV.
  • AMD GPU — VA-API. Same /dev/dri.
  • NVIDIA — NVENC. Needs the NVIDIA Container Toolkit and a Jellyfin image variant with NVIDIA drivers.
  • Apple Silicon — VideoToolbox. Mac-only.

To verify transcoding is actually using hardware: play a file that requires transcoding, then check Dashboard → Sessions — the session should show "Transcoding (Hardware)."

Per-user permissions

Dashboard → Users → Add. Per user:

  • Which libraries they can see
  • Whether they can download content (vs stream-only)
  • Max streaming bitrate (over LAN / over internet)
  • Parental rating limits
  • Whether they can remote-access

Mobile and TV clients

  • Jellyfin Mobile on iOS / Android — official.
  • Findroid / Streamyfin — third-party Android clients, often nicer UI.
  • Swiftfin — native iOS / tvOS client; the best Apple TV experience.
  • Jellyfin for Android TV — official, mostly stable.
  • Kodi + Jellyfin add-on — for hardcore Kodi users who want Jellyfin as the library backend.
  • Roku — via the Jellyfin Roku channel.
  • LG webOS, Samsung Tizen — community-maintained apps, install instructions on the wiki.

Plugins worth knowing

  • Skip Intro / Outro detector — analyzes audio to detect repeated intros across episodes, surfaces a "Skip Intro" button.
  • Trakt — sync watch state to Trakt.tv for social features.
  • Reports — library stats: how much watched, biggest files, most-watched users.
  • Webhook — trigger notifications on playback events.

Companion ecosystem

  • Sonarr — automatically download and rename new TV episodes.
  • Radarr — same for movies.
  • Bazarr — subtitle automation.
  • Prowlarr — indexer aggregator that feeds Sonarr/Radarr.
  • Jellyseerr — request management for media (users request titles; admin approves; Sonarr/Radarr fetches).

This whole stack ("the *arr stack") is the canonical pairing for serious self-hosted media; Jellyfin is the front-end.

Backups

The ./config directory is everything that matters — users, libraries, watch state, metadata, configuration. A nightly restic job (see that tutorial) on it is sufficient. The media itself is on disk separately; back that up only if losing it would matter (rip again from physical originals if applicable).