Jellyfin Docker Guide: Self-Host a Free Plex Alternative on a VPS
Run Jellyfin on a VPS using Docker Compose. Learn how to configure media libraries, hardware acceleration (transcoding), and secure remote SSL access.
This guide outlines the technical implementation steps to deploy a Jellyfin Media Server on a virtual private server (VPS) using Docker Compose. It covers directory layout, environment configuration, hardware acceleration setup (Intel/Nvidia), and reverse proxy integration with SSL.
Directory Structure Setup
Before running the containers, establish a persistent directory structure on the host system to store application configurations, cache data, and media libraries.
Execute the following commands to create the required directory paths:
sudo mkdir -p /opt/jellyfin/{config,cache}
sudo mkdir -p /mnt/media/{movies,tvshows}
Assign ownership of these directories to the non-root user that will execute the Docker container (replace 1000:1000 with your target user and group IDs):
sudo chown -R 1000:1000 /opt/jellyfin
sudo chown -R 1000:1000 /mnt/media
Docker Compose Configuration
Create a file named docker-compose.yml in your deployment directory (e.g., /opt/jellyfin/). The configuration below maps the persistent storage volumes, configures the network interface, and sets up container behavior.
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
user: "1000:1000" # Explicitly match host PUID and PGID
environment:
- JELLYFIN_LOG_DIR=/config/log
volumes:
- /opt/jellyfin/config:/config
- /opt/jellyfin/cache:/cache
- /mnt/media/movies:/data/movies:ro # Mount media as read-only for security
- /mnt/media/tvshows:/data/tvshows:ro
ports:
- "127.0.0.1:8096:8096" # Bind to localhost to restrict external HTTP traffic
restart: unless-stopped
security_opt:
- no-new-privileges:true
Hardware Acceleration (HWA)
Hardware transcoding reduces CPU utilization when streaming media that requires format conversion.
Intel and AMD GPUs (VA-API / QuickSync)
For Intel or AMD hardware acceleration, pass the render device /dev/dri from the host to the container.
- Ensure the user running the container belongs to the
videoorrendergroup on the host. Verify the group ID of/dev/dri/renderD128:bash ls -l /dev/dri/renderD128 - Update the
jellyfinservice block indocker-compose.ymlto include the device node:
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
- /dev/dri/card0:/dev/dri/card0
NVIDIA GPUs (NVENC / NVDEC)
To utilize NVIDIA graphics cards for hardware acceleration, you must install the NVIDIA Container Toolkit on the host VPS and configure the container to use the NVIDIA runtime.
- Install the NVIDIA Container Toolkit according to your host OS documentation.
- Update the
docker-compose.ymlconfiguration:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Reverse Proxy and SSL Configuration
To secure the Jellyfin interface with SSL/TLS, configure a reverse proxy to route traffic from port 443 to the container's port 8096.
Option 1: Caddy (Recommended)
Caddy automatically handles Let's Encrypt SSL certificate provisioning and renewals.
Create a Caddyfile in /etc/caddy/Caddyfile:
jellyfin.example.com {
reverse_proxy 127.0.0.1:8096 {
header_up X-Real-IP {remote_host}
}
}
Option 2: Nginx
If using Nginx, place the following configuration block inside your server configuration /etc/nginx/sites-available/jellyfin:
server {
listen 80;
listen [::]:80;
server_name jellyfin.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name jellyfin.example.com;
ssl_certificate /etc/letsencrypt/live/jellyfin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/jellyfin.example.com/privkey.pem;
# Security headers
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
location / {
proxy_pass http://127.0.0.1:8096;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering for smoother video playback
proxy_buffering off;
}
# WebSockets support for Jellyfin sync/casting features
location /socket {
proxy_pass http://127.0.0.1:8096;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Running the Deployment
To start the Jellyfin container, navigate to the directory containing your docker-compose.yml file and run:
docker compose up -d
Verify that the container is running and inspect the startup logs:
docker compose ps
docker compose logs -f jellyfin
Access the Jellyfin setup wizard by navigating to your configured domain (e.g., https://jellyfin.example.com) in a web browser.