Jellyfin’s setup hinges on the host first: /dev/dri/renderD128 for Intel Quick Sync or AMD VAAPI, or the NVIDIA Container Toolkit for NVENC. With that present, it is one linuxserver container on 8096, with PUID/PGID 1000, media bind mounts, and Nginx Proxy Manager with Websockets on. Choose media paths before starting; changing them means re-adding libraries.
Contents
By LK Wood IV · 2026-05-25 · Updated 2026-10-10 · ~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 covers a Jellyfin Docker install from start to finish. You get a Compose file, the right ports, hardware transcoding for Intel, NVIDIA, and AMD GPUs, HTTPS via Nginx Proxy Manager, and Tailscale remote access.
(Still choosing? Jellyfin vs Plex covers the common alternative. Come back here to build it.)
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: Jellyfin Docker Compose file
A Jellyfin docker-compose.yml is short. It needs an image, port 8096, a /config volume and your media folders.
Jellyfin’s own example uses the official jellyfin/jellyfin image and adds a /cache volume. This guide uses LinuxServer’s image. That image takes PUID and PGID, and it sets /dev/dri permissions for hardware transcoding by itself.
Save the file below as docker-compose.yml. If you are not running Nginx Proxy Manager, delete its two networks: blocks first (see below). Then run docker compose up -d in the same folder.
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
networks:
proxy:
external: true
The two networks: blocks attach Jellyfin to an external network named proxy, the one Nginx Proxy Manager also joins in this setup. It has to exist before you start Jellyfin. No Nginx Proxy Manager? Delete both blocks. Otherwise docker compose up stops: Compose returns an error when an external network does not exist.
For Intel Quick Sync (QSV/VAAPI) — uncomment the device section above:
devices:
- /dev/dri:/dev/dri
No group_add is needed. LinuxServer says the image makes sure its user “has the proper permissions to access this device”.
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
- NVIDIA_VISIBLE_DEVICES=all
volumes:
- ./config:/config
- /mnt/media/movies:/data/movies
- /mnt/media/tv:/data/tv
- /tmp/jellyfin-transcode:/transcode
ports:
- 8096:8096
runtime: nvidia
networks:
- proxy
networks:
proxy:
external: true
All the variables sit in one environment: list. A second environment: key in the same service is invalid, because YAML requires each key in a mapping to be unique.
LinuxServer’s instructions set runtime: nvidia and NVIDIA_VISIBLE_DEVICES=all. The image sets the NVIDIA capabilities variable itself. 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.
The official image instead
Jellyfin’s own Compose example uses jellyfin/jellyfin. It runs as the user you set, so create the config and cache folders first and hand them to that user, or Docker creates them owned by root and Jellyfin cannot write to them:
cd /opt/stacks/jellyfin
mkdir -p config cache
chown 1000:1000 config cache
Trimmed to this guide’s paths, the file looks like this:
services:
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
user: 1000:1000
ports:
- 8096:8096/tcp
- 7359:7359/udp
volumes:
- ./config:/config
- ./cache:/cache
- type: bind
source: /mnt/media/movies
target: /data/movies
- type: bind
source: /mnt/media/tv
target: /data/tv
restart: unless-stopped
user: is optional. Jellyfin says to leave it out to use the image’s default user.
An image with no tag pulls latest. Jellyfin’s latest follows every stable release, including major versions. Jellyfin also publishes version-pinned tags. Use one if you would rather choose when to upgrade.
The media targets match this guide’s /data/movies and /data/tv, so the setup wizard steps below still apply.
For Quick Sync, VAAPI or NVENC with this image, follow Jellyfin’s hardware acceleration docs. The LinuxServer permission handling above does not apply to it.
Jellyfin does not support running it in a container on Windows or macOS, where hardware transcoding is among the broken features.
Jellyfin Docker install with docker run
You can skip Compose. Jellyfin’s container install page, read on 2026-10-10, gives this command for the official image. Replace uid:gid and the paths with your own, or drop --user to use the image’s default user:
docker pull jellyfin/jellyfin
mkdir -p /path/to/config /path/to/cache
docker run -d \
--name jellyfin \
--user uid:gid \
-p 8096:8096/tcp \
-p 7359:7359/udp \
--volume /path/to/config:/config \
--volume /path/to/cache:/cache \
--mount type=bind,source=/path/to/media,target=/media \
--restart=unless-stopped \
jellyfin/jellyfin
Three details from that page save time:
- Jellyfin calls bind mounts easier than named volumes for a simple setup.
- Add one
--mount type=bind,source=...,target=...per media folder. Append,readonlyto a mount that Jellyfin should not write to. - The default Docker network mode is bridge. Host networking (
--net=host) is optional, but Jellyfin says DLNA needs it.
The same page says Jellyfin gives no support for containers on platforms other than Linux. Hardware-accelerated transcoding is known to be broken there.
Jellyfin Docker ports: 8096, 8920 and 7359
I use 8096/TCP for the Jellyfin web interface and streaming connection. 8920/TCP is for Jellyfin’s own HTTPS listener, which is disabled by default; a reverse proxy can handle HTTPS separately. 7359/UDP serves local client discovery, not the web interface. Jellyfin documents each port here.
If local discovery is needed, add - 7359:7359/udp under the service’s existing ports: list. Discovery is local-subnet traffic, so publishing the port does not make it work across routed networks. A client can instead connect directly to http://your-host-ip:8096. I keep discovery off the public internet.
Jellyfin’s networking page says firewalls block incoming traffic by default and that opening a port straight to the internet is insecure and not recommended. It strongly recommends handling HTTPS on a reverse proxy. Access through a proxy only works correctly when Jellyfin has the right known proxies set and knows which address ranges count as local. Local ranges go in as comma-separated CIDR blocks.
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
Check that the container sees the GPU first. For Intel, Jellyfin’s Intel hardware acceleration page (read 2026-10-10) says to run vainfo inside the container and read the codec list:
docker exec -it jellyfin /usr/lib/jellyfin-ffmpeg/vainfo
That path is for the official image, which ships the Intel media drivers. If the command fails, the device mapping is the first thing to check. The same page notes that QSV is not available on Windows Docker or WSL, and that on Linux it covers Broadwell (5th gen Core) and newer.
Codec support varies by Intel generation, so enable only what your CPU lists:
| Codec | Intel generation per Jellyfin’s docs |
|---|---|
| H.264 8-bit | Any QSV-capable GPU |
| HEVC 8-bit encode and decode | Gen 9 (Skylake, 6th gen Core) and newer |
| HEVC 10-bit encode and decode | Gen 9.5 (Kaby Lake, 7th gen Core), Apollo Lake, Gemini Lake and newer |
| AV1 decode | Gen 12 (Tiger Lake, 11th gen Core) and newer |
| AV1 encode | Arc A-series and Meteor Lake and newer |
Jellyfin’s hardware acceleration overview also says to confirm that Jellyfin uses its own ffmpeg build. Its version string carries a -Jellyfin suffix, for example /usr/lib/jellyfin-ffmpeg/ffmpeg. Binaries from other sources only get partial acceleration.
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.
Tailscale is the right first step, not the whole answer — it has no client for Roku, webOS, or Tizen TVs, and family who won’t install a VPN need a different path. The Jellyfin remote access decision guide routes every situation: Tailscale, reverse proxy with a domain, VPS relay for CGNAT, and why Cloudflare Tunnel is a trap for media.
Update and back up the Jellyfin container
Back up before you update. Jellyfin’s backup page, read on 2026-10-10, puts the data in the folder mounted at /config, for both the official and the LinuxServer image. Jellyfin also says a backup is required to restore a previous version.
A cold backup of that folder:
- Stop the container with
docker stop jellyfin. Jellyfin calls this extremely important, because otherwise the database is locked. - Copy the host folder behind
/configto a dated directory. - Start the container again.
Jellyfin 10.11 and newer can also back up while it runs, from Dashboard, Backups, Create Backup.
To update, pull the new image and recreate the container from the folder with your compose file:
docker compose pull jellyfin
docker compose up -d
docker compose pull only downloads images. Docker’s up reference says that when a service’s image has changed, docker compose up stops and recreates the container. Your ./config folder stays where it was, so libraries and users carry over. A version-pinned tag stays put until you edit it.
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 transcoding GPU picks and benchmarks across platforms, see the GPU transcoding comparison. If you’re deciding between Jellyfin and Plex, the Jellyfin vs Plex comparison breaks down hardware transcoding cost, privacy, remote access, and app quality by use case. If your library also includes audiobooks, the AudioBookshelf Docker setup covers the purpose-built server for them, with per-user progress sync across mobile apps.
Sources
- Jellyfin networking – ports 8096, 8920 and 7359, firewall guidance, known proxies and local networks. Read 2026-10-10.
- Jellyfin Intel hardware acceleration –
vainfocheck inside the container, QSV generations and codec support. Read 2026-10-10. - Jellyfin backup and restore – stop the server, copy the
/configfolder, built-in Backups. Read 2026-10-10. - Docker Compose up reference and pull reference –
uprecreates a container when its image changed;pullonly downloads. Read 2026-10-10. - Jellyfin Container install – official docs on running Jellyfin via Docker, Docker Compose, and Podman.
- Jellyfin Hardware Acceleration – official docs on Intel QSV, NVIDIA NVENC, and AMD VAAPI transcoding setup.
- LinuxServer.io Jellyfin image – official docs for the lscr.io/linuxserver/jellyfin image, PUID/PGID, and device mounts.
- Docker Compose networks reference – an
externalnetwork is not created by Compose, which returns an error if it does not exist. - Docker image pull reference – with no tag, Docker Engine uses
latest. - YAML 1.2.2 specification – each key in a mapping must be unique.
- Tailscale Subnet routers – official docs on advertising a LAN subnet for remote access without opening ports.
Frequently asked questions
Should I use the official Jellyfin Docker image or LinuxServer’s?
Does Jellyfin need a GPU for transcoding?
Is Jellyfin actually better than Plex in 2026?
Can I run Jellyfin and the Arr stack (Radarr, Sonarr) together?
How much storage does Jellyfin itself use?
How do I access Jellyfin outside my home network?
Sources and corrections
- Last updated
- Methodology
- See our methodology for research and review standards.
- Update log
- 2026-09-28 — Added a Jellyfin Docker install section with the official docker run command, a GPU check with vainfo, update and backup steps, and Jellyfin’s port and DLNA notes, all read from jellyfin.org and docker.com on 2026-10-10. Earlier: added a short answer for the Jellyfin Docker Compose file and Jellyfin’s own official-image example. Fixed the NVIDIA compose, which declared environment twice in one service; YAML requires each key in a mapping to be unique. Dropped group_add from the Intel lines: LinuxServer’s image sets /dev/dri permissions itself. Said what to delete when the external proxy network does not exist.
- Corrections
- Spotted an error or a stale number? Email contact@techfuelhq.com. Confirmed corrections are recorded here with the date and change.