Jellyfin Docker Compose: complete setup guide (2026)
Install Jellyfin with Docker Compose the way the official docs describe it — volumes, network mode, ports, hardware acceleration, updates and backups.
By Swiftly Player12 min read
Disclosure: we build Swiftly Player, one of the clients in this article. We point out where other apps do better.
Checked in September 2026. Jellyfin's versioning changed this year: the project dropped the "10." prefix, so the release after 10.11 is called 12.0 (8 September 2026), followed by 12.1 (15 September 2026), the current stable release at the time of writing. Docker Hub currently tags the jellyfin/jellyfin image as latest, 12, 12.1 and 12.0, plus an unstable channel for nightly builds. Version numbers and image tags change; check the linked pages before you copy a tag into production.
TL;DR
- Use the official
jellyfin/jellyfinimage unless you specifically wantlinuxserver/jellyfin's PUID/PGID environment variables instead of auser:line. - Three volumes matter:
/config(settings and database),/cache(transcoding scratch space), and one or more media mounts, which you can markread_only: truesince Jellyfin never needs to write to your media. - Use host networking if you want DLNA or local auto-discovery to work. Jellyfin's own docs say host mode is "optional but required in order to use DLNA."
- Ports: 8096/tcp (HTTP), 8920/tcp (HTTPS, optional), 7359/udp (client discovery), 1900/udp (DLNA/SSDP, only if you run the DLNA plugin).
- Hardware acceleration needs the GPU passed into the container explicitly —
/dev/drifor Intel/AMD, the Nvidia Container Toolkit for Nvidia. See the full hardware acceleration guide for codec support tables and dashboard settings. - Jellyfin has no downgrade path. Back up before every update.
Which image: jellyfin/jellyfin or linuxserver/jellyfin
Jellyfin's container installation docs list the official jellyfin/jellyfin image first, and name LinuxServer.io and hotio as community alternatives "based on Ubuntu and the official Jellyfin Ubuntu binary packages."
The practical difference that trips people up is user handling. The official image takes a user: uid:gid line directly in your compose file and runs as that user from the start; it does not read PUID/PGID environment variables at all. The LinuxServer.io image instead starts as root, reads PUID and PGID, and then drops to that user — plus it accepts TZ for the container's timezone, which the official image also supports as an environment variable in the usual TZ=Region/City form.
If you copy a compose file from a forum post without checking which image it targets, you can end up with PUID/PGID lines that the official image silently ignores, and permissions stay whatever the image's own default user is. Pick one image and match its documented configuration style; don't mix instructions from both.
This guide uses the official image, since it is what jellyfin.org documents in detail.
The docker-compose.yml
This is the structure from Jellyfin's own container docs, with the parts you need to fill in marked:
services:
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
user: 1000:1000
network_mode: 'host'
volumes:
- /path/to/config:/config
- /path/to/cache:/cache
- type: bind
source: /path/to/media
target: /media
read_only: true
restart: 'unless-stopped'
environment:
- JELLYFIN_PublishedServerUrl=http://192.168.1.50
extra_hosts:
- 'host.docker.internal:host-gateway'A few notes on each part, based on the official container docs:
user: 1000:1000— replace with the UID:GID that should own files Jellyfin writes. "Exclude theuserargument entirely if you want to use the default user," per the docs. If your media files are owned by a specific user on the host, match that UID here so Jellyfin can read them./config— "persistent storage for configuration." This also holds the SQLite (or PostgreSQL, if you configure it) database, so it is the one directory you must never lose./cache— scratch space for transcoding and image caching. It can grow large and does not need to be backed up.- Media mounts — the docs show you can bind multiple separate media directories (
/media,/media2, and so on) instead of forcing everything under one path. Addingread_only: trueon a media mount is good practice: Jellyfin only ever needs to read your files, and a read-only mount means a bug or a bad plugin cannot touch your originals. JELLYFIN_PublishedServerUrl— "alternative address used for autodiscovery." Set this if clients on your network find the wrong address, or if you want a specific hostname reported.- Fonts — if you burn in subtitles during transcoding and want custom fonts available, the docs show an extra read-only bind mount to
/usr/local/share/fonts/custom.
If you plan to run Jellyfin behind a reverse proxy with a domain name instead of host networking, see accessing Jellyfin from outside your home for the proxy-specific settings, including Known proxies.
Network mode: host vs bridge, and why it matters for DLNA
Docker's default is bridge mode, which gives the container its own private network and maps only the ports you list. That is fine for plain web access to Jellyfin — pointing a browser or an app at http://server-ip:8096 works the same either way.
The difference shows up with auto-discovery. Jellyfin's networking docs describe host networking as "optional but required in order to use DLNA," and the DLNA docs are more specific: DLNA relies on SSDP broadcasts on UDP port 1900, and "the network should use Host Mode, otherwise the broadcast signal will not reach the bridged network inside of Docker." SSDP is a broadcast protocol, and Docker's bridge network does not forward broadcast traffic from the host's LAN into the container — so even if you publish 1900/udp, DLNA clients on your network cannot find the server.
Two things to know before you decide:
- DLNA is not built into a base Jellyfin install. Per the same docs, "DLNA support has been moved to a first party plugin and is not included in a Jellyfin base install since 10.9." If you don't use DLNA (most Jellyfin apps, including the official clients, connect directly and don't need it), you don't need host networking for this reason.
- Port 1900 cannot be remapped. The docs note "UPnP is a standard Protocol expected to be on UDP port 1900, its not possible to configure this," so in bridge mode you cannot work around the conflict by publishing it on a different host port.
If you don't need DLNA, stick with bridge mode and the explicit port list below — it keeps the container's network isolated from the rest of your LAN. If you do need it (some smart TVs and older DLNA-only renderers), use network_mode: host and skip the ports: section entirely, since host mode exposes every port the process binds directly.
Ports
| Port | Protocol | Purpose |
|---|---|---|
| 8096 | TCP | Default HTTP web and API access |
| 8920 | TCP | Default HTTPS, if you configure a certificate directly in Jellyfin |
| 7359 | UDP | Client discovery on the local network |
| 1900 | UDP | DLNA/SSDP discovery, only relevant if the DLNA plugin is installed |
Source: Jellyfin networking docs. In bridge mode, publish the ones you need:
ports:
- 8096:8096/tcp
- 7359:7359/udpAdd 8920:8920/tcp only if you terminate HTTPS inside Jellyfin itself. Most setups instead put a reverse proxy in front and leave 8920 closed — see the remote access guide for that approach and why Jellyfin's own docs recommend it.
Hardware acceleration device mappings
Transcoding is the expensive part of running Jellyfin, and hardware acceleration only matters once a client can't Direct Play a file. If your library is compatible with your clients' codecs, you may not need any of this — see Do you need hardware acceleration at all? for that question first.
If you do transcode regularly, the container needs the GPU device passed in explicitly; Docker does not do this automatically.
Intel Quick Sync / VA-API, and AMD VA-API on Linux:
devices:
- /dev/dri:/dev/driJellyfin's Intel and AMD hardware acceleration guides both note that the container's jellyfin user also needs to be a member of the host's render group — find the group ID with getent group render on the host and add it with group_add in compose, since it differs between machines.
Nvidia NVENC/NVDEC, per Jellyfin's Nvidia guide, needs the Nvidia Container Toolkit installed on the host, plus:
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=allThe full walkthrough — including the codec support tables that tell you whether your specific GPU generation actually supports HEVC 10-bit or AV1, the dashboard steps to turn it on, and the most common failure modes — is in Jellyfin hardware acceleration: set it up on a NAS or mini PC.
First run
- Start the container:
docker compose up -d. - Open
http://<server-ip>:8096in a browser (or the host's own address if you used host networking). - Jellyfin's setup wizard walks through, in order: choosing a language, creating the administrator account, adding media libraries (you can click Next without adding anything and add libraries later), setting a preferred metadata language and region, and networking settings, including whether to allow remote connections.
- On the networking step, the docs recommend leaving automatic port mapping (UPnP) disabled unless you specifically need it — it opens ports on your router automatically, which is convenient but harder to audit.
- Finish the wizard. You land on the dashboard with an empty or populated library depending on what you added.
If you skipped adding libraries, add them from Dashboard → Libraries → Add Media Library, pointing at the paths inside the container that you mapped from your media volumes — not the host paths.
Updating
Jellyfin containers update like any other Docker image: pull the new tag and recreate the container.
docker compose pull
docker compose up -dPaste it into a terminal and press Enter.
For LinuxServer's image, the equivalent is the same two commands against lscr.io/linuxserver/jellyfin. Jellyfin's upgrade documentation says plainly: "Always make a backup of your instance data and configuration directories before upgrading ... for any reason." That applies to every upgrade, not only moves to the unstable channel — back up first, every time.
If you track latest, you get whatever the current stable release is whenever you pull; if you want to stay on a specific line, pin the tag (for example jellyfin/jellyfin:12.1) and move it forward deliberately.
Backups
Jellyfin 10.11 added a built-in backup and restore feature, reachable from the dashboard's Backups tab. It can back up the database (always included), plus optionally metadata, metadata images, extracted subtitles and Trickplay data, while the server keeps running — though the docs recommend doing it "during a time of low activity and while no scan is currently active." Restoring can be done from the same tab, or from the command line with --restore-archive PATH_TO_BACKUP_ZIP.
For a Docker install specifically, the docs are direct about where your data actually lives: "The data directory is wherever your /config volume is sourced from; this is set in your docker-compose.yml or in your -v options to docker run." A manual backup of that one host directory, stopped or hot, captures everything the built-in tool would.
Either way, remember the one-way door: "Jellyfin does not have a downgrade mechanism. ... once your Jellyfin instance has been started with a new version, any pending migrations are immediately applied, and your Jellyfin data will no longer work with the old version." A recent /config backup is the only way back if an update goes wrong.
Common mistakes
- Copying PUID/PGID into the official image's compose file. They do nothing there; use
user: uid:gidinstead, or switch to the LinuxServer image if you want PUID/PGID. - Pointing volumes at relative paths. Compose resolves relative host paths against the directory the compose file lives in, which behaves unexpectedly once you move the file or run compose from a different working directory. Use absolute host paths for
/config,/cacheand media. - Mounting media as read-write when Jellyfin never writes to it. Add
read_only: trueon media mounts; there is no downside for normal viewing and library scanning. - Expecting DLNA to work in bridge mode. As covered above, SSDP broadcasts don't cross into a bridged container network; you need host mode for DLNA specifically.
- Enabling QSV in a Windows Docker container. Jellyfin's Intel guide states plainly that QSV is not available on Windows Docker or WSL/WSL2 — use the Linux VA-API path or run Jellyfin natively on Windows instead.
- Forgetting the
jellyfinuser's group membership for hardware acceleration. Passing/dev/driinto the container is not enough by itself; the process inside also needs to be in the host'srendergroup, and that group's numeric ID differs between hosts. - Skipping backups before an update because "it's just Docker." The image tag changes, but the migration that runs against your
/configvolume is the same one-way process either way.
FAQ
Do I need host networking to install Jellyfin in Docker?
No. Host networking is only needed for DLNA and for local network auto-discovery to reach across Docker's bridge. If you connect clients by entering the server's address directly — which is how most Jellyfin apps work — bridge mode with published ports 8096 and 7359 is enough.
Why does my docker-compose.yml with PUID and PGID not change file ownership?
You are probably using the official jellyfin/jellyfin image, which does not read those variables at all. It uses the user: field in the compose file instead. PUID/PGID are a LinuxServer.io convention, documented for linuxserver/jellyfin.
Can I run Jellyfin in Docker without giving it write access to my media?
Yes, and it is recommended. Mount media directories with read_only: true; Jellyfin only reads media files during scanning and playback.
Do I need port 8920 open?
Only if you configure Jellyfin's own HTTPS with a certificate. Most people instead put a reverse proxy in front for HTTPS and never open 8920 — see the remote access guide for that setup.
How do I know which version of Jellyfin I'm running?
Check Dashboard → General, or the /System/Info/Public endpoint on your server, which reports the running version without needing to sign in.
What changed with Jellyfin 12?
The project dropped the "10." prefix from its version numbers (10.11 was followed by 12.0, not 11.0) purely to reduce confusion about what counted as a major release. See what's new in Jellyfin 12 for the feature changes in that release.
Summary
Use the official jellyfin/jellyfin image with a user: line for permissions, three volumes for config, cache and read-only media, and bridge networking with 8096 and 7359 published unless you specifically need DLNA, which requires host mode. Pass /dev/dri or the Nvidia Container Toolkit into the container only if you actually transcode. Update with docker compose pull && docker compose up -d, and back up the /config volume — or use the dashboard's built-in Backups tab — before every update, since Jellyfin cannot downgrade once a new version has run its migrations.


