👍 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
📑 Contents ▾
- 01 Prerequisites
- 02 Step 1: Install Docker and Docker Compose
- 03 Step 2: Configure DNS
- 04 Step 3: Prepare the media directory structure
- 05 Step 4: Create the shared Docker network
- 06 Step 5: The Jellyfin docker-compose.yml
- · Variant for an NVIDIA GPU
- 08 Step 6: The Caddyfile for automatic HTTPS
- 09 Step 7: Start the services
- 10 Step 8: Initial configuration and libraries
- 11 Step 9: Enable and verify hardware transcoding
- 12 Step 10: Final verification
- 13 Security, hardening, and backup
- 14 Common pitfalls and troubleshooting
- 15 FAQ
- · Is Jellyfin really 100% free, unlike Plex?
- · Do I absolutely need a GPU?
- · What is the difference between Intel QuickSync and NVIDIA NVENC?
- · Can I access Jellyfin outside my home?
- · Jellyfin, Plex, or Emby: which one to choose?
- · How to manage multiple users with different access levels?
- 22 Related topics
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:
| Type | Name | Value |
|---|---|---|
| A | media.example.com | 203.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:
- Choose the language, create the administrator account with a strong password.
- Add your libraries: “Movies” pointing to
/media/movies, “Series” to/media/series, etc. Jellyfin will then scan and fetch cover art and synopses. - 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:
- Select hardware acceleration: Intel QuickSync (QSV) or VAAPI for an Intel iGPU, NVIDIA NVENC for an NVIDIA card.
- Check the codecs to decode/encode in hardware (H.264, HEVC, and ideally 10-bit HEVC decoding).
- 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
:romount prevents any compromised container from altering your files. - Updates. Run
docker compose pull && docker compose up -dregularly. 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/driis mounted (Intel) and that the user belongs to therendergroup. For NVIDIA, verify the NVIDIA Container Toolkit installation withdocker 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.
Related topics
- Jellyfin vs Plex vs Emby: which media server in 2026
- Automatic HTTPS reverse proxy with Caddy and Docker
- Install and secure an Ubuntu VPS from A to Z
- Automatic backup with Restic and Backblaze
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.