4 KiB
| title | aliases | tags | sources | created | updated | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Docker LXC GPU Device Node Mismatch — renderD128 vs renderD129 |
|
|
|
2026-05-03 | 2026-05-03 |
Docker LXC GPU Device Node Mismatch — renderD128 vs renderD129
When a Docker Compose volume bind-mounts a GPU device node (e.g., /dev/dri/renderD129), if that device file does not exist on the host, Docker creates an empty regular file at that path instead of the expected character device. The container starts but immediately exits with code 255 — silently, with no useful error message — because the "device" it receives is an empty file, not a real GPU.
Key Points
- Docker bind-mounts a non-existent device path → Docker creates an empty file, not a char device
- Container exits with code 255 immediately on start — no diagnostic message in logs
- GPU device node numbers depend on the host hardware:
renderD128is the first render device,renderD129would be a second GPU that may not exist - Always verify available device nodes with
ls -la /dev/dri/before configuring bind mounts - For Jellyfin in Docker inside LXC: the correct device is almost always
/dev/dri/renderD128unless the host has multiple GPUs
Details
How the Bug Manifests
A typical Jellyfin docker-compose.yml includes GPU access for hardware transcoding:
devices:
- /dev/dri/renderD129:/dev/dri/renderD129
If the Proxmox host only has one GPU (most homelab builds), only renderD128 exists. When Docker starts the container, it tries to bind-mount renderD129 — which doesn't exist on the host. Docker creates an empty regular file at /dev/dri/renderD129 and passes it to the container. Jellyfin attempts to open the device, gets an empty file instead of a char device with the right major:minor numbers, and the VA-API or DRI initialization fails. The container exits with code 255.
docker logs jellyfin shows nothing useful — the process exits before writing any output.
Diagnosing the Problem
# On the Proxmox host (or LXC host with GPU passthrough) — check available DRI devices
ls -la /dev/dri/
# Output:
# crw-rw---- 1 root video 226, 0 May 3 14:00 card0
# crw-rw---- 1 root render 226, 128 May 3 14:00 renderD128
# ← renderD129 does NOT appear
# Check what Docker actually passed to the container
docker inspect jellyfin | grep -A 5 "Devices"
# Look for empty file instead of char device
The Fix
# docker-compose.yml — use renderD128, not renderD129
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
Also confirm in LXC config that the GPU is properly passed through to the LXC container (not just to Docker inside it):
# /etc/pve/lxc/<CTID>.conf — required for GPU passthrough to LXC
lxc.cgroup2.devices.allow: c 226:* rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
renderD128 vs renderD129 Explained
GPU device nodes follow a kernel naming convention:
card0,card1— primary display interfacesrenderD128— first render device (DRM render node, offset 128 from base)renderD129— second render device (would require a second GPU or specific multi-head config)
For a typical homelab server with a single iGPU (Intel HD/UHD) or discrete GPU, only renderD128 exists. The 129 variant appears only with multiple GPUs or in specific VGPU setups.
Related Concepts
- wiki/concepts/proxmox-lxc-autostart — LXC container configuration and device passthrough
- wiki/homelab/_index — homelab GPU and LXC infrastructure context
- wiki/concepts/docker-compose-restart-no-code-reload — other Docker silent failure patterns where the container appears fine but behaves wrong
Sources
- daily/2026-05-03.md — CT111 Jellyfin down; exit code 255; root cause was
/dev/dri/renderD129not existing on host, Docker created empty file instead of char device; fixed by changing torenderD128