Install Jellyfin on Ubuntu Server
Why Ubuntu for Jellyfin?
Ubuntu Server is the most common host for Jellyfin deployments, and for good reason. Driver support for Intel, NVIDIA, and AMD GPUs is mature. Docker runs natively without the overhead of a hypervisor. If you have a dedicated box or a cloud VPS with GPU access, Ubuntu is the path of least resistance.
This guide covers Ubuntu-specific setup that the main Jellyfin guide doesn’t — GPU driver installation, kernel module configuration, firewall rules, and filesystem tuning for large media libraries.
Prerequisites
- Ubuntu Server 22.04 LTS or 24.04 LTS (fresh install or existing)
- Docker and Docker Compose installed (guide)
- Root or sudo access
- 4 GB RAM minimum (2 GB usable for Jellyfin, OS takes the rest)
- Media files on local storage, NAS mount, or external drive
- A GPU for hardware transcoding (Intel iGPU, NVIDIA discrete, or AMD — optional but strongly recommended)
Confirm your Ubuntu version:
lsb_release -a
Install Docker if you haven’t:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect
Platform Setup
Create the Jellyfin Directory
sudo mkdir -p /opt/jellyfin
sudo chown $USER:$USER /opt/jellyfin
cd /opt/jellyfin
Identify Your GPU
Before writing your Docker Compose file, figure out what GPU you have:
# Check for Intel iGPU
lspci | grep -i vga
# Check for NVIDIA
lspci | grep -i nvidia
# Check for AMD
lspci | grep -i amd
If you see Intel Corporation with UHD Graphics, Iris, or HD Graphics, you have an Intel iGPU. Proceed to Intel Quick Sync setup. If you see an NVIDIA card, go to the NVIDIA section. AMD users, skip to AMD.
No GPU listed? You can still run Jellyfin with software transcoding, but expect high CPU usage during transcodes. Consider using clients that support direct play to avoid transcoding entirely.
Set Up inotify Watches for Large Libraries
Ubuntu’s default inotify watch limit (8192) is too low for Jellyfin when monitoring large media libraries. Jellyfin uses inotify to detect new files and changes — if you hit the limit, library scanning breaks silently.
# Check current limit
cat /proc/sys/fs/inotify/max_user_watches
# Increase permanently
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.d/99-jellyfin.conf
sudo sysctl -p /etc/sysctl.d/99-jellyfin.conf
This matters if your library has 10,000+ files across nested directories. Set it now and forget about it.
Docker Compose Configuration
Intel Quick Sync (VAAPI)
This is the recommended setup for Intel CPUs with integrated graphics (Haswell and newer, including N100 mini PCs).
services:
jellyfin:
image: jellyfin/jellyfin:10.11.6
container_name: jellyfin
user: "1000:1000"
group_add:
- "render"
ports:
- "8096:8096/tcp"
- "7359:7359/udp"
volumes:
- jellyfin-config:/config
- jellyfin-cache:/cache
- /mnt/media/movies:/media/movies:ro
- /mnt/media/tv:/media/tv:ro
- /mnt/media/music:/media/music:ro
environment:
- JELLYFIN_PublishedServerUrl=http://YOUR_SERVER_IP:8096
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
restart: unless-stopped
volumes:
jellyfin-config:
jellyfin-cache:
Notes:
group_add: "render"— Docker resolves the group name to the correct GID automatically. If this fails on older Docker versions, replace with the numeric GID fromgetent group render | cut -d: -f3.user: "1000:1000"— Match your host user. Runidto confirm your UID and GID.- Replace
/mnt/media/*paths with your actual media locations. - Replace
YOUR_SERVER_IPwith your server’s LAN IP (e.g.,192.168.1.50).
NVIDIA NVENC
For servers with a discrete NVIDIA GPU (GTX 1050+, RTX series, or Quadro/Tesla).
services:
jellyfin:
image: jellyfin/jellyfin:10.11.6
container_name: jellyfin
user: "1000:1000"
runtime: nvidia
ports:
- "8096:8096/tcp"
- "7359:7359/udp"
volumes:
- jellyfin-config:/config
- jellyfin-cache:/cache
- /mnt/media/movies:/media/movies:ro
- /mnt/media/tv:/media/tv:ro
- /mnt/media/music:/media/music:ro
environment:
- JELLYFIN_PublishedServerUrl=http://YOUR_SERVER_IP:8096
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=all
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu, video]
restart: unless-stopped
volumes:
jellyfin-config:
jellyfin-cache:
Notes:
runtime: nvidiarequires the NVIDIA Container Toolkit (installed below).capabilities: [gpu, video]— thevideocapability is required for NVENC/NVDEC transcoding. Without it, Jellyfin sees the GPU but cannot use it for encoding.- Consumer NVIDIA GPUs (GTX/RTX) limit concurrent NVENC sessions to 3-5 by default. Quadro and Tesla cards have no limit. You can patch the driver to remove the limit — search for “nvidia-patch” on GitHub.
AMD VAAPI
For AMD APUs (Ryzen with Radeon Graphics) or discrete AMD GPUs.
services:
jellyfin:
image: jellyfin/jellyfin:10.11.6
container_name: jellyfin
user: "1000:1000"
group_add:
- "render"
- "video"
ports:
- "8096:8096/tcp"
- "7359:7359/udp"
volumes:
- jellyfin-config:/config
- jellyfin-cache:/cache
- /mnt/media/movies:/media/movies:ro
- /mnt/media/tv:/media/tv:ro
- /mnt/media/music:/media/music:ro
environment:
- JELLYFIN_PublishedServerUrl=http://YOUR_SERVER_IP:8096
- ROC_ENABLE_PRE_VEGA=1
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
- /dev/kfd:/dev/kfd
restart: unless-stopped
volumes:
jellyfin-config:
jellyfin-cache:
Notes:
- AMD GPUs need both the
renderandvideogroups for VAAPI access. /dev/kfdis the AMD Kernel Fusion Driver — required for ROCm-based tone mapping. If you do not need HDR tone mapping, you can omit it.ROC_ENABLE_PRE_VEGA=1— required for pre-Vega AMD GPUs (Polaris, etc.). Safe to include even on newer hardware; it is ignored if not needed.
Hardware Transcoding Setup
Intel Quick Sync
Install the Intel GPU drivers and VAAPI libraries:
# Ubuntu 22.04/24.04
sudo apt update
sudo apt install -y intel-media-va-driver-non-free vainfo mesa-va-drivers
Add your user to the render group so Docker can access the GPU:
sudo usermod -aG render $USER
# Log out and back in, or run: newgrp render
Verify the GPU is accessible:
vainfo
You should see output listing supported profiles — VAProfileH264Main, VAProfileHEVCMain, etc. If vainfo fails with “libva error”, the driver is not installed correctly. Reinstall intel-media-va-driver-non-free.
For Intel 12th gen+ (Alder Lake, Raptor Lake, N100), the intel-media-va-driver-non-free package provides full hardware encoding support including AV1. For older Intel GPUs (6th-11th gen), H.264 and HEVC encoding are supported.
Verify the render device exists:
ls -la /dev/dri/
# Should show: card0, renderD128
NVIDIA GPU
Install the NVIDIA driver:
# Add the driver PPA
sudo apt update
sudo apt install -y ubuntu-drivers-common
sudo ubuntu-drivers autoinstall
sudo reboot
After reboot, verify the driver:
nvidia-smi
You should see your GPU model, driver version, and CUDA version. If nvidia-smi is not found, the driver install failed — check dmesg | grep -i nvidia for errors.
Install the NVIDIA Container Toolkit:
# Add the NVIDIA container toolkit repo
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
# Configure Docker to use the NVIDIA runtime
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Verify the container can see the GPU:
docker run --rm --runtime=nvidia --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi
If this shows your GPU, Docker-to-NVIDIA passthrough is working.
AMD GPU
Install the AMDGPU driver and VAAPI libraries:
sudo apt update
sudo apt install -y mesa-va-drivers vainfo
For newer AMD GPUs (RDNA, RDNA2+), the default mesa-va-drivers package is sufficient. For hardware-accelerated tone mapping (HDR to SDR), you also need ROCm OpenCL:
# ROCm for HDR tone mapping (optional, RDNA and newer)
sudo apt install -y rocm-opencl-runtime
Add your user to the required groups:
sudo usermod -aG render,video $USER
Verify VAAPI:
vainfo
Check that /dev/dri/renderD128 and /dev/kfd exist:
ls -la /dev/dri/
ls -la /dev/kfd
Enable Transcoding in Jellyfin
After starting the container (docker compose up -d), configure hardware transcoding in the Jellyfin web UI:
- Log in at
http://YOUR_SERVER_IP:8096. - Go to Dashboard > Playback > Transcoding.
- Set Hardware acceleration:
- Intel:
Video Acceleration API (VAAPI)with device/dev/dri/renderD128 - NVIDIA:
NVIDIA NVENC - AMD:
Video Acceleration API (VAAPI)with device/dev/dri/renderD128
- Intel:
- Check Enable hardware decoding for all codecs your GPU supports (H.264, HEVC at minimum).
- Check Enable hardware encoding.
- Check Enable tone mapping (converts HDR to SDR for clients that don’t support HDR).
- Click Save.
Test by playing a file that requires transcoding — check Dashboard > Active Sessions to confirm hardware transcoding is active. You should see HW next to the transcode reason.
Firewall Configuration
Ubuntu’s UFW firewall blocks Jellyfin’s ports by default. Open them:
# Web UI
sudo ufw allow 8096/tcp comment "Jellyfin Web UI"
# Client auto-discovery (LAN only — Jellyfin clients use this to find the server)
sudo ufw allow 7359/udp comment "Jellyfin auto-discovery"
# Verify
sudo ufw status
Port 7359/udp is only needed for LAN auto-discovery. If you access Jellyfin exclusively through a reverse proxy or by IP, you can skip it.
If you are exposing Jellyfin through a reverse proxy, you do not need to open port 8096 to the outside world — only the reverse proxy needs access to it on localhost.
First-Time Setup
- Open
http://YOUR_SERVER_IP:8096in your browser. - Select your language.
- Create your admin account with a strong password.
- Add media libraries:
- Movies — Content type: Movies, Folder:
/media/movies - TV Shows — Content type: Shows, Folder:
/media/tv - Music — Content type: Music, Folder:
/media/music
- Movies — Content type: Movies, Folder:
- Set your metadata language and country.
- Enable remote access if needed.
- Complete the wizard.
Jellyfin starts scanning your libraries immediately. A library with 5,000+ files takes 10-30 minutes for the initial metadata scrape, depending on your internet speed (metadata is fetched from TMDb and MusicBrainz).
Troubleshooting
vainfo Shows No Profiles
Symptom: vainfo runs but lists zero supported profiles, or outputs vaInitialize failed.
Fix: You are likely missing the non-free driver. On Ubuntu, the default intel-media-va-driver only supports older Intel GPUs. Install the non-free version:
sudo apt install -y intel-media-va-driver-non-free
Then re-run vainfo. If it still fails, check that your kernel is loading the i915 module:
lsmod | grep i915
If i915 is not loaded, your Intel iGPU may be disabled in BIOS. Check BIOS settings and enable integrated graphics.
Permission Denied on /dev/dri/renderD128
Symptom: Jellyfin logs show Permission denied when trying to access the GPU, or hardware transcoding falls back to software.
Fix: The container user does not have access to the render device. Verify the render group is passed through:
# On the host
getent group render
# Output: render:x:122:youruser
# Check docker-compose.yml has group_add matching this group
If you changed group_add and it still fails, try passing the numeric GID instead of the group name. Also ensure your host user is in the render group (sudo usermod -aG render $USER, then log out and back in).
NVIDIA Container Toolkit Not Found
Symptom: docker compose up fails with unknown runtime: nvidia or similar error.
Fix: The NVIDIA Container Toolkit is not installed or Docker was not reconfigured after installation. Re-run:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Then verify with:
docker run --rm --runtime=nvidia --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi
Library Not Detecting New Files
Symptom: New media files added to your library folder don’t appear in Jellyfin until you manually trigger a scan.
Fix: You have hit the inotify watch limit. Increase it:
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.d/99-jellyfin.conf
sudo sysctl -p /etc/sysctl.d/99-jellyfin.conf
Restart the Jellyfin container after applying. If the problem persists, verify that your media volumes are not mounted with noatime or other flags that suppress filesystem events.
Docker Compose Cannot Resolve Group Name “render”
Symptom: docker compose up fails with an error about resolving the render group.
Fix: Older Docker Compose versions (pre-2.20) do not support group name resolution in group_add. Replace the group name with the numeric GID:
getent group render | cut -d: -f3
# Example output: 122
Then update docker-compose.yml:
group_add:
- "122" # render group GID
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments