If you've searched for GPU passthrough in Proxmox VE, you've probably landed on guides about IOMMU groups, VFIO binding, and handing an entire graphics card to a virtual machine. That's the right approach if you're running a Windows gaming VM. But if all you want is for Jellyfin, Plex, or Frigate running inside an LXC container to use your GPU for hardware transcoding or object detection, VFIO passthrough is overkill — and honestly, a lot more work than you need.
LXC containers can reach a host GPU a much simpler way: by mounting the actual device files straight into the container. No IOMMU, no vfio-pci driver binding, no rebooting into a BIOS menu to flip on virtualization extensions. You expose a couple of device nodes, fix up permissions, and you're done in about ten minutes.
This guide walks through exactly that, using an Intel or AMD integrated GPU as the working example, with notes on what changes if you're passing through an NVIDIA card instead.
What You Will Learn
- How GPU passthrough for containers differs from GPU passthrough for virtual machines
- How to find the right device files on your Proxmox host
- How to add a GPU to an LXC container using the
dev0anddev1config options - How to fix the permission errors that trip up almost everyone the first time
- How to confirm the GPU is actually being used, not just present
What Is This Feature?
An LXC container is not a virtual machine. It doesn't run its own kernel — it shares the Proxmox host's kernel and just gets an isolated view of processes, users, and the filesystem. That single fact is why GPU access works so differently for containers than for VMs.
A VM needs VFIO passthrough because the VM has its own kernel that needs to talk to the GPU's hardware directly, at the PCI level, as if it were bare metal. A container doesn't need any of that. The host kernel already has the GPU driver loaded and already exposes it as device files under /dev/dri (for Intel and AMD) or /dev/nvidia* (for NVIDIA). All the container needs is permission to open those files.
Proxmox VE handles this with a container config option called a device mount point, written as dev0, dev1, and so on. Each one maps a single device file from the host into the container, with its own ownership and permission settings. You're not passing through a PCI device — you're sharing access to a device node that the host driver already manages.
Why Would You Use It?
The most common reason is hardware-accelerated video transcoding. Jellyfin and Plex can both use Intel Quick Sync, AMD VCE, or NVIDIA NVENC to transcode video streams using dedicated hardware instead of burning CPU cycles doing it in software. On a low-power NUC or mini PC, that's the difference between four simultaneous 4K transcodes and your CPU pinned at 100% struggling with one.
It also comes up a lot with Frigate, the open-source NVR software, which uses a GPU (or a Coral TPU) to run object detection models on camera feeds in near real time.
There's a practical bonus too: because you're just sharing device files, more than one container can use the same GPU at once. Try that with VM-style VFIO passthrough and you'll hit a wall — a PCI device bound to vfio-pci can only be handed to one VM at a time. If you run Jellyfin in one container and Frigate in another, both can share a single integrated GPU with device passthrough. That's not possible with full VFIO passthrough at all.
Prerequisites
- A working Proxmox VE 9.x host (the steps below also work on 8.x — the
dev[n]option has been around since PVE 8.0) - An existing LXC container, ideally based on Debian or Ubuntu — this guide assumes you already have one; if not, create one first with a Debian 12 or Ubuntu 22.04/24.04 template
- An Intel or AMD GPU with a driver already working on the Proxmox host itself, or an NVIDIA GPU with the NVIDIA driver installed on the host
- SSH or shell access to the Proxmox host (the web GUI's Shell button works fine too)
- Root access on the host — device passthrough isn't something you configure from inside the container
One thing worth clearing up before you start: you do not need IOMMU or "Above 4G Decoding" enabled in your BIOS for this. Those settings matter for VFIO passthrough into a VM. They do nothing for container device passthrough, so don't waste time hunting through BIOS menus for something you don't need here.
Step-by-Step Tutorial
Step 1: Find your GPU's device files on the host
Log into the Proxmox host shell (not the container) and look at what's under /dev/dri:
ls -l /dev/dri/
On a host with an Intel or AMD integrated GPU, you'll typically see something like this:
crw-rw---- 1 root video 226, 0 Jul 20 09:14 card0
crw-rw---- 1 root render 226, 128 Jul 20 09:14 renderD128
card0 is the full display device. renderD128 is the render-only node, which is the one hardware transcoding actually uses. You generally want to pass through both, since some applications expect card0 to exist even if they only render through renderD128.
Notice the group column — video and render. You'll need the numeric group ID for each, which you get with:
getent group render
getent group video
On a fairly current Debian-based Proxmox host this is commonly GID 104 for render, but don't assume — I've seen it land on different numbers depending on what else is installed on the host. Always check with the command above rather than copying a number from a forum post.
Step 2: Add the device mount points to the container config
Pick the container you want to grant access to. You can either edit its config file directly at /etc/pve/lxc/<CTID>.conf, or use the pct set command, which is safer because it validates the syntax for you. Replace 100 with your container's actual ID:
pct set 100 -dev0 /dev/dri/renderD128,gid=104
pct set 100 -dev1 /dev/dri/card0,gid=44
Here, gid=104 and gid=44 are the group IDs you looked up in Step 1 — use the numbers from your own host, not these exact ones. This tells Proxmox to create the device node inside the container owned by that group, so a process running as a member of the matching group inside the container can actually use it.
If you'd rather edit the file by hand, the equivalent lines in /etc/pve/lxc/100.conf look like this:
dev0: /dev/dri/renderD128,gid=104
dev1: /dev/dri/card0,gid=44
Step 3: Create a matching group inside the container
This is the step almost everyone skips, and it's the one that causes "permission denied" later. The GID you passed in Step 2 needs to correspond to a real group inside the container, and your application's user needs to be a member of it.
Enter the container:
pct enter 100
Then create (or reuse) a group with the matching GID and add your service user to it. For example, if Jellyfin runs as a user called jellyfin:
groupadd -g 104 render
usermod -aG render,video jellyfin
If a group with that GID already exists under a different name inside the container, just add your user to that existing group instead of creating a new one — Linux only cares about the numeric GID matching, not the name.
Step 4: Restart the container
Device mount points are applied when the container starts, so a restart is required after any config change:
pct reboot 100
Step 5: Confirm the devices are visible
Back inside the container, check that the device files showed up:
ls -l /dev/dri/
You should see card0 and renderD128 listed, owned by the groups you configured. If they're missing entirely, go back and double check the container config — a typo in the device path is the usual culprit.
Commands Explained
| Command | What it does |
|---|---|
ls -l /dev/dri/ | Lists the GPU device files on the host and shows which group owns each one |
getent group render | Looks up the numeric group ID for the "render" group on the host |
pct set <CTID> -dev0 <path>,gid=<n> | Adds a device mount point to a container's config, mapping a host device file into it |
pct enter <CTID> | Opens a root shell directly inside the container, without needing SSH |
groupadd -g <n> <name> | Creates a new group inside the container with a specific numeric GID |
usermod -aG <group> <user> | Adds an existing user to a group without removing them from their current groups |
pct reboot <CTID> | Restarts the container so config changes, including device mount points, take effect |
vainfo | Reports which VAAPI hardware video profiles the system can see — useful for confirming Intel/AMD transcoding actually works |
Common Errors
"Permission denied" when the app tries to open /dev/dri/renderD128. Almost always a GID mismatch. The group you created inside the container doesn't have the same numeric ID as the group that owns the file on the host, or your application's user was never added to that group.
vainfo reports "no VA display found" or fails to load a driver. The device node is visible but there's no VAAPI driver installed inside the container to talk to it. On Debian/Ubuntu containers, install intel-media-va-driver for newer Intel GPUs (or i965-va-driver for older ones) and vainfo itself with apt install vainfo intel-media-va-driver.
The devices disappear after a Proxmox host reboot. Device mount points are read from the container config every time it starts, so this shouldn't happen. If it does, check that you edited the config for the right container ID — it's an easy typo to make when you're managing several containers.
"unable to open lxc.conf" or a syntax error when you edit the file by hand. A stray character or wrong comma placement in the dev0: line will break parsing. Stick to pct set if you're not comfortable hand-editing the config — it rejects bad syntax before it gets written.
Troubleshooting
Start by separating two different problems: "the device isn't there" versus "the device is there but nothing can use it." ls -l /dev/dri/ inside the container answers the first question immediately.
If the device is missing, the config change on the host either wasn't applied or the container wasn't actually restarted. Run pct config 100 on the host (swap in your CTID) and confirm the dev0/dev1 lines are actually present in the output. If they're not there, the pct set command didn't take — check for a typo in the flag name.
If the device is present but permission is denied, compare the GID shown by ls -l /dev/dri/ inside the container against the group your application's user belongs to. Run id jellyfin (or whatever user your app runs as) and check the group list. If the numbers don't line up, fix the group membership rather than changing the container's users — resist the temptation to just run the app as root to make the error go away. That's not a fix, it's a shortcut that trades one problem for a bigger one.
For NVIDIA GPUs specifically, the troubleshooting shape is the same but the device list is longer: you're passing through /dev/nvidia0, /dev/nvidiactl, /dev/nvidia-uvm, and usually /dev/nvidia-modeset, and the NVIDIA driver version installed inside the container has to match the driver version installed on the host exactly. A mismatch here is the single most common cause of NVIDIA passthrough failing after everything looks correctly configured.
Best Practices
Keep your containers unprivileged. Device passthrough works fine with unprivileged containers, and there's no real reason to run privileged just to get GPU access — privileged containers have a much larger attack surface if something inside gets compromised.
Pass through both card0 and renderD128 even if you think you only need one. Some applications probe for card0 at startup and fail confusingly if it's absent, even though all the actual transcoding work happens through the render node.
Don't reach for full VM-style VFIO passthrough just because a guide about it shows up first when you search. If your workload runs in a container — Jellyfin, Plex, Frigate, Immich — device passthrough is simpler, faster to set up, and lets you share the GPU across multiple containers. Save VFIO passthrough for cases where you genuinely need a full virtual machine, like a Windows gaming VM.
Write down the exact pct set commands you used somewhere outside the container. If you ever need to recreate the container, or clone it, you'll want that config again and it's much faster than re-deriving the right GIDs from scratch.
Frequently Asked Questions
Do I need to enable IOMMU for this?
No. IOMMU and VFIO are only needed for passing a GPU into a virtual machine. Container device passthrough uses the host's existing driver and doesn't touch IOMMU groups at all.
Can multiple containers use the same GPU at once?
Yes. Since you're sharing device files rather than handing over exclusive PCI ownership, several containers can access the same GPU simultaneously. This is one of the main advantages over VM passthrough.
Does this work with NVIDIA GPUs?
Yes, the same device mount point mechanism applies, but you'll pass through several /dev/nvidia* files instead of the two Intel/AMD ones, and the NVIDIA driver has to be installed on both the Proxmox host and inside the container, matching versions.
Will this slow down my Proxmox host?
No more than running the transcoding workload directly on the host would. You're not adding virtualization overhead — the container talks to the GPU driver almost as directly as a native process would.
Can I do this through the Proxmox web GUI instead of the command line?
Yes — open the container, go to Resources, click Add, and choose Device Passthrough. It sets the same dev[n] options under the hood; the CLI is just faster once you've done it a few times.
Conclusion
Once you've done this on one container, doing it on a second or third takes about two minutes, since it's the same three or four lines of config each time. The part that actually matters is getting the group IDs right — everything else is close to copy-paste.
If you came here after wrestling with a VFIO passthrough guide that felt like overkill for what you actually needed, this is usually the simpler path for anything running as an LXC container. Save the full VM passthrough route for when you genuinely need a complete virtual machine sitting on top of the GPU.