iGPU Passthrough to LXC on Proxmox
Section 1: Install Jellyfin and Configure NAS Mounts
1.1 Install Jellyfin
- Install Jellyfin via Proxmox Helper Scripts using the following command, which pre-configures the most important settings via generated mode:
https://community-scripts.org/generator?script=jellyfin
mode=generated var_cpu="4" var_ram="8192" var_disk="32" var_unprivileged="0" var_ssh="yes" var_pw="password123" var_gpu="yes" var_mount_fs="nfs" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jellyfin.sh)"
This configures the following automatically:
- Privileged container (
var_unprivileged="0") - GPU passthrough (
var_gpu="yes") - NFS feature flag (
var_mount_fs="nfs") - SSH access (
var_ssh="yes")
Note: Change
var_pwto a secure password before running. The values forvar_cpu,var_ram, andvar_diskcan be adjusted to suit your hardware.
- Once the script completes, verify the LXC config was generated correctly:
cat /etc/pve/lxc/<id>.conf
Confirm the following are present:
dev0: /dev/dri/renderD128,gid=993dev1: /dev/dri/card1,gid=44(card number may vary)features: nesting=1,mount=nfs
1.2 Fix DNS if Needed
If Tailscale is installed on the Proxmox host, it may inject its DNS server (100.100.100.100) into the LXC's resolv.conf, breaking DNS resolution inside the container. Verify DNS is working inside the LXC:
ping -c 3 google.com
If DNS resolution fails, stop the LXC and add DNS settings to the LXC config on the Proxmox host:
pct stop <id>
nano /etc/pve/lxc/<id>.conf
Add the following lines (replace 192.168.2.7 with your local DNS server):
nameserver: 192.168.2.7
searchdomain: local
Start the LXC and verify:
pct start <id>
Inside the LXC:
ping -c 3 google.com
1.3 Mount NAS Shares on the Proxmox Host
:::danger Important: NFS shares should be mounted on the Proxmox host and bind-mounted into the LXC via the mp parameter in the LXC config. Mounting NFS directly inside an LXC is unreliable and not recommended. :::
- Create the mount point directory on the Proxmox host:
mkdir -p /mnt/syn-media
- Verify the correct NFS export paths on the Synology before editing fstab. NFS paths on Synology are lowercase (e.g.
/volume1/Medianot/Volume1/Media) and are case-sensitive:
showmount -e 192.168.2.22
- Test the mount manually before making it persistent:
mount -t nfs 192.168.2.22:/volume1/Media /mnt/syn-media
Verify the contents are accessible:
ls /mnt/syn-media
- If the manual mount succeeded, make it persistent by adding it to the Proxmox host fstab:
nano /etc/fstab
Add the following entries:
# Synology NAS - NFS Mounts
192.168.2.22:/volume1/Media /mnt/syn-media nfs rw,hard,noresvport 0 0
# Unraid NAS - NFS Mounts (commented out until Unraid is back online)
# 192.168.2.6:/mnt/user/Media /mnt/unraid-media nfs rw,hard,noresvport 0 0
- Reload fstab and mount all entries:
systemctl daemon-reload
mount -a
1.4 Bind Mount the NAS Share into the LXC
- Stop the LXC:
pct stop <id>
- Edit the LXC config file on the Proxmox host:
nano /etc/pve/lxc/<id>.conf
Add the following line to bind mount the NAS share into the container:
mp0: /mnt/syn-media,mp=/mnt/media
If you have additional shares to mount, increment the mp number:
mp1,mp2, etc.
- Start the LXC and verify the mount is visible inside the container:
pct start <id>
pct exec <id> -- ls /mnt/media
Section 2: iGPU Passthrough Configuration
Reference: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel#lxc-on-proxmox
:::danger Steps in this section are performed on the Proxmox host unless otherwise noted. :::
2.1 Prepare the Proxmox Host
- Ensure the i915 driver is not blacklisted on the Proxmox host:
grep -r "blacklist i915" /etc/modprobe.d/
:::info If any results are returned, remove those lines from the relevant file: :::
sed -i '/blacklist i915/d' /etc/modprobe.d/<filename>.conf
- Configure GuC and HuC firmware loading. Create or edit the i915 options file:
nano /etc/modprobe.d/i915.conf
:::info The file should contain exactly one line: :::
options i915 enable_guc=3
:::info
Note: enable_guc=3 enables both GuC submission and HuC, which is required for full hardware transcoding on modern Intel iGPUs. Do not have both enable_guc=2 and enable_guc=3 in the same or different files — use enable_guc=3 only.
:::
- Update initramfs across all installed kernels and reboot the Proxmox host:
update-initramfs -u -k all
reboot
- After reboot, verify the i915 driver loaded and
/dev/driis present:
lsmod | grep i915
ls -la /dev/dri/
:::info
You should see renderD128 listed under /dev/dri/. The card device may appear as card0 or card1 depending on the system — this is normal and does not affect functionality since Jellyfin uses renderD128 directly.
Important: If /dev/dri/ does not exist, the driver is not loading correctly. Common causes are a remaining i915 blacklist entry, a VFIO binding from a previous GPU passthrough configuration, or the xe driver conflicting with i915. Do not proceed until this is resolved.
:::
- If the GPU was previously passed through to a VM, verify VFIO is no longer
claiming it:
lspci -nnk | grep -A3 "00:02.0"
:::info
The output should show Kernel driver in use: i915. If it shows vfio-pci, check the VM config for a hostpci entry pointing to the iGPU and remove it:
:::
grep -r "hostpci" /etc/pve/qemu-server/
nano /etc/pve/qemu-server/<vmid>.conf
:::info
Remove or comment out the hostpci0 line, then reboot the host.
:::
- Verify GuC and HuC firmware loaded successfully:
dmesg | grep -i huc
dmesg | grep -i guc
:::info Both should show as authenticated with no ERROR or FAIL in the output. The message "Setting dangerous option enable_guc - tainting kernel" is expected and harmless. :::
2.2 Install GPU Drivers Inside the LXC
Note: The version of
intel-media-va-driver-non-freein the default Ubuntu repositories is too old to support Alder Lake and newer Intel iGPUs (e.g. N100, N150, 12th Gen Core and later). Always install from Intel's
official repository. The repo codename must match the Ubuntu version inside the LXC:
- Ubuntu 22.04 (Jammy): use
jammy- Ubuntu 24.04 (Noble): use
noble
- Run the following inside the LXC to add Intel's GPU repository and install the correct driver. Verify DNS is working before proceeding (Section 1.2):
apt install -y gpg-agent wget
wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu noble client" > /etc/apt/sources.list.d/intel-graphics.list
apt update
apt install -y intel-media-va-driver-non-free libva2 libva-drm2 vainfo intel-gpu-tools
:::info
If the wget command fails with "no valid OpenPGP data found", DNS is not working. Resolve DNS first (Section 1.2), then remove the broken files before retrying:
:::
rm /etc/apt/sources.list.d/intel-graphics.list
rm /usr/share/keyrings/intel-graphics.gpg
- Verify the driver initialized correctly inside the LXC:
vainfo --display drm --device /dev/dri/renderD128
:::info
The output should show Intel iHD driver for Intel(R) Gen Graphics and list supported profiles including H264, HEVC, VP9, and AV1. If iHD_drv_video.so init failed appears, the driver version does not support your iGPU — revisit Step 17.
:::
- Verify GPU engine activity inside the LXC (requires root):
intel_gpu_top
:::info With no active transcodes the engines will show 0% — this is expected. GPU activity will appear when a transcode is in progress. :::
- Add the Jellyfin service user to the required groups inside the LXC:
usermod -aG video jellyfin
usermod -aG render jellyfin
- Restart Jellyfin for group changes to take effect:
systemctl restart jellyfin
Section 3: Jellyfin Transcoding Configuration
- In the Jellyfin web UI, go to Admin Dashboard > Playback > Transcoding.
- Set Hardware Acceleration to Video Acceleration API (VAAPI).
Note: Although Intel Quick Sync (QSV) appears as an option, VAAPI is the correct choice on Linux. On Linux, VAAPI is the native interface to Intel Quick Sync hardware — selecting QSV on Linux adds an unnecessary abstraction layer and offers no benefit over VAAPI.
- Set VA-API Device to
/dev/dri/renderD128. - Under Enable hardware decoding for, enable the following codecs: H264, HEVC, MPEG2, VC1, VP8, VP9, AV1, HEVC 10bit, VP9 10bit.
-
Under Hardware encoding options:
-
Check Enable hardware encoding
- Uncheck both Enable Intel Low-Power H.264 hardware encoder and
Enable Intel Low-Power HEVC hardware encoder
Low-power encoding requires confirmed HuC firmware authentication (Step 16) and on some iGPU generations causes transcoding failures even when HuC is loaded. Leave these disabled unless you have a specific reason to enable them.
-
Under Encoding format options:
-
Check Allow encoding in HEVC format
- Uncheck Allow encoding in AV1 format unless your iGPU supports
AV1 encoding. To verify, look forVAEntrypointEncSliceunderVAProfileAV1Profile0in thevainfooutput from Step 18. Most Intel iGPUs support AV1 decoding (VAEntrypointVLD) but not AV1 encoding.
Browser playback note: Most browsers (Chrome, Firefox) do not support HEVC decode and will automatically receive an H264 stream instead. Jellyfin handles this per-client automatically based on what the client reports as supported — enabling HEVC encoding globally does not break browser playback. Devices such as Nvidia Shield TV, Apple TV, and modern smart TVs support HEVC natively and will receive the more efficient HEVC stream when it is enabled.
- Save settings and test playback. Monitor GPU utilization to confirm hardware transcoding is active:
intel_gpu_top
:::info
During an active transcode, VCS/0 (video decode) and RCS/0 (render/encode) should show non-zero utilization. If both remain at 0% during playback, transcoding is falling back to software — check the FFmpeg transcode logs in /var/log/jellyfin/ for errors.
:::