Skip to content

⚡ Intel QuickSync Video (QSV) Hardware Acceleration & Host udev Rules ​

Hardware transcoding in unprivileged containers (e.g. FileFlows, Jellyfin, Plex in docker-media LXC 131) requires passing through /dev/dri/card1 and /dev/dri/renderD128 from the host CPU/iGPU.


🧩 How Unprivileged LXC GPU ID Mapping Works ​

In Proxmox VE unprivileged containers, user IDs and group IDs are shifted into the unprivileged host namespace starting at 100000:

$$\text{Host ID} = 100000 + \text{Container ID}$$

EntityContainer ID (Inside LXC)Host ID (On PVE Host)Purpose / Note
Container rootUID 0 / GID 0UID 100000 / GID 100000Unprivileged root
Container Default User (ubuntu / rayman-ssh)UID 1000 / GID 1000UID 101000 / GID 101000First non-root user in Debian/Ubuntu
Container video GroupGID 44GID 100044Access to display/framebuffer devices (card1)
Container render GroupGID 104 (Debian/Ubuntu standard)GID 100104Access to DRI render node (renderD128)

🛑 The "Reboot Trap" (Why Manual chown is Ephemeral) ​

Running manual chown / chmod commands on the Proxmox host works immediately for runtime:

bash
# Temporary runtime fix on PVE host:
chown 101000:100104 /dev/dri/renderD128
chown 101000:100044 /dev/dri/card1
chmod 666 /dev/dri/renderD128
chmod 666 /dev/dri/card1

Why this works: ​

  1. 101000 maps to the LXC container's standard user (UID 1000).
  2. 100104 maps to the container's render group (GID 104).
  3. 100044 maps to the container's video group (GID 44).
  4. chmod 666 grants read/write permissions to world, allowing any container user or Docker container process to access the device.

The Problem: ​

On host reboot or GPU driver reload, the Linux kernel udev subsystem dynamically recreates /dev/dri/* device nodes with default permissions:

  • Owner: root:render (host UID 0, host GID 104 or 107).
  • Mode: 0660 (crw-rw----).

Inside an unprivileged LXC:

  • Host UID 0 is unmapped and appears as nobody (UID 65534).
  • Host GID 104/107 is unmapped and appears as nogroup (GID 65534).
  • Because permissions are 0660, non-root container users and Docker processes are completely blocked with Permission denied.

🛠️ Permanent Solution: Host udev Rule ​

To make shifted permissions permanent across host reboots, create a udev rule on PVE-1:

File: /etc/udev/rules.d/99-intel-qsv.rules

udev
SUBSYSTEM=="drm", KERNEL=="card1", GROUP="100044", MODE="0660"
SUBSYSTEM=="drm", KERNEL=="renderD128", GROUP="100104", MODE="0666"

TIP

Setting MODE="0666" on renderD128 guarantees that any container user, daemon, or nested Docker container (e.g. Jellyfin running as UID 1000 or nobody) can read/write the render node without failing permission checks.

Apply Without Rebooting ​

bash
udevadm control --reload-rules && udevadm trigger

📋 Container Configuration Requirements ​

Inside /etc/pve/lxc/<VMID>.conf (e.g. /etc/pve/lxc/131.conf for docker-media):

ini
lxc.cgroup2.devices.allow: c 226:1 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir

🔍 Verification & Current Status on docker-media (LXC 131) ​

1. Check DRI Device Permissions in Container ​

bash
# Inside docker-media (LXC 131):
ls -ln /dev/dri

Expected / Active Output:

text
crw-rw---- 1 65534    44 226,   1 card1        # Group 44 = video
crw-rw-rw- 1 65534   104 226, 128 renderD128   # Group 104 = render, Mode 0666

2. Verify Hardware Acceleration in Docker Services ​

bash
# Test VA-API in Jellyfin:
docker exec jellyfin /usr/lib/jellyfin-ffmpeg/vainfo

# Test VA-API in FileFlows:
docker exec fileflows vainfo

Both report:

  • VA-API version: 1.24 / 1.23
  • Driver version: Intel iHD driver for Intel(R) Gen Graphics
  • va_openDriver() returns 0 (Hardware acceleration active and working).

Authoritative operational repository and DR hub.