The LXC Pivot: When Bare-Metal GPU Topology Met the Blackwell GSP Firmware Wall
Introduction
In the high-stakes world of deploying large-scale AI models across cutting-edge GPU infrastructure, the path from architectural vision to working deployment is rarely straight. This segment of an opencode coding session captures a particularly dramatic arc: the pivot from a KVM virtual machine that fundamentally could not support GPU peer-to-peer (P2P) DMA to an LXC container approach that promised — and initially delivered — the bare-metal topology needed for efficient multi-GPU communication, only to crash against an unexpected wall of CUDA runtime initialization failure rooted in Blackwell GPU firmware incompatibility with the Proxmox VE host kernel.
The session's overarching goal was to deploy the GLM-5-NVFP4 model — a 744-billion-parameter Mixture-of-Experts language model — across eight NVIDIA RTX PRO 6000 Blackwell GPUs using the SGLang inference engine. Earlier segments had established a working deployment inside a KVM virtual machine on Proxmox, but with a critical performance bottleneck: the VFIO/IOMMU passthrough topology presented the GPUs as being on separate PCIe root complexes, forcing inter-GPU communication through system memory rather than direct P2P DMA. This PHB (PCIe Host Bridge) topology, as revealed by nvidia-smi topo -m, crippled the tensor-parallel inference throughput that the GLM model required.
This article traces the arc of Segment 4, examining the key decisions, assumptions, and discoveries that shaped the LXC experiment. We will explore how the assistant and user navigated from the recognition that KVM's VFIO/IOMMU layer was an insurmountable barrier for P2P DMA, through the meticulous setup of an LXC container with GPU bind-mounts, to the moment of triumph when nvidia-smi topo -m confirmed the true bare-metal topology, and finally to the devastating discovery that CUDA itself would not initialize on the host — a problem that no amount of container configuration could fix.
The Context: Why LXC Seemed Like the Answer
The session's overarching goal was to deploy the GLM-5-NVFP4 model — a 744-billion-parameter Mixture-of-Experts language model — across eight NVIDIA RTX PRO 6000 Blackwell GPUs using the SGLang inference engine. Earlier segments had established a working deployment inside a KVM virtual machine on Proxmox, but with a critical performance bottleneck: the VFIO/IOMMU passthrough topology presented the GPUs as being on separate PCIe root complexes, forcing inter-GPU communication through system memory rather than direct P2P DMA. This PHB (PCIe Host Bridge) topology, as revealed by nvidia-smi topo -m, crippled the tensor-parallel inference throughput that the GLM model required.
The assistant had exhaustively researched every possible workaround within the KVM paradigm: ACS override patches to merge IOMMU groups, IOMMUFD as a newer VFIO backend, and investigating known Blackwell-era P2P bugs. The conclusion was stark: with each GPU sitting on its own PCIe root complex, there was no shared bridge to merge under, no IOMMU group trick that could bypass the isolation, and no VFIO backend that would enable cross-device P2P. The research was thorough, the verdict was final.
Then the user asked a question that opened a new door: "Would I get better results in proxmox LXC containers?" ([msg 427]). This single question proved to be the pivot that changed the trajectory of the entire investigation. Unlike KVM virtual machines, LXC containers share the host kernel and access devices through bind mounts, not VFIO passthrough. There is no IOMMU translation layer between the GPUs. The GPUs see each other's physical PCIe addresses directly. NCCL would detect the real bare-metal topology — NODE or SYS instead of PHB. P2P DMA should theoretically work.
The assistant's initial response was cautious — it began with "No, LXC won't help with P2P either" — but then corrected itself mid-message as it worked through the implications. This self-correction is a beautiful example of how structured reasoning can force a re-examination of assumptions. The assistant had been so focused on the KVM/VFIO paradigm that it initially overlooked the fundamental architectural difference of LXC.
The Implementation: Installing the NVIDIA Driver on the Proxmox Host
The first and most critical prerequisite was installing the NVIDIA driver directly on the Proxmox VE host. This was a non-trivial step — Proxmox VE is built on a custom kernel (6.8.12-9-pve) and installing proprietary NVIDIA drivers on the hypervisor host is not the recommended configuration. It trades host purity for container capability.
The assistant first explored the host state, discovering that the Proxmox host had no NVIDIA driver, no build tools, and no vfio-pci bindings — a clean slate. The GPUs were visible via lspci but unclaimed by any driver. Nouveau, the open-source NVIDIA driver, was loaded but not actively driving the GPUs.
The assistant then installed build dependencies (build-essential, pve-headers, dkms), blacklisted nouveau, and downloaded the 397MB driver installer. The driver version 590.48.01 was chosen deliberately to match the version used in the KVM guest — a consistency decision that assumed the same driver would behave identically on host and guest.
The installation ran with --dkms --silent --no-x-check flags, completing successfully. The output showed the archive uncompressing, but notably truncated before showing any warnings or errors. The assistant then verified the installation with nvidia-smi, which showed all eight RTX PRO 6000 Blackwell GPUs detected, driver version confirmed, CUDA 13.1 reported. Everything looked good.
But this was a false summit. The assistant's comment — "The warnings are about X libraries and 32-bit compat — not relevant for headless GPU compute" — was a reasonable dismissal of installation warnings. However, it embedded a critical assumption: that nvidia-smi success implies CUDA runtime availability. This assumption would prove catastrophically wrong.
The Topology Revelation: Confirming Bare-Metal P2P
With the host driver installed, the assistant turned to configuring the LXC container. The container (ID 129, named llm-two) was converted from unprivileged to privileged mode — a necessary step for full GPU device access. The device bind-mount configuration was meticulous: entries for all eight GPU device nodes (/dev/nvidia0 through /dev/nvidia7), plus /dev/nvidiactl, /dev/nvidia-uvm, /dev/nvidia-uvm-tools, and /dev/nvidia-caps. Each entry used the bind,optional,create=file flags, ensuring the container could access the devices without requiring the host to pre-create the mount points.
The container's transition from unprivileged to privileged mode caused a UID mapping issue — the container's internal UIDs no longer matched the host's expectations, breaking SSH access and file ownership. The assistant diagnosed this and fixed it by updating the container's /etc/subuid and /etc/subgid mappings and running a recursive chown to reassign file ownership.
Inside the container, the NVIDIA userspace driver was installed with the --no-kernel-module flag — the critical insight being that LXC containers share the host kernel and cannot install their own kernel modules. The container only needs the userspace components: the libraries, tools, and configuration files.
Then came the moment of truth. The assistant ran nvidia-smi topo -m inside the container, and the output was exactly what they had been chasing for hours:
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
GPU0 X NODE NODE NODE SYS SYS SYS SYS
GPU1 NODE X NODE NODE SYS SYS SYS SYS
GPU2 NODE NODE X NODE SYS SYS SYS SYS
GPU3 NODE NODE NODE X SYS SYS SYS SYS
GPU4 SYS SYS SYS SYS X NODE NODE NODE
GPU5 SYS SYS SYS SYS NODE X NODE NODE
GPU6 SYS SYS SYS SYS NODE NODE X NODE
GPU7 SYS SYS SYS SYS NODE NODE NODE X
This was the critical result. The bare-metal topology showed NODE within sockets (GPUs 0-3 on NUMA 0, GPUs 4-7 on NUMA 1) and SYS across sockets — precisely the topology needed for P2P DMA to function. The LXC approach had succeeded where KVM had failed. The assistant's response — "Excellent! This is the critical result" — captured the significance of the moment. The entire LXC migration was validated by this single command. The team now had definitive proof that LXC containers could expose the true bare-metal GPU topology, with NODE connections within each NUMA domain.## Building the ML Stack: From Optimism to Infrastructure
With the topology confirmed, the assistant moved swiftly to capitalize on the hard-won victory. The todo list shows the progression: explore the host, install the driver, configure the container, install the NVIDIA userspace driver, and now — set up the ML stack.
The model cache was a major consideration. The KVM VM had a 296GB HuggingFace cache stored on a ZFS zvol. Rather than re-downloading everything, the assistant devised a plan to copy the cache from the VM's ZFS zvol to a shared dataset and bind-mount it into the container. This required mounting the zvol — a process that involved identifying the correct partition device node (the zvol had a partition table, so the raw device /dev/zvol/tank/vm-128-disk-1 couldn't be mounted directly; instead, the partition /dev/zvol/tank/vm-128-disk-1-part1 had to be used).
The file copy itself was a 405GB operation — the model weights plus the HuggingFace cache. The assistant used rsync with --progress to transfer the data from the mounted zvol to the host's filesystem, a process that took significant time but completed successfully.
Meanwhile, the assistant began installing the ML stack inside the container: CUDA 12.8 toolkit (chosen to match PyTorch's build), a Python virtual environment, PyTorch, and SGLang. The installation of SGLang proved unexpectedly difficult — the package resolver hung repeatedly, forcing the assistant to debug dependency conflicts, batch-install packages, and eventually build from source.
The Moment CUDA Failed
After all this work — the driver installation, the container configuration, the topology verification, the model cache transfer, the ML stack setup — the assistant finally ran the definitive test: a simple Python diagnostic to verify CUDA initialization inside the container.
The result was devastating:
/root/ml-env/lib/python3.12/site-packages/torch/cuda/__init__.py:184: UserWarning:
CUDA initialization: CUDA driver initialization failed, you might not have a CUDA gpu.
torch.cuda.is_available() returned False. Any attempt to query GPU properties crashed with an exception. The second command testing SGLang server importability never produced output, presumably because the first script's error terminated the entire heredoc or because the SGLang import itself failed due to the missing CUDA runtime.
This was the moment when an entire approach — the LXC container workaround for GPU P2P DMA — was revealed to be fundamentally blocked, not by topology constraints, but by a deeper driver compatibility issue between the NVIDIA Blackwell architecture and the Proxmox VE host kernel.
The Diagnostic Spiral: Chasing the Ghost
The assistant spent the next dozen messages systematically debugging this failure. The error pattern was deeply puzzling: nvidia-smi detected all 8 GPUs perfectly, but any CUDA program calling cuInit() failed with error code 3 (CUDA_ERROR_NOT_INITIALIZED). This dissociation between the driver's administrative interface and its compute runtime pointed toward a problem in the driver's initialization sequence — something that happens after the kernel module loads but before the CUDA runtime can take control.
The assistant checked device file permissions, verified the kernel module was loaded, tested with raw libcuda.so calls via Python's ctypes, downgraded PyTorch from 2.10.0 to 2.9.1, compiled CUDA test programs, and inspected /proc/driver/nvidia/gpus/ for each GPU. Every check came back positive for the infrastructure but negative for CUDA initialization.
One hypothesis was that /dev/nvidia-modeset — a device node associated with the NVIDIA modeset kernel module — was missing from the container. The assistant found that it was indeed absent on the host as well. But adding it to the container config wouldn't solve the root problem, because the host itself lacked this device node.
Another hypothesis was a minor number mismatch between the device nodes and the kernel's internal GPU numbering. The assistant observed that the minor numbers reported by /proc/driver/nvidia/gpus/ for each GPU bus did not match the minor numbers of the /dev/nvidiaX device nodes inside the container. This triggered a critical insight: if the device nodes are bind-mounted directly from the host, any mismatch must originate from the host.
The Moment the Blame Shifted
The assistant executed a command on the Proxmox host itself to check two things: the device nodes and whether CUDA could initialize on the bare host. The results were devastating to the LXC hypothesis:
Host device nodes:
crw-rw-rw- 1 root root 195, 0 Feb 19 04:34 /dev/nvidia0
crw-rw-rw- 1 root root 195, 1 Feb 19 04:34 /dev/nvidia1
...
Host CUDA test:
cuInit: 3
The host had all 8 device nodes with the expected minor numbers (0 through 7). But cuInit returned 3 — CUDA_ERROR_NOT_INITIALIZED — even on the bare Proxmox host, with no container layer involved at all.
This was the moment of truth. The CUDA initialization failure was not caused by:
- Missing device node bind-mounts in the LXC config
- Incorrect cgroup device permissions
- Container-specific namespace isolation
- Mismatched minor numbers between container and host It was a host-level problem. The NVIDIA kernel module loaded successfully,
nvidia-smidetected all 8 GPUs,/proc/driver/nvidia/was fully populated, but the CUDA user-space driver library (libcuda.so) could not initialize. This was a fundamental incompatibility between the driver and the kernel, or between the driver and the GPU firmware.
The Open vs. Proprietary Kernel Module Hypothesis
With the problem isolated to the host, the assistant examined the loaded kernel module. Message 524 revealed the critical clue: /proc/driver/nvidia/version showed "NVIDIA UNIX Open Kernel Module for x86_64 590.48.01." The system was running the open-source variant of NVIDIA's kernel module.
The assistant hypothesized that Blackwell GPUs — a brand-new architecture with compute capability 12.0 — might require the proprietary kernel module instead. This was a reasonable inference: NVIDIA's open-source kernel module, introduced in the R515 driver series, has historically lagged behind the proprietary module in supporting new hardware.
Further evidence emerged when modinfo nvidia revealed the firmware files bundled with the driver:
firmware: nvidia/590.48.01/gsp_tu10x.bin
firmware: nvidia/590.48.01/gsp_ga10x.bin
Two firmware files — one for Turing architecture (tu10x) and one for Ampere/Ada (ga10x). No Blackwell firmware file was present.
The assistant spent messages 527 through 530 searching the NVIDIA installer's help and advanced options for the flag to switch kernel module types, eventually discovering --kernel-module-type=proprietary in the advanced options output.
The Proprietary Module Gamble and Its Reversal
Message 531 captured the moment of action: the assistant unloaded all NVIDIA kernel modules and reinstalled the driver with --kernel-module-type=proprietary. The procedure required stopping the LXC container, killing nvidia-persistenced, disabling persistence mode, and unloading modules in dependency order — a textbook example of clean driver replacement.
The result, revealed in message 533, was stark:
NVRM version: NVIDIA UNIX x86_64 Kernel Module 590.48.01
cuInit: 101
No devices were found
The proprietary module loaded successfully, but cuInit now returned error code 101 (CUDA_ERROR_NO_DEVICE), and nvidia-smi reported "No devices were found." The proprietary module made the GPUs completely invisible — a worse outcome than the open module, which at least detected them.
The assistant's response in message 534 was swift and decisive. Recognizing that the proprietary module was a dead end, it immediately reversed course:
"The proprietary module can't see the GPUs at all! The open module was recommended for a reason — Blackwell GPUs require the open module."
This reversal is a textbook example of scientific debugging: form a hypothesis, test it, and accept the evidence even when it contradicts your expectations. The assistant reinstalled the open kernel module, confirming that Blackwell GPUs on this driver version require the open-source module path.
The GSP Firmware Hypothesis
With the kernel module type eliminated as the root cause, the assistant turned its attention to the GSP (GPU System Processor) firmware. The GSP is a dedicated microcontroller embedded in modern NVIDIA GPUs (since Turing) that handles low-level initialization, power management, and firmware loading. Without the correct GSP firmware, the GPU cannot complete its initialization sequence, and cuInit will fail — even though nvidia-smi can still enumerate the device at the PCIe level.
Message 535 captured the assistant's observation: "I notice there's no GSP firmware loading message — it should show loading gsp_*.bin. On Blackwell GPUs, the open module requires GSP firmware." The dmesg output confirmed the absence of any GSP-related messages from the NVIDIA driver, suggesting the firmware loading step was either being skipped or failing silently.
Message 536 drove the point home with a targeted file listing:
ls -la /lib/firmware/nvidia/590.48.01/
The directory contained exactly two files: gsp_ga10x.bin (73 MB) for Ampere and gsp_tu10x.bin (30 MB) for Turing. No Blackwell firmware file existed.
The Counterexample: Why Does the VM Work?
The assistant's reasoning in message 537 grappled with a crucial contradiction. The same driver version (590.48.01) worked perfectly inside the KVM VM, where the guest operating system ran its own instance of the NVIDIA kernel module against VFIO-passed GPUs. If missing firmware files were the sole cause, the VM should also fail — but it didn't.
The assistant articulated two possible explanations:
- The GSP firmware for Blackwell might be embedded within the kernel module binary itself (
nvidia.ko) rather than distributed as a separate.binfile. The open module's initialization path might differ between native and VFIO-passed devices, with the native path requiring separate firmware files that aren't present. - The Proxmox VE kernel (6.8.12-9-pve) might be too old for the Blackwell open module. The VM, by contrast, runs its own kernel (likely a standard Ubuntu or Debian kernel) that could be newer or configured differently. The assistant checked and confirmed the kernel version:
6.8.12-9-pve, a Proxmox-specific build from March 2025. This second hypothesis gained traction when the assistant discovered that a kernel upgrade was available — fromproxmox-kernel-6.8.12-9to6.8.12-18— suggesting that the PVE kernel was indeed lagging behind.
The Silence at Message 538
Message 538 is notable for what it does not contain: an empty user message. In the flow of the conversation, this silence carries meaning. After the assistant presented the kernel upgrade finding, the user offered no new information or direction. The assistant interpreted this as a continuation signal — a non-verbal "proceed" or "keep going."
The assistant's response (message 539) was a comprehensive status summary spanning 871 lines, recapping the entire project's goals, discoveries, blockers, and next steps. This was a strategic reset: before attempting the kernel upgrade or any other intervention, the assistant ensured both parties shared a complete understanding of the situation. The summary documented the hardware configuration, the LXC GPU topology success, the CUDA initialization failure, the open vs. proprietary module experiment, the GSP firmware gap, and a detailed list of next steps for resolving the blocker.
The Deeper Significance: Blackwell's Open-Source Mandate
One of the most important findings in this segment is that Blackwell GPUs require the open-source NVIDIA kernel module. The proprietary module, which has been NVIDIA's primary Linux driver for decades, cannot detect Blackwell GPUs at all. This represents a significant shift in NVIDIA's driver strategy. The open-source kernel module, introduced in the R515 driver series, was initially positioned as an alternative for data center GPUs. Blackwell appears to be the architecture where this becomes the primary — and perhaps mandatory — path.
This has practical implications for anyone deploying Blackwell GPUs on Linux. Administrators must ensure they install the open-source kernel module variant, which may have different dependencies, firmware requirements, and compatibility profiles than the proprietary module they're accustomed to. The GSP firmware requirements add another layer of complexity: the open module requires specific firmware blobs that may not be present in all driver versions.
Lessons Learned
This segment offers several important lessons for anyone deploying GPU workloads on virtualized infrastructure:
1. nvidia-smi is not a sufficient test for GPU availability. The NVIDIA driver stack has multiple layers — kernel module, firmware, driver API, runtime API, and application libraries — and each layer can fail independently. A GPU that appears in nvidia-smi is not necessarily a GPU that CUDA can use. A CUDA runtime test — even python -c "import torch; torch.cuda.is_available()" — is the minimum bar for declaring success.
2. LXC containers inherit host driver limitations. While LXC containers successfully bypass VFIO's IOMMU isolation and expose bare-metal GPU topology, they also inherit the host's driver stack. If the host's NVIDIA driver doesn't fully support the GPU architecture — particularly for new architectures like Blackwell with GSP firmware requirements — the container cannot compensate.
3. The GSP firmware requirement for Blackwell GPUs introduces a new dependency. Blackwell GPUs require GSP firmware that may not be present in older NVIDIA driver packages or supported by older kernels. This is a relatively new requirement that existing deployment scripts and container configurations may not account for.
4. Systematic hypothesis testing is essential. The assistant's methodical approach — ruling out container-specific issues by testing on the host — is sound engineering practice. The experiment was clean, the result was unambiguous, and the conclusion was inescapable. Even when the outcome was negative, the debugging process generated valuable knowledge.
5. New GPU architectures require careful driver validation. The assumption that a driver version that works on one platform (KVM guest) will work on another (Proxmox host with LXC) proved incorrect. The driver's compatibility with the host kernel, its firmware support for the specific GPU architecture, and its initialization sequence all need independent validation.
6. The dissociation between nvidia-smi and CUDA is a critical diagnostic signal. When nvidia-smi works but cuInit fails, the problem is almost certainly in the userspace-to-kernel interface, the firmware loading path, or the kernel module's initialization sequence. This pattern should immediately direct attention to GSP firmware, kernel module type, and kernel version compatibility.
Conclusion
The LXC experiment represented a bold and creative approach to a fundamental hardware limitation. The assistant and user correctly identified that LXC containers could bypass VFIO's IOMMU isolation, and they successfully demonstrated that the container exposed true bare-metal GPU topology with NODE and SYS connections. This was a genuine achievement — the topology was correct for P2P DMA.
But the approach was ultimately blocked by a deeper issue: the NVIDIA driver's GSP firmware incompatibility with the Proxmox VE kernel. The Blackwell GPUs required firmware that the 590.48.01 driver did not provide, and the older PVE kernel may not have supported Blackwell's GSP requirements. This was a fundamental difference from the KVM VM, where the guest's own driver stack handled VFIO-passed GPUs without host-level GSP firmware dependencies.
The segment ends with the investigation at an impasse. The LXC approach showed promise for P2P but was blocked by host driver initialization issues that needed resolution before the ML stack could function. The session would need to explore alternative strategies: upgrading the Proxmox kernel, finding compatible NVIDIA driver versions with Blackwell GSP firmware, or accepting the VFIO P2P limitation and optimizing around it.
For anyone working with cutting-edge hardware on virtualization platforms, this segment is a cautionary tale and a master class in systematic debugging. It demonstrates the importance of testing each layer of the stack independently, the danger of assuming that success at one layer guarantees success at the next, and the value of clean experiments that isolate variables. The LXC approach was architecturally correct but practically blocked — a reminder that in complex systems engineering, the path from theory to working deployment is rarely straight.
The discovery that Blackwell GPUs require the open-source kernel module — and that driver 590.48.01 lacks the necessary GSP firmware for this architecture — is a concrete, actionable finding that will inform the next phase of the investigation. The LXC container approach remains conceptually sound: the bare-metal topology inside the container confirmed that P2P DMA should work, and the CUDA initialization failure is a host-level driver compatibility issue, not a container configuration problem. Resolving this blocker will require either upgrading the Proxmox VE kernel to a version that supports the Blackwell open module's firmware loading requirements, finding a driver version that includes Blackwell GSP firmware, or exploring alternative initialization paths.
In the broader narrative of this opencode session, this segment represents the moment when an elegant architectural workaround (LXC containers) collided with a fundamental dependency (driver and firmware support for a new GPU architecture). It is a reminder that in complex systems engineering, the most elegant solutions can be defeated by the lowest-level software components — in this case, a missing firmware binary for a microcontroller most developers never even know exists.
References
[1] Chunk article: "The LXC Experiment: From P2P Breakthrough to CUDA Dead End" — Detailed coverage of the LXC container setup, topology verification, and initial CUDA failure diagnosis.
[2] Chunk article: "The Blackwell GSP Firmware Wall: Debugging CUDA Initialization Failure in an LXC GPU Environment" — In-depth analysis of the GSP firmware investigation, open vs. proprietary module experiments, and kernel version hypothesis.
[3] Segment 3 summary — Established the KVM VFIO/IOMMU P2P bottleneck that motivated the LXC pivot.
[4] Message 427 — User's pivotal question: "Would I get better results in proxmox LXC containers?"
[5] Message 524 — Critical discovery that cuInit fails on the host itself, ruling out container-specific issues.
[6] Message 531 — Driver reinstallation with proprietary kernel module.
[7] Message 533 — Proprietary module fails to detect any GPUs (error code 101).
[8] Message 534 — Swift reversal back to open-source module, confirming Blackwell requires open module.
[9] Message 535 — GSP firmware loading investigation.
[10] Message 536 — Confirmation that only gsp_ga10x.bin and gsp_tu10x.bin exist, no Blackwell firmware.
[11] Message 537 — Kernel version hypothesis and VM vs. host discrepancy analysis.
[12] Message 538 — Empty user message, interpreted as continuation signal.
[13] Message 539 — Comprehensive 871-line status summary of the entire project.