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.comvia 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:
- Language — pick your locale
- Create admin account — use a strong password
- Add media libraries — this is where your
/data/movies,/data/tvmounts are used:- Add library → Content type: Movies → Add folder →
/data/movies - Add library → Content type: Shows → Add folder →
/data/tv
- Add library → Content type: Movies → Add folder →
- Preferred metadata language — English (or your locale)
- 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:
- Install Tailscale on your homelab host (or in a Proxmox LXC)
- Enable subnet routing to advertise your homelab LAN:
tailscale up --advertise-routes=192.168.1.0/24 - On each device (phone, laptop, etc.) that you want Jellyfin access from, install the Tailscale app and connect
- 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
| Platform | App | Notes |
|---|---|---|
| Android | Jellyfin (official) | Play Store |
| iOS / iPadOS | Jellyfin (official) | App Store |
| Apple TV | Jellyfin (official) | tvOS App Store |
| Android TV / Fire TV | Jellyfin (official) | Available on both stores |
| Roku | Jellyfin for Roku | Channel Store |
| Web browser | Built-in web client | At your Jellyfin URL |
| Desktop (Windows/macOS/Linux) | Jellyfin Media Player | GitHub releases |
| Kodi | Jellyfin for Kodi | Kodi 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 streams | CPU load | Transcode 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.