Jellyfin Setup on Docker

By LK Wood IV · 2026-06-13 · ~12 min read · St. Louis County, MO

Jellyfin is a free, open-source media server. No subscription, no required cloud account, no phoning home. It runs on your own hardware, serves your own files, and the transcoding works whether you pay anyone money or not.

This guide sets up Jellyfin in Docker with hardware transcoding for Intel, NVIDIA, and AMD GPUs, HTTPS via Nginx Proxy Manager, and Tailscale remote access.

What you’ll have at the end

  • Jellyfin running in Docker on your homelab host
  • Hardware transcoding configured (Intel QSV, NVIDIA, or AMD VAAPI)
  • HTTPS access at jellyfin.yourdomain.com via Nginx Proxy Manager
  • Remote access via Tailscale without opening ports
  • Metadata scraping from TMDB and TheTVDB

Prerequisites

  • Docker installed on a Linux host (Proxmox LXC, VM, or bare-metal Debian/Ubuntu)
  • For NVIDIA transcoding: NVIDIA Container Toolkit installed
  • For Intel QSV: Intel 6th gen+ CPU (built-in GPU with Quick Sync)
  • Nginx Proxy Manager running for HTTPS (or skip and use LAN IP)

Step 1: Prepare media directories

Decide where your media lives before starting the container. The paths you choose here become the volume mounts in Docker Compose and can’t be easily changed later without restarting and re-adding libraries in the UI.

# Example structure on a NAS mount or local disk
mkdir -p /mnt/media/{movies,tv,music,photos}

# Or on local storage
mkdir -p /opt/media/{movies,tv}

Set ownership if running Jellyfin as non-root:

# Jellyfin container runs as UID/GID 1000 by default with linuxserver image
chown -R 1000:1000 /mnt/media

Step 2: Docker Compose

mkdir -p /opt/stacks/jellyfin && cd /opt/stacks/jellyfin

Base compose file (CPU transcoding or Intel QSV):

# /opt/stacks/jellyfin/docker-compose.yml
services:
  jellyfin:
    image: lscr.io/linuxserver/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Chicago
      - JELLYFIN_PublishedServerUrl=https://jellyfin.yourdomain.com  # optional
    volumes:
      - ./config:/config
      - /mnt/media/movies:/data/movies
      - /mnt/media/tv:/data/tv
      - /mnt/media/music:/data/music
      - /tmp/jellyfin-transcode:/transcode   # SSD path for transcode cache
    ports:
      - 8096:8096    # HTTP
      - 8920:8920    # HTTPS (optional, if not using NPM)
    networks:
      - proxy
    # Intel QSV — add this if you have an Intel CPU with Quick Sync:
    # devices:
    #   - /dev/dri:/dev/dri
    # group_add:
    #   - "video"
    #   - "render"

networks:
  proxy:
    external: true

For Intel Quick Sync (QSV/VAAPI) — uncomment the device section above:

    devices:
      - /dev/dri:/dev/dri
    group_add:
      - "video"
      - "render"

Verify the /dev/dri device exists on the host:

ls /dev/dri
# renderD128  card0   (you should see renderD128 at minimum)

For NVIDIA GPU transcoding — use this compose instead:

services:
  jellyfin:
    image: lscr.io/linuxserver/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Chicago
    volumes:
      - ./config:/config
      - /mnt/media/movies:/data/movies
      - /mnt/media/tv:/data/tv
      - /tmp/jellyfin-transcode:/transcode
    ports:
      - 8096:8096
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
    networks:
      - proxy

networks:
  proxy:
    external: true

The NVIDIA Container Toolkit must be installed on the host. See the GPU transcoding guide for the toolkit install commands.

Start Jellyfin:

docker compose up -d
docker compose logs -f jellyfin

First startup takes 60–90 seconds as Jellyfin initializes its database. Access at http://your-host-ip:8096.

Step 3: Initial setup wizard

The wizard walks you through:

  1. Language — pick your locale
  2. Create admin account — use a strong password
  3. Add media libraries — this is where your /data/movies, /data/tv mounts are used:
    • Add library → Content type: Movies → Add folder → /data/movies
    • Add library → Content type: Shows → Add folder → /data/tv
  4. Preferred metadata language — English (or your locale)
  5. Enable remote access — leave on for now, configure access control after

After the wizard, Jellyfin starts scraping metadata from TMDB/TheTVDB immediately. For a large library, this takes 10–30 minutes.

Step 4: Enable hardware transcoding

In Jellyfin → Dashboard → Playback → Transcoding:

Intel Quick Sync (most Intel CPUs 6th gen+):

  • Hardware acceleration: Intel QuickSync (QSV)
  • Enable hardware encoding: on
  • H.264, HEVC, VP9, AV1 encode/decode: enable all that your CPU supports

NVIDIA GPU:

  • Hardware acceleration: Nvidia NVENC
  • Enable hardware encoding: on
  • H.264, HEVC, AV1 encode/decode: enable based on your GPU generation (RTX 4000+ supports AV1)

AMD (VAAPI):

  • Hardware acceleration: Video Acceleration API (VAAPI)
  • VA API device: /dev/dri/renderD128
  • Enable hardware encoding: on

Verify transcoding is working:

Play a video and intentionally trigger a transcode by selecting a lower quality than the source (e.g., source is 4K HEVC, select “1080p 8Mbps” in the quality settings). Watch the Jellyfin dashboard → Active Sessions — you should see the stream marked as HEVC → H264 (hw) or similar, with GPU utilization on the host.

On a mini PC with Intel N-series, a single 4K → 1080p HEVC transcode should consume under 15% CPU with Quick Sync active (vs 80–100% without it).

Step 5: Nginx Proxy Manager HTTPS

In NPM → Add Proxy Host:

  • Domain: jellyfin.yourdomain.com
  • Scheme: http
  • Forward hostname: jellyfin (container name, on proxy network)
  • Port: 8096
  • Websockets Support: ON (required for Jellyfin’s sync features)
  • SSL: wildcard cert, Force SSL on, HTTP/2 support on

Access Jellyfin at https://jellyfin.yourdomain.com.

Optional — allow direct access for LAN clients:

Some Jellyfin clients (especially smart TV apps) work better with a direct LAN IP if the DNS resolves. Add 192.168.1.X to the Jellyfin “Known Proxies and Networks” list in Dashboard → Networking so Jellyfin knows to trust the X-Forwarded-For header from NPM.

Step 6: Remote access via Tailscale

The simplest secure remote access is Tailscale:

  1. Install Tailscale on your homelab host (or in a Proxmox LXC)
  2. Enable subnet routing to advertise your homelab LAN: tailscale up --advertise-routes=192.168.1.0/24
  3. On each device (phone, laptop, etc.) that you want Jellyfin access from, install the Tailscale app and connect
  4. Access Jellyfin via the local LAN IP or via the NPM domain if you’ve set AdGuard Home DNS rewrites

The Jellyfin iOS/Android apps work identically over Tailscale as they do on your local network. No special configuration needed in Jellyfin itself.

Step 7: Jellyfin app clients

PlatformAppNotes
AndroidJellyfin (official)Play Store
iOS / iPadOSJellyfin (official)App Store
Apple TVJellyfin (official)tvOS App Store
Android TV / Fire TVJellyfin (official)Available on both stores
RokuJellyfin for RokuChannel Store
Web browserBuilt-in web clientAt your Jellyfin URL
Desktop (Windows/macOS/Linux)Jellyfin Media PlayerGitHub releases
KodiJellyfin for KodiKodi repository

On Apple TV and Android TV, the Jellyfin app supports Direct Play for most common formats (H.264/HEVC MP4/MKV containers). If the container or audio codec requires a transcode, hardware transcoding on the server handles it.

Library organization best practices

Name your files correctly. Jellyfin uses the TMDB naming convention:

/data/movies/The Dark Knight (2008)/The Dark Knight (2008).mkv
/data/tv/Breaking Bad/Season 01/Breaking Bad S01E01.mkv

The year in the movie folder name is required for disambiguation. TV shows need Season folders with SxxExx naming. Wrong naming is the #1 reason metadata fails to scrape.

Extras and specials:

/data/movies/The Dark Knight (2008)/
  The Dark Knight (2008).mkv
  extras/
    The Dark Knight Behind the Scenes.mkv

Jellyfin picks up extras from an extras/ subdirectory and shows them in the detail view.

Subtitles:

External subtitle files go in the same directory as the video with matching name:

The Dark Knight (2008).mkv
The Dark Knight (2008).en.srt        # English SRT
The Dark Knight (2008).en.forced.srt  # Forced English subtitles

Jellyfin surfaces these in the subtitle selection in the player.

Performance reference

Hardware transcoding throughput measured on Intel N100 (Quick Sync, 4K HEVC → 1080p H.264):

Concurrent streamsCPU loadTranscode FPS
1~8%60–90 fps
2~14%55–80 fps each
3~20%45–70 fps each
4~28%40–60 fps each

At 4 concurrent 4K → 1080p transcodes, playback remains smooth on a $160 N100 mini PC. Software transcoding (CPU only) would saturate all 4 N100 cores on a single 4K stream.

For the full GPU hardware comparison across Intel QSV, NVIDIA, and AMD, see the Best GPU for Jellyfin Transcoding guide.


For the Arr stack (Radarr, Sonarr, Prowlarr) that populates your Jellyfin library automatically, the Docker Compose starter stack covers the service stack with shared proxy networking. For transcoding GPU picks and benchmarks across platforms, see the GPU transcoding comparison.