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 from getent group render | cut -d: -f3.
  • user: "1000:1000" — Match your host user. Run id to confirm your UID and GID.
  • Replace /mnt/media/* paths with your actual media locations.
  • Replace YOUR_SERVER_IP with 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: nvidia requires the NVIDIA Container Toolkit (installed below).
  • capabilities: [gpu, video] — the video capability 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 render and video groups for VAAPI access.
  • /dev/kfd is 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:

  1. Log in at http://YOUR_SERVER_IP:8096.
  2. Go to Dashboard > Playback > Transcoding.
  3. 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
  4. Check Enable hardware decoding for all codecs your GPU supports (H.264, HEVC at minimum).
  5. Check Enable hardware encoding.
  6. Check Enable tone mapping (converts HDR to SDR for clients that don’t support HDR).
  7. 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

  1. Open http://YOUR_SERVER_IP:8096 in your browser.
  2. Select your language.
  3. Create your admin account with a strong password.
  4. 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
  5. Set your metadata language and country.
  6. Enable remote access if needed.
  7. 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

Comments