MiniLabHQ
Flat isometric illustration of a black mini PC cube with green light strips and a mesh top, stacked on a green vented unit, on a pale grey background.
guides

Proxmox iGPU Passthrough for Plex in an LXC

Give a Plex or Jellyfin LXC access to an N100 iGPU under Proxmox: the render device, the group ID trap, unprivileged containers, and how to verify it.

By MiniLabHQ Editorial · · Updated August 22, 2026 · 7 min read

You bought an N100 box specifically because Quick Sync makes it a competent transcoder. You installed Proxmox, put Plex or Jellyfin in a container, played a file that needs converting, and the dashboard says the transcode is running on the CPU. All four cores are pinned, the stream buffers, and the graphics engine you paid for is doing nothing.

The fix is not PCI passthrough, and that misconception is what sends most people down a two-hour detour. This walks the actual path: what the container needs, the group-ID problem that breaks it on unprivileged containers, and how to prove hardware acceleration is really running rather than assuming it from a green label.

Why this is not a passthrough problem

A VM gets hardware by having a physical device removed from the host and assigned to it. That is what the Proxmox PCI passthrough documentation describes, and it is heavyweight: IOMMU groups, VFIO binding, driver blacklists.

An LXC container is not a VM. It shares the host’s kernel. The host’s i915 driver has already initialised the integrated GPU and exposed it as a character device at /dev/dri/renderD128. Getting the GPU into the container therefore means making that one device node visible inside it, with permissions the container’s process can use. Nothing is detached from the host, the host keeps its console, and several containers can use the same GPU simultaneously.

This is why containers are the right default on a single-node mini PC generally, an argument made at more length in the Proxmox on a mini PC setup guide. Hardware acceleration is the sharpest example of it.

Step 1: confirm the host actually has a render node

On the Proxmox host shell:

ls -l /dev/dri

You want to see something like:

crw-rw---- 1 root video  226,   0 Aug 18 09:12 card0
crw-rw---- 1 root render 226, 128 Aug 18 09:12 renderD128

Three things to read off this output, all of which you will need later:

  • renderD128 exists. This is the render node, the device that does the encoding and decoding. card0 is the display node; media work does not need it.
  • 226 is the major number for DRM devices, with minor 0 for card0 and 128 for renderD128.
  • The owning groups. Above they are video and render. Get their numeric IDs with getent group video render. On Debian-based systems these are commonly 44 and 104 respectively, but check rather than assume — this number is the single most common cause of a setup that looks correct and does not work.

If /dev/dri does not exist at all, the container configuration is not your problem. Either the integrated GPU is disabled in firmware, or the board’s BIOS has a “primary display” or “headless” option that switches the iGPU off when no monitor is attached. Fix that first; nothing downstream works without a render node.

Step 2: pass the device into the container

Proxmox VE 8 and later can do this from the web interface, which is the path to prefer because it writes correct configuration and handles the ID mapping for you. Select the container, go to Resources, then Add → Device Passthrough, and set the device path to /dev/dri/renderD128. The dialog also takes UID and GID fields, which set the ownership of the node as it appears inside the container. The Jellyfin Intel acceleration documentation points at this same GUI flow and stresses the GID setting specifically.

For older versions, or if you prefer to see what is happening, edit /etc/pve/lxc/<CTID>.conf on the host directly and add:

lxc.cgroup2.devices.allow: c 226:0 rw
lxc.cgroup2.devices.allow: c 226:128 rw
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file

The first two lines permit the container’s cgroup to open DRM character devices. The third bind-mounts the render node itself. Note the mount target has no leading slash: it is relative to the container root, which is a requirement of the lxc.mount.entry syntax, and a leading slash is a common typo that produces a container that will not start.

Restart the container after editing, not just the application inside it.

Step 3: the group ID trap on unprivileged containers

This is where most setups fail, and the failure is silent — the device appears inside the container, and the application still refuses to use it.

An unprivileged container, which is Proxmox’s default and the safer choice, uses user namespaces: root inside the container is an unprivileged user outside it. The standard mapping offsets everything by 100000. So a device owned by group 104 on the host shows up inside the container as owned by group 100104, which does not correspond to the render group the container knows about, and the application’s process is not a member of it.

Run ls -l /dev/dri inside the container. If the group column shows nogroup or a bare number like 100104, this is your problem.

Two ways out.

Option A, map the group through (recommended). Add an idmap to the container config so one host GID passes through unshifted. Proxmox documents the idmap syntax as type:container:host:range-size, where type is u for UIDs and g for GIDs. A mapping that lets container GID 104 resolve to host GID 104, with everything else shifted normally, takes three lxc.idmap lines: the GIDs below 104, the single passthrough entry, and the GIDs above it. The host also needs the passed-through ID allowed in /etc/subgid. Then add your media user inside the container to the render group in the ordinary way.

Option B, use a privileged container. Ownership matches, everything works immediately, and you have given the container a materially larger blast radius. It is a legitimate choice for a box that hosts nothing sensitive and a bad habit to form otherwise. If the container is reachable from the internet through a reverse proxy, do not take this option.

There is a third approach people reach for — setting the device to mode 666 on the host — which works and is worth knowing you are choosing, because it grants every local process access to the GPU.

Step 4: install the userspace drivers inside the container

The kernel driver lives on the host. The userspace media driver lives in the container. On a Debian or Ubuntu container:

apt install intel-media-va-driver-non-free vainfo

intel-media-va-driver-non-free is the iHD driver, the one that covers modern Intel graphics; the free variant omits several codecs. Jellyfin’s own jellyfin-ffmpeg7 package bundles the drivers it needs, so a Jellyfin container may not need the separate install. Plex ships its own transcoder binary and needs the render node plus group membership rather than a system VA-API stack.

Step 5: verify, do not assume

Inside the container:

vainfo --display drm --device /dev/dri/renderD128

A working setup prints a driver line naming the iHD driver and then a list of supported profiles and entrypoints. What you are looking for is decode and encode entries for the codecs you care about: VAProfileH264*, VAProfileHEVCMain and VAProfileHEVCMain10, VAProfileVP9Profile0, and VAProfileAV1Profile0 for decode.

Two errors and what they mean:

  • “failed to open /dev/dri/renderD128” or permission denied — Step 3. The device is there and the process cannot use it.
  • “no such file or directory” — Step 2. The bind mount is not in place, or the container was not restarted.

Then confirm at the application layer. Plex requires an active Plex Pass subscription for hardware transcoding at all; Plex’s own documentation is explicit that the feature is subscription-gated, and no amount of container configuration substitutes. With it enabled, an active session shows “(hw)” next to the transcode. In Jellyfin, enable QSV in the playback settings, then read the transcode log for the hardware acceleration lines rather than trusting the toggle.

The most honest check is intel_gpu_top on the host while a transcode runs. If the video engine rows are busy, it is real.

What it will and will not do once it works

Getting the plumbing right does not change the ceiling of the hardware. The N100 Plex transcoding guide covers the limits in detail; the short version:

  • Audio transcoding always runs on the CPU. It is cheap, but it is never accelerated.
  • HDR tone mapping is expensive and configuration-sensitive, and it is the most common reason a stream falls back to software after everything else is correct.
  • Burned-in image subtitles force a software path in many cases.
  • AV1 decode is supported on this generation of Intel graphics; AV1 encode is not, and arrives only on later hardware. Jellyfin’s compatibility table is the reference for which generation gets which codec.

The payoff is power as much as capability. A software transcode pins four efficiency cores and pushes an N100 system from single-digit idle watts toward its 20-plus watt ceiling; the same job on the media engine costs a fraction of that. If you want the actual number for your box rather than the shape of the claim, measure it at the wall with a transcode running and again at idle.

When a VM is the right answer instead

Rarely, on this hardware. Full PCIe passthrough of the integrated GPU to a VM is possible in principle and awkward in practice: the iGPU is often the host’s console output, so passing it through takes the Proxmox console with it. Alder Lake-N and its Twin Lake refresh have no SR-IOV to split the GPU between guests, and the older mediated-passthrough approach does not cover this generation. One VM gets the GPU, exclusively, and the host loses it.

Choose the VM route only when the media application genuinely cannot run in a container — a Windows-only tool, or a distribution you need isolated at the kernel level. Everything else belongs in an LXC, which is also the cheaper option in RAM, and RAM is the resource that actually runs out first on a 16 GB machine. That constraint is the same one driving hardware choice in the best mini PC for a Proxmox homelab guide, and it does not change between the N100 and the N150.

Sources

  1. Proxmox VE Wiki — Linux Container (unprivileged containers, idmap, bind mounts)
  2. Jellyfin documentation — Intel hardware acceleration (QSV and VA-API)
  3. Proxmox VE Wiki — PCI(e) Passthrough
  4. Plex Support — Using hardware-accelerated streaming
#proxmox #lxc #quick-sync#transcoding#plex#jellyfin #n100 #igpu #mini-pc #homelab

Related