🛠️ Tutorials · ⏱ 9 min read

Deploy Jellyfin with Docker in 2026: Complete Guide (Hardware Transcoding, HTTPS, Reverse Proxy)

Step-by-step 2026 tutorial to self-host Jellyfin, the open-source media server, using Docker Compose. Covers film/series libraries, Intel/NVIDIA hardware transcoding, automatic HTTPS via Caddy and Let's Encrypt, remote access, and hardening. Ready-to-use configs included.

S By Selfhostr Team · independent tests
Deploy Jellyfin with Docker in 2026: Complete Guide (Hardware Transcoding, HTTPS, Reverse Proxy)
ⓘ This article may contain affiliate links (no extra cost to you, it supports our tests). See the disclosure.
🆓
100% Open Source
License
🎬
GPU Hardware
Transcoding
🔒
HTTPS via Caddy
Security
🐳
Docker Container
Platform

👍 What we like

  • Completely free with no paid features or subscriptions
  • Automatic HTTPS and Let's Encrypt management via Caddy
  • Supports Intel QuickSync and NVIDIA hardware transcoding
  • Read-only media mount enhances security and stability

👎 What to watch

  • Requires a local GPU for efficient hardware transcoding
  • VPS users limited to CPU transcoding or direct play
  • Needs manual DNS configuration and port forwarding
  • Strict media folder naming conventions required for metadata
▶ Video tutorial: deploy Jellyfin
📑 Contents

Want your own home Netflix, without subscriptions, ads, or limits on what you stream? Jellyfin is the ultimate open-source media server: fully open source, no mandatory cloud account, and no paid features locked behind a paywall. It indexes your movies, TV shows, music, and photos, automatically fetches cover art and metadata, and streams everything to all your devices — TV, phone, browser, or console.

The technical feature that makes all the difference on a media server is hardware transcoding: the ability to convert a video stream on the fly using the GPU instead of the CPU. Without it, a single 4K stream can saturate a VPS. In this tutorial, we deploy Jellyfin via Docker, with Intel QuickSync or NVIDIA hardware transcoding, behind a Caddy reverse proxy that handles HTTPS and Let’s Encrypt automatically.

Prerequisites

  • A server or mini-PC running Ubuntu 24.04 (or Debian 12). For hardware transcoding, an Intel iGPU (QuickSync) or an NVIDIA GPU is highly recommended: this is why many host Jellyfin on a home machine rather than a VPS, which generally does not expose a GPU. A VPS from Hetzner or OVHcloud is suitable for light software transcoding or direct play.
  • Plenty of disk space for your media library.
  • Docker and Docker Compose installed (command in step 1).
  • A domain name for which you control the DNS. We will use media.example.com.
  • Ports 80 and 443 open for Let’s Encrypt and HTTPS traffic.

Step 1: Install Docker and Docker Compose

If Docker is not present, install it from the official repository:

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER

Log out and log back in, then verify:

docker --version && docker compose version

Step 2: Configure DNS

At your DNS provider, create an A record (and AAAA if IPv6) pointing to your server’s public IP:

TypeNameValue
Amedia.example.com203.0.113.10

Verify propagation:

dig +short media.example.com

Step 3: Prepare the media directory structure

Jellyfin expects well-organized folders. Create your structure:

mkdir -p ~/jellyfin/{config,cache}
mkdir -p /srv/media/{movies,series,music}

Organize your files according to Jellyfin naming conventions so that metadata is found automatically, for example: Movies/Inception (2010)/Inception (2010).mkv and Series/The Office/Season 01/The Office S01E01.mkv.

Step 4: Create the shared Docker network

So that Caddy can join the Jellyfin container by its name:

docker network create web

If you are already following our Caddy reverse proxy tutorial, this network might already exist: ignore any potential error.

Step 5: The Jellyfin docker-compose.yml

Create the composition file:

cd ~/jellyfin
nano docker-compose.yml
services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    environment:
      TZ: "Europe/Paris"
      # Public URL (useful for links and discovery)
      JELLYFIN_PublishedServerUrl: "https://media.example.com"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /srv/media:/media:ro
    # Intel QuickSync / VAAPI hardware transcoding: expose the GPU device
    devices:
      - /dev/dri:/dev/dri
    networks:
      - web

networks:
  web:
    external: true

Note the absence of a ports: section: Jellyfin is not exposed directly; everything goes through Caddy via the internal web network. The media folder is mounted as read-only (:ro): Jellyfin has no reason to modify your files.

Variant for an NVIDIA GPU

If you have an NVIDIA card and the NVIDIA Container Toolkit installed, replace the devices: block with a GPU resource request:

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

For Intel, the container user must belong to the render group on the host system to access /dev/dri. Check the group ID with getent group render and, if necessary, add group_add: ["RENDER_GROUP_NUMBER"] to the service.

Step 6: The Caddyfile for automatic HTTPS

If you already have a Caddy setup, add the site block. Otherwise, create a Caddy directory:

mkdir -p ~/caddy && cd ~/caddy
nano Caddyfile
{
    email admin@example.com
}

media.example.com {
    encode gzip zstd

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server
    }

    # Jellyfin uses WebSockets for remote control and sync
    reverse_proxy jellyfin:8096
}

Jellyfin’s internal port is 8096. Caddy handles WebSocket upgrades natively, so no additional configuration is needed.

Then the docker-compose.yml for Caddy:

nano docker-compose.yml
services:
  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - web

volumes:
  caddy_data:
  caddy_config:

networks:
  web:
    external: true

Step 7: Start the services

cd ~/jellyfin && docker compose up -d
cd ~/caddy && docker compose up -d

Follow Caddy’s logs to see the certificate issuance:

docker compose logs -f caddy

When you see certificate obtained successfully for media.example.com, visit https://media.example.com. The Jellyfin setup wizard will appear.

Step 8: Initial configuration and libraries

Follow the wizard:

  1. Choose the language, create the administrator account with a strong password.
  2. Add your libraries: “Movies” pointing to /media/movies, “Series” to /media/series, etc. Jellyfin will then scan and fetch cover art and synopses.
  3. Leave the remote access settings as default: Caddy handles public exposure, not Jellyfin itself.

Next, install the Jellyfin apps on your devices (Android, iOS, Android TV, web browser, and third-party clients like Findroid or Streamyfin) and point them to https://media.example.com.

Step 9: Enable and verify hardware transcoding

Hardware transcoding offloads the CPU and allows multiple simultaneous streams. In Dashboard → Playback → Transcoding:

  1. Select hardware acceleration: Intel QuickSync (QSV) or VAAPI for an Intel iGPU, NVIDIA NVENC for an NVIDIA card.
  2. Check the codecs to decode/encode in hardware (H.264, HEVC, and ideally 10-bit HEVC decoding).
  3. Save.

To verify it works, start a playback that forces transcoding (for example, by limiting quality in the client), then on the host:

# For Intel, monitor GPU usage during playback
sudo intel_gpu_top

You should see the “Video” engine activity rise. In the Jellyfin dashboard, the “Activity” tab also indicates whether the session is hardware or software transcoding.

Step 10: Final verification

# The container is running
docker ps --filter "name=jellyfin"

# HTTP redirects to HTTPS (code 308)
curl -sI http://media.example.com | head -1

# HTTPS responds
curl -sI https://media.example.com | head -1

# The GPU device is present in the container (Intel)
docker exec jellyfin ls -l /dev/dri

Security, hardening, and backup

  • Strict UFW firewall. Only open 80, 443, and SSH. Jellyfin’s port 8096 should never be exposed directly.
  • Separate user accounts. Create an account for each person rather than sharing admin; limit accessible libraries per user.
  • Read-only media. The :ro mount prevents any compromised container from altering your files.
  • Updates. Run docker compose pull && docker compose up -d regularly. Jellyfin is stable, but security patches matter.
  • Backup the config. Your libraries, accounts, metadata, and watch progress live in ~/jellyfin/config. Back up this folder (media files themselves remain your source). Our guide automatic backup with Restic and Backblaze shows how to automate an encrypted off-site copy.

To harden the exposed server, see install and secure an Ubuntu VPS.

Common pitfalls and troubleshooting

  • Transcoding remains software (CPU at 100%). The GPU is not accessible to the container. Verify that /dev/dri is mounted (Intel) and that the user belongs to the render group. For NVIDIA, verify the NVIDIA Container Toolkit installation with docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu24.04 nvidia-smi.
  • “Playback failed” on certain formats. The client does not support the codec and the server is not transcoding. Enable hardware transcoding (step 9) or use a more capable client.
  • Missing cover art. Incorrect file naming. Respect the Title (Year) convention and rescan the library.
  • Buffering on remote access. Your upload bandwidth limits the stream. Reduce the maximum bitrate allowed in the client, or transcode to a lower quality.

FAQ

Is Jellyfin really 100% free, unlike Plex?

Yes. Jellyfin is fully open source and has no paid features: no “Pass” to buy to unlock hardware transcoding, mobile apps, or sync. Everything in the server is freely usable. This is its main philosophical difference from Plex and Emby.

Do I absolutely need a GPU?

No, but it is highly recommended. Without a GPU, transcoding happens on the CPU: a single 4K HEVC stream can saturate a modest server. If all your clients play files in “direct play” (without conversion), the CPU is sufficient. But as soon as a device does not support a codec or you limit quality remotely, hardware transcoding becomes critical.

What is the difference between Intel QuickSync and NVIDIA NVENC?

Both provide excellent hardware transcoding. QuickSync (Intel iGPU) is unbeatable in performance-per-watt ratio and ideal on a silent mini-PC. NVENC (NVIDIA) handles more simultaneous streams and suits more powerful servers. For a home lab, a recent Intel iGPU is often the best compromise.

Can I access Jellyfin outside my home?

Yes, that is the whole point of the HTTPS reverse proxy in this tutorial: your media library is accessible from anywhere via https://media.example.com. For more privacy, you can also keep it private and access it via a VPN like WireGuard, exposing nothing publicly.

Jellyfin, Plex, or Emby: which one to choose?

Jellyfin for 100% free and open source, Plex for the most polished ecosystem and content discovery, Emby for a commercial middle ground. If sovereignty and zero cost are priorities, Jellyfin wins. Our comparison Jellyfin vs Plex vs Emby details each use case.

How to manage multiple users with different access levels?

In Dashboard → Users, create an account for each person. For each, you define accessible libraries, download permissions, and parental controls (age classification restrictions). This is cleaner and safer than sharing a single account.

You now have your own HTTPS media server, with hardware transcoding and access from any device — without subscriptions or ads. It is one of the key components of a home lab. To follow Jellyfin updates, transcoding tips, and best practices for self-hosting, subscribe to our Telegram watch bot.

Tags: JellyfinDockerDocker ComposeHardware TranscodingHTTPSCaddyLet's EncryptReverse ProxySelf-hostingMedia Server

Related

🛠️ Tutorials

Deploy Immich with Docker in 2026: Complete Guide (HTTPS, Reverse Proxy, Backups)

Step-by-step 2026 tutorial to self-host Immich, the Google Photos alternative, using Docker Compose. Features include photo/video gallery, facial recognition, automatic HTTPS via Caddy and Let's Encrypt, backups, and hardening. Ready-to-use configs included.

Read
🛠️ Tutorials

Automated HTTPS Reverse Proxy with Caddy and Docker in 2026 (Effortless Let's Encrypt Certs)

2026 tutorial on deploying a Caddy reverse proxy with Docker: automatic HTTPS and Let's Encrypt certificates, multi-service routing, security headers, and seamless renewal. Ready-to-use Caddyfile and docker-compose configs.

Read
🛠️ Tutorials

Deploy n8n with Docker and HTTPS in 2026: Complete Tutorial (Postgres, Reverse Proxy, Persistence)

Step-by-step 2026 guide to self-host n8n, the no-code automation platform, using Docker Compose. Features PostgreSQL, workflow persistence, automatic HTTPS via Caddy and Let's Encrypt, credential encryption, backups, and hardening. Ready-to-use configs.

Read