Setting up Plex hardware transcoding with an Intel N100 GPU on Proxmox
Table of Contents
These are my notes on setting up Plex hardware transcoding with an Intel N100 GPU and PCI passthrough on Proxmox.
Considering this configuration:
- The host is running Proxmox 8
- The host is using EFI and
systemd-boot
- My Plex server is in a VM — not a container
- The VM is running Debian 12
These instructions might not work for you if you have a different setup!
Why is it useful? #
Most of the time, I don’t need to transcode my content on Plex: my devices can easily play 4K content. The Plex web app doesn’t support all the codecs, like Dolby Vision, but I’m running Infuse on my Mac, iPhone, and Apple TV, so I can direct stream everything.
However, I was up in the mountains a few weeks ago, and the internet connection was not great. I needed to transcode my content to a lower quality to match the available bandwidth. Thanks to hardware transcoding, I was able to do so without any buffering!
Enable PCI passthrough on Proxmox #
First, we need to enable IOMMU on the Proxmox hypervisor to be able to passthrough the GPU as a PCI device to the Plex VM.
Most guides online suggest adding intel_iommu=on
to the kernel boot parameters in /etc/default/grub
and then running update-grub
to apply the changes.
However, in my case, Proxmox 8 came with systemd-boot
as the bootloader. The Proxmox admin guide provides instructions to determine the bootloader in use.
root@pve ~# efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0007,0003,0001
Boot0000* Linux Boot Manager HD(2,GPT,3fcd1521-6aea-43d9-aa6d-dcfe2126c832,0x800,0x200000)/File(\EFI\systemd\systemd-bootx64.efi)
Boot0001* UEFI: Built-in EFI Shell VenMedia(5023b95c-db26-429b-a648-bd47664c8012)..BO
Boot0003* UEFI: PXE IPv4 Realtek PCIe GBE Family Controller PciRoot(0x0)/Pci(0x1c,0x0)/Pci(0x0,0x0)/MAC(7c8334beba78,0)/IPv4(0.0.0.00.0.0.0,0,0)..BO
Boot0007* UEFI OS HD(2,GPT,3fcd1521-6aea-43d9-aa6d-dcfe2126c832,0x800,0x200000)/File(\EFI\BOOT\BOOTX64.EFI)..BO
See the systemd-bootx64.efi
on the Linux Boot Manager
line.
According to the documentation:
The kernel command line needs to be placed as one line in
/etc/kernel/cmdline
. To apply your changes, runproxmox-boot-tool refresh
, which sets it as the option line for all config files inloader/entries/proxmox-*.conf
.
root@pve ~# cat /etc/kernel/cmdline
root=ZFS=rpool/ROOT/pve-1 boot=zfs intel_iommu=on
root@pve ~# proxmox-boot-tool refresh
Running hook script 'proxmox-auto-removal'..
Running hook script 'zz-proxmox-boot'..
Re-executing '/etc/kernel/postinst.d/zz-proxmox-boot' in new private mount namespace..
Copying and configuring kernels on /dev/disk/by-uuid/C5F7-FA6C
Copying kernel and creating boot-entry for 6.5.13-6-pve
Copying kernel and creating boot-entry for 6.8.12-5-pve
Copying kernel and creating boot-entry for 6.8.12-6-pve
After rebooting, we can verify that IOMMU is enabled:
root@pve ~# dmesg | grep IOMMU
[ 0.047817] DMAR: IOMMU enabled
[ 0.109426] DMAR-IR: IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
[ 0.299102] pci 0000:00:02.0: DMAR: Skip IOMMU disabling for graphics
[ 0.378355] DMAR: IOMMU feature fl1gp_support inconsistent
[ 0.378356] DMAR: IOMMU feature pgsel_inv inconsistent
[ 0.378358] DMAR: IOMMU feature nwfs inconsistent
[ 0.378360] DMAR: IOMMU feature dit inconsistent
[ 0.378361] DMAR: IOMMU feature sc_support inconsistent
[ 0.378363] DMAR: IOMMU feature dev_iotlb_support inconsistent
Attach the GPU to the Plex VM #
In the Proxmox UI, go to the Plex VM, then Hardware
> Add
> PCI Device
and select the GPU.
Then the VM should be rebooted from the Proxmox UI to apply the changes.
Install the Intel GPU drivers on the Plex VM #
After rebooting the VM, we can see in the kernel logs that there is indeed a GPU, but the drivers are not loaded:
root@seedbox ~# dmesg -T | grep -i i915
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] Found ALDERLAKE_P/ADL-N (device ID 46d1) display version 13.00
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] VT-d active for gfx access
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] Using Transparent Hugepages
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] *ERROR* conflict detected with stolen region: [mem 0x7c800000-0x803fffff]
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: Invalid PCI ROM header signature: expecting 0xaa55, got 0x4556
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] Failed to find VBIOS tables (VBT)
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=none:owns=io+mem
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: firmware: failed to load i915/adlp_dmc.bin (-2)
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: firmware: failed to load i915/adlp_dmc.bin (-2)
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: Direct firmware load for i915/adlp_dmc.bin failed with error -2
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: firmware: failed to load i915/adlp_dmc_ver2_16.bin (-2)
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: firmware: failed to load i915/adlp_dmc_ver2_16.bin (-2)
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: Direct firmware load for i915/adlp_dmc_ver2_16.bin failed with error -2
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] Failed to load DMC firmware i915/adlp_dmc.bin (-ENOENT). Disabling runtime power management.
[Tue Jan 28 19:31:20 2025] i915 0000:00:10.0: [drm] DMC firmware homepage: https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] [ENCODER:188:DDI A/PHY A] failed to retrieve link info, disabling eDP
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_70.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_70.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_70.1.1.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_70.1.1.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_69.0.3.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: firmware: failed to load i915/tgl_guc_69.0.3.bin (-2)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] *ERROR* GT0: GuC firmware i915/tgl_guc_70.bin: fetch failed -ENOENT
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] GT0: GuC firmware(s) can be downloaded from https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/i915
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] GT0: GuC firmware i915/tgl_guc_70.bin version 0.0.0
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] *ERROR* GT0: GuC initialization failed -ENOENT
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] *ERROR* GT0: Enabling uc failed (-5)
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] *ERROR* GT0: Failed to initialize GPU, declaring it wedged!
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm:add_taint_for_CI [i915]] CI tainted:0x9 by intel_gt_init+0xb7/0x340 [i915]
[Tue Jan 28 19:31:22 2025] [drm] Initialized i915 1.6.0 for 0000:00:10.0 on minor 1
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] Cannot find any crtc or sizes
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] Cannot find any crtc or sizes
[Tue Jan 28 19:31:22 2025] i915 0000:00:10.0: [drm] Cannot find any crtc or sizes
Note the failed to load i915
errors.
Debian 12 comes with Linux 6.1, which was released in December 2022, while the Intel N100 came out in Q1 2023..
root@seedbox ~# uname -a
Linux seedbox 6.1.0-30-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.124-1 (2025-01-12) x86_64 GNU/Linux
We can install a more recent kernel from the backports to get the latest drivers. At this time, the latest kernel in the backports is 6.12. The backports seem to follow the latest stable kernel release..
Make sure the bookworm-backports
repository is enabled in /etc/apt/sources.list
:
root@seedbox ~# grep backports /etc/apt/sources.list
deb http://deb.debian.org/debian bookworm-backports main contrib non-free-firmware
Then install the latest kernel and firmware from the backports:
apt install linux-image-amd64 firmware-linux -t bookworm-backports
root@seedbox ~# uname -a
Linux seedbox 6.12.9+bpo-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.9-1~bpo12+1 (2025-01-19) x86_64 GNU/Linux
After reboot, the driver is properly loaded:
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] Found ALDERLAKE_P/ADL-N (device ID 46d1) display version 13.00 stepping D0
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] VT-d active for gfx access
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] Using Transparent Hugepages
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] *ERROR* conflict detected with stolen region: [mem 0x7c800000-0x803fffff]
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: Invalid PCI ROM header signature: expecting 0xaa55, got 0x4556
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] Failed to find VBIOS tables (VBT)
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=none:owns=io+mem
[Tue Jan 28 18:50:25 2025] i915 0000:00:10.0: [drm] Finished loading DMC firmware i915/adlp_dmc.bin (v2.20)
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] [ENCODER:188:DDI A/PHY A] failed to retrieve link info, disabling eDP
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: GuC firmware i915/tgl_guc_70.bin version 70.20.0
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: HuC firmware i915/tgl_huc.bin version 7.9.3
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: HuC: authenticated for all workloads
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: GUC: submission enabled
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: GUC: SLPC enabled
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] GT0: GUC: RC enabled
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] Protected Xe Path (PXP) protected content support initialized
[Tue Jan 28 18:50:27 2025] [drm] Initialized i915 1.6.0 for 0000:00:10.0 on minor 1
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] Cannot find any crtc or sizes
[Tue Jan 28 18:50:27 2025] i915 0000:00:10.0: [drm] Cannot find any crtc or sizes
Enable hardware transcoding in Plex #
Before enabling hardware transcoding in Plex, I found that it was necessary to reinstall the Plex package to make it detect the GPU:
root@seedbox ~# apt reinstall plexmediaserver
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
0 upgraded, 0 newly installed, 1 reinstalled, 0 to remove and 0 not upgraded.
Need to get 81.6 MB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 https://downloads.plex.tv/repo/deb public/main amd64 plexmediaserver amd64 1.41.3.9314-a0bfb8370 [81.6 MB]
Fetched 81.6 MB in 4s (19.5 MB/s)
(Reading database ... 104138 files and directories currently installed.)
Preparing to unpack .../plexmediaserver_1.41.3.9314-a0bfb8370_amd64.deb ...
PlexMediaServer install: Pre-installation Validation.
PlexMediaServer install: Pre-installation Validation complete.
Unpacking plexmediaserver (1.41.3.9314-a0bfb8370) over (1.41.3.9314-a0bfb8370) ...
Setting up plexmediaserver (1.41.3.9314-a0bfb8370) ...
PlexMediaServer install: PlexMediaServer-1.41.3.9314-a0bfb8370 - Installation starting.
PlexMediaServer install:
PlexMediaServer install: Now installing based on:
PlexMediaServer install: Installation Type: Update
PlexMediaServer install: Process Control: systemd
PlexMediaServer install: Plex User: plex
PlexMediaServer install: Plex Group: plex
PlexMediaServer install: Video Group: video
PlexMediaServer install: Metadata Dir: /var/lib/plexmediaserver/Library/Application Support
PlexMediaServer install: Temp Directory: /tmp
PlexMediaServer install: Lang Encoding: en_US.UTF-8
PlexMediaServer install: Processor: Intel(R) N100
PlexMediaServer install: Intel i915 Hardware: Found
PlexMediaServer install: Nvidia GPU card: Not Found
PlexMediaServer install:
PlexMediaServer install: Completing final configuration.
PlexMediaServer install: Starting Plex Media Server.
PlexMediaServer install: PlexMediaServer-1.41.3.9314-a0bfb8370 - Installation successful. Errors: 0, Warnings: 0
Processing triggers for mailcap (3.70+nmu1) ...
Once that’s done and Plex has restarted, Plex should detect the GPU, which we can verify in the server’s transcoding settings, after enabling hardware transcoding:
Let’s try it out! After selecting a video and choosing a lower quality, the Plex dashboard shows that the video is being transcoded using the GPU:
Monitoring the GPU and power usage #
To verify that the GPU is used, we can install intel-gpu-tools
, which gives us the intel_gpu_top
command.
Here, I was testing with a 4K DoVi/HDR10 (HEVC Main 10)
video stream @24Mbps. The CPU was not able to keep up, so the video was stuttering.
With hardware transcoding enabled, the GPU was able to transcode the video stream in real-time, without any buffering!
In terms of power usage, the base usage of my N100 mini PC is around 10W.
- When transcoding with the CPU (which stays at 100%), the power usage goes up to 25W.
- When transcoding with the GPU, the power usage goes up to 25W, then drops down to ~15W.
By default, Plex will buffer the transcoded stream for 60s, which is why the power usage drops after a while.
So with GPU transcoding, we get a smoother experience and save power! Considering the usage, multiple 4K streams could easily be transcoded at the same time, but that’s not a use case I have.