Run a media server on Proxmox long enough and you'll meet the same three walls everyone does, in the same order: getting a GPU into the VM or container, sizing transcoding so it doesn't choke, and laying out storage so the media doesn't fight the hypervisor. They're not Jellyfin or Immich bugs — they're the friction of putting hardware-hungry media apps on a virtualization host. Here's how to clear each.
Wall 1: GPU passthrough that actually sticks
You need the GPU's hardware encoder reachable from the app. Two paths on Proxmox:
- VM with PCIe passthrough — pass the whole GPU to a VM. Strong isolation; the host can't use that GPU anymore, and you'll need IOMMU enabled and the GPU bound to
vfio-pci. Best when one VM owns media. - LXC with device passthrough — share the host's GPU into an unprivileged container by mapping the render device and matching group IDs. Lighter, and lets multiple containers share one GPU — the usual choice for Jellyfin in LXC.
The common "it worked then broke after reboot" trap is non-persistent setup: bind the device and load modules on boot, or the passthrough silently reverts. This is the same host-prerequisite class as the hidden prerequisites Compose scripts miss.
Wall 2: sizing the transcode, not guessing
Hardware transcoding turns a melting CPU into a quiet GPU — but only if it's sized for real concurrency:
- Count simultaneous transcodes, not total users. Four people, but only two transcoding at once, is a two-stream sizing problem.
- Direct play beats transcoding. Match your library formats to your clients and most sessions won't transcode at all — the cheapest optimization there is.
- Immich uses the GPU too (ML, thumbnails). If Jellyfin and Immich share one GPU, their peaks can collide — the capacity-planning logic for self-hosted AI applies to shared media GPUs as well.
Wall 3: storage that doesn't fight the hypervisor
Media is terabytes; the hypervisor's fast disk is precious. Keep them separate:
- App state (Jellyfin's database, Immich's Postgres) on the fast VM/LXC disk.
- Media library on a dedicated pool/share, passed in cleanly — not stored on the same dataset Proxmox uses for VM disks.
- Avoid the ZFS-on-ZFS and ownership tangles covered in a sane Proxmox storage plan.
Order of operations
- Enable IOMMU; decide VM-passthrough vs LXC-device-share.
- Make the passthrough persistent across reboot.
- Confirm the app sees the encoder (
/dev/dripresent, ffmpeg lists the hw encoder). - Size transcoding by concurrent streams; prefer direct play.
- Split app state (fast disk) from library (dedicated pool).
Takeaway
Media on Proxmox is three predictable walls, not a hundred random ones. Get the GPU passed through persistently, size transcoding by real concurrent streams, and keep the library off the hypervisor's disks. Clear those three and Jellyfin and Immich are genuinely pleasant on Proxmox.
From across the StoicSoft network
Hand-curated reads on the same topic from sister sites in the StoicSoft family.

