Appearance
⚡ 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}$$
| Entity | Container ID (Inside LXC) | Host ID (On PVE Host) | Purpose / Note |
|---|---|---|---|
Container root | UID 0 / GID 0 | UID 100000 / GID 100000 | Unprivileged root |
Container Default User (ubuntu / rayman-ssh) | UID 1000 / GID 1000 | UID 101000 / GID 101000 | First non-root user in Debian/Ubuntu |
Container video Group | GID 44 | GID 100044 | Access to display/framebuffer devices (card1) |
Container render Group | GID 104 (Debian/Ubuntu standard) | GID 100104 | Access 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/card1Why this works:
101000maps to the LXC container's standard user (UID 1000).100104maps to the container'srendergroup (GID 104).100044maps to the container'svideogroup (GID 44).chmod 666grants 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(hostUID 0, hostGID 104or107). - Mode:
0660(crw-rw----).
Inside an unprivileged LXC:
- Host
UID 0is unmapped and appears asnobody(UID 65534). - Host
GID 104/107is unmapped and appears asnogroup(GID 65534). - Because permissions are
0660, non-root container users and Docker processes are completely blocked withPermission 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/driExpected / 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 06662. 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 vainfoBoth report:
VA-API version: 1.24/1.23Driver version: Intel iHD driver for Intel(R) Gen Graphicsva_openDriver() returns 0(Hardware acceleration active and working).