Skip to content
appsgit

Deploy guide

How to self-host Jellyfin with Docker Compose

Set up the Jellyfin media server with Docker Compose: media folders, permissions, hardware transcoding, HTTPS reverse proxy, backups and safe upgrades.

  • Updated
  • Beginner
  • About 20 minutes

You will need

  • 2 vCPU / 2 GB RAM (more for software transcoding)
  • Docker + Docker Compose v2
  • Your movies, shows or music on local or NAS storage
  • A domain name (optional, for remote access)

What is Jellyfin?

Jellyfin is a free media server that organises your movies, TV shows, music and photos and streams them to browsers, phones, TVs and set-top boxes. It is open source under the GPL-2.0 license, community-run, and has no premium tier, tracking or required online account. It is a common replacement for Plex and Emby.

Requirements

  • A Linux server with 2 CPU cores and 2 GB of RAM. Software transcoding of 4K content needs a much stronger CPU, or a GPU.
  • Docker Engine and Docker Compose v2.
  • Your media library on a local disk or a mounted NAS share.
  • Optional: an Intel CPU with Quick Sync, an AMD GPU or an NVIDIA GPU for hardware transcoding.

Step 1: Prepare the server

This guide uses Ubuntu 24.04 with Docker installed. If you still need Docker, follow the official Docker install guide. Create folders for config and cache:

mkdir -p ~/jellyfin/{config,cache} && cd ~/jellyfin
id -u && id -g

Note the two numbers printed by id. Jellyfin will run as that user, so it needs read access to your media folders.

Step 2: Create the Docker Compose file

Save this as docker-compose.yml, adjusting the media paths and the user line:

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    user: "1000:1000"
    restart: unless-stopped
    ports:
      - "8096:8096"
      - "7359:7359/udp"
    environment:
      JELLYFIN_PublishedServerUrl: "https://media.example.com"
      TZ: "Europe/London"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /srv/media/movies:/media/movies:ro
      - /srv/media/shows:/media/shows:ro
      - /srv/media/music:/media/music:ro
    # Uncomment for Intel Quick Sync or AMD VAAPI hardware transcoding:
    # devices:
    #   - /dev/dri:/dev/dri
    # group_add:
    #   - "993"

Mounting media read-only (:ro) protects your files from accidental changes. Jellyfin does not need write access unless you want it to save artwork and NFO files next to your media. For hardware transcoding with /dev/dri, replace 993 with the group ID of the render group (getent group render | cut -d: -f3). Jellyfin does not use any secrets in the Compose file. You create the admin password in the setup wizard.

latest tracks the current stable release. To pin a version, use a tag from Docker Hub, such as the major version tag.

Step 3: Start and open the app

docker compose up -d

Open http://YOUR_SERVER_IP:8096. The setup wizard asks for a display language, then creates the administrator account. Use a strong password, because this account can also manage the server. Next, add libraries: choose a content type (Movies, Shows, Music) and point it at the matching path inside the container, for example /media/movies. Jellyfin scans the folders and fetches metadata and artwork.

Name files the way Jellyfin expects (Movie Name (2024)/Movie Name (2024).mkv and Show Name/Season 01/Show Name S01E01.mkv) for accurate matching.

Step 4: Put it behind HTTPS

For remote access, use a reverse proxy instead of exposing port 8096. With Caddy:

media.example.com {
    reverse_proxy 127.0.0.1:8096
}

Then, in Dashboard, Networking, add the proxy's IP to "Known proxies" so Jellyfin logs real client addresses and applies its LAN and remote bitrate rules correctly. Nginx Proxy Manager works too. Enable "Websockets Support" on the proxy host. When remote access works, change the mapping to "127.0.0.1:8096:8096" if you do not need direct LAN access on 8096.

Backups and upgrades

Back up the config folder. It holds the database, users, watch history, plugins and settings. The cache folder can be rebuilt and does not need a backup. Stop the container before copying config so the SQLite database is consistent:

docker compose stop && tar czf jellyfin-config-$(date +%F).tgz config && docker compose start

Upgrade with:

docker compose pull && docker compose up -d

Jellyfin migrates its database on major upgrades and cannot be downgraded afterwards, so take a config backup first.

Troubleshooting

  • Libraries are empty after a scan: the container user cannot read the media path, or the library points at a host path instead of the container path (/media/...).
  • Playback buffers or the CPU is pegged: the client is transcoding. Enable hardware acceleration in Dashboard, Playback, or use a client that can direct-play the format.
  • Hardware transcoding fails: check that /dev/dri exists on the host and that the group_add ID matches the render group.
  • Apps cannot auto-discover the server: UDP 7359 must be published, and discovery does not work across subnets or VPNs.

Next steps

Install the Jellyfin apps for your TV and phone, set per-user parental controls, and add plugins such as OpenSubtitles from the built-in catalog. Community repositories add extras like Intro Skipper.

Spotted something out of date? Tell us and we will update the guide.

FAQ

Jellyfin questions

Still curious? Email info@appsgit.com.

What port does Jellyfin use?

Jellyfin serves its web UI and API on port 8096 over HTTP. Port 8920 is the optional built-in HTTPS port, and UDP 7359 is used for client auto-discovery on the local network.

Is Jellyfin free?

Yes. Jellyfin is completely free and open source under the GPL-2.0 license. There is no premium tier, account requirement or paywalled feature such as hardware transcoding.

Jellyfin vs Plex: which is better?

Jellyfin gives you every feature, including hardware transcoding and live TV, for free and without a cloud account. Plex has more polished apps on some smart TVs and easier remote access, but key features require Plex Pass.

Does Jellyfin need a GPU?

No, but a GPU or an Intel CPU with Quick Sync makes transcoding far cheaper. Without one, clients that cannot direct-play a file will push CPU usage high.

Why can Jellyfin not see my media files?

Usually the container user has no read permission on the mounted folder. Make sure the user in the Compose file can read the media directory on the host.