The module lifecycle stage: General Availability
The module has requirements for installation
How do I delete a workload that has a GPU allocated?
To delete a workload that uses a GPU, run:
d8 k delete job gpu-helloThis deletes the related Pods and frees the GPU.
How do I install the NVIDIA driver on GPU nodes?
The NVIDIA driver and the NVIDIA Container Toolkit are installed through a NodeGroupConfiguration. Set nodeGroups to match your GPU NodeGroup.
The list of platforms supported by the NVIDIA Container Toolkit is in the NVIDIA documentation.
Installing the driver on Ubuntu
Tested on Ubuntu 24.04.
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda-ubuntu.sh
spec:
bundles:
- ubuntu-lts
content: |
#!/bin/bash
set -e
if ! command -v curl &> /dev/null || ! command -v wget &> /dev/null; then
sudo apt update
sudo apt install -y curl wget
fi
CUDA_KEYRING_DEB="cuda-keyring_1.1-1_all.deb"
NVIDIA_GPG_KEY="/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"
sudo apt update
if [ ! -f "$CUDA_KEYRING_DEB" ]; then
wget -q https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/$CUDA_KEYRING_DEB
sudo dpkg -i $CUDA_KEYRING_DEB
sudo apt update
fi
if [ ! -f "$NVIDIA_GPG_KEY" ]; then
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
fi
if ! dpkg-query -W -f='${Status}' "linux-headers-$(uname -r)" 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y "linux-headers-$(uname -r)"
fi
if ! dpkg-query -W -f='${Status}' cuda-drivers 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y cuda-drivers
fi
if ! dpkg-query -W -f='${Status}' nvidia-container-toolkit 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y nvidia-container-toolkit
fi
if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
sudo sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
sudo update-grub
fi
nodeGroups:
- gpu
weight: 5Installing the driver on Debian
Tested on Debian 13.
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda.sh
spec:
bundles:
- debian
content: |
#!/bin/bash
set -e
export DEBIAN_FRONTEND=noninteractive
# Preseed debconf answers to avoid interactive prompts.
echo "keyboard-configuration keyboard-configuration/layout select English (US)" | debconf-set-selections
echo "keyboard-configuration keyboard-configuration/model select Generic 105-key (Intl) PC" | debconf-set-selections
echo "keyboard-configuration keyboard-configuration/variant select English (US)" | debconf-set-selections
echo "console-setup console-setup/charmap select UTF-8" | debconf-set-selections
sudo -E apt update
if ! command -v curl &> /dev/null || ! command -v wget &> /dev/null; then
sudo apt update
sudo apt install -y curl wget
fi
CUDA_KEYRING_DEB="cuda-keyring_1.1-1_all.deb"
NVIDIA_GPG_KEY="/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"
sudo apt update
if [ ! -f "$CUDA_KEYRING_DEB" ]; then
wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/$CUDA_KEYRING_DEB
sudo dpkg -i $CUDA_KEYRING_DEB
sudo apt update
fi
if [ ! -f "$NVIDIA_GPG_KEY" ]; then
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
fi
if ! dpkg-query -W -f='${Status}' "linux-headers-$(uname -r)" 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y "linux-headers-$(uname -r)"
fi
if ! dpkg-query -W -f='${Status}' cuda-drivers 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" cuda-drivers
fi
if ! dpkg-query -W -f='${Status}' nvidia-container-toolkit 2>/dev/null | grep -q "ok installed"; then
sudo apt install -y nvidia-container-toolkit
fi
if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
sudo sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
sudo update-grub
fi
nodeGroups:
- gpu
weight: 5Installing the driver on CentOS
Tested on CentOS 9.
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda.sh
spec:
bundles:
- centos
content: |
#!/bin/bash
set -e
INSTALL_NEEDED=false
# Install drivers (first run only).
if ! rpm -q nvidia-driver-cuda &> /dev/null; then
dnf install -y curl epel-release dkms gcc make dracut kernel-devel-$(uname -r) elfutils-libelf-devel
dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
rpm --import https://developer.download.nvidia.com/compute/cuda/repos/GPGKEY
dnf install -y nvidia-driver-cuda nvidia-driver-cuda-libs nvidia-settings nvidia-persistenced
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | tee /etc/yum.repos.d/nvidia-container-toolkit.repo
dnf install -y nvidia-container-toolkit
INSTALL_NEEDED=true
fi
# Force-rebuild the module for the current kernel and update initramfs.
dkms autoinstall
dracut --force
# Configure module autoload and disable nouveau.
printf '%s\n' 'nvidia' 'nvidia_uvm' 'nvidia_drm' > /etc/modules-load.d/nvidia.conf
grubby --update-kernel ALL --args="nouveau.modeset=0"
# Reboot if changes were made.
if [ "$INSTALL_NEEDED" = true ]; then
base64_timer="W1VuaXRdCkRlc2NyaXB0aW9uPWJhc2hpYmxlIHRpbWVyCgpbVGltZXJdCk9uQm9vdFNlYz0xbWluCk9uVW5pdEFjdGl2ZVNlYz0xbWluCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQK"
echo "$base64_timer" | base64 -d | tee /etc/systemd/system/bashible.timer
systemctl enable bashible.timer
base64_bashible="W1VuaXRdCkRlc2NyaXB0aW9uPUJhc2hpYmxlIHNlcnZpY2UKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9lbnZpcm9ubWVudApFeGVjU3RhcnQ9L2Jpbi9iYXNoIC0tbm9wcm9maWxlIC0tbm9yYyAtYyAiL3Zhci9saWIvYmFzaGlibGUvYmFzaGlibGUuc2ggLS1tYXgtcmV0cmllcyAxMCIKUnVudGltZU1heFNlYz0zaAo="
echo "$base64_bashible" | base64 -d | tee /etc/systemd/system/bashible.service
systemctl enable bashible.service
systemctl reboot
fi
nodeGroups:
- gpu
weight: 5Installing the driver on RED OS
Tested on RED OS 8.
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda.sh
spec:
bundles:
- redos
content: |
#!/bin/bash
set -e
INSTALL_NEEDED=false
# Install the NVIDIA drivers.
if ! rpm -q nvidia-drivers &> /dev/null || ! rpm -q cuda &> /dev/null || ! rpm -q nvidia-persistenced &> /dev/null; then
echo "Update system"
sudo dnf update -y
sudo dnf install -y nvidia-drivers.x86_64 cuda.x86_64 nvidia-persistenced.x86_64
INSTALL_NEEDED=true
fi
# Install the NVIDIA Container Toolkit.
if ! rpm -q nvidia-container-toolkit &> /dev/null; then
echo "NVIDIA container toolkit is not installed. Installing..."
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
sudo dnf install -y nvidia-container-toolkit
INSTALL_NEEDED=true
fi
# Configure GRUB and disable the nouveau driver.
if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
echo "GRUB configuration and nouveau disabling"
sudo -E grubby --update-kernel ALL --args="nouveau.modeset=0"
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
INSTALL_NEEDED=true
fi
# Create the bashible service if drivers were installed.
if [ "$INSTALL_NEEDED" = true ]; then
base64_timer="W1VuaXRdCkRlc2NyaXB0aW9uPWJhc2hpYmxlIHRpbWVyCgpbVGltZXJdCk9uQm9vdFNlYz0xbWluCk9uVW5pdEFjdGl2ZVNlYz0xbWluCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQK"
echo "$base64_timer" | base64 -d | sudo tee /etc/systemd/system/bashible.timer
sudo systemctl enable bashible.timer
base64_bashible="W1VuaXRdCkRlc2NyaXB0aW9uPUJhc2hpYmxlIHNlcnZpY2UKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9lbnZpcm9ubWVudApFeGVjU3RhcnQ9L2Jpbi9iYXNoIC0tbm9wcm9maWxlIC0tbm9yYyAtYyAiL3Zhci9saWIvYmFzaGlibGUvYmFzaGlibGUuc2ggLS1tYXgtcmV0cmllcyAxMCIKUnVudGltZU1heFNlYz0zaAo="
echo "$base64_bashible" | base64 -d | sudo tee /etc/systemd/system/bashible.service
sudo systemctl enable bashible.service
sudo systemctl reboot
fi
nodeGroups:
- gpu
weight: 5Installing the driver on Astra Linux
Tested on Astra Linux 1.8.
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda-astra.sh
spec:
bundles:
- astra
content: |
#!/bin/bash
set -e
export DEBIAN_FRONTEND=noninteractive
# Clean up.
rm -f /etc/apt/sources.list.d/cuda* 2>/dev/null || true
rm -f /etc/apt/sources.list.d/nvidia* 2>/dev/null || true
apt update
apt install -y curl wget linux-headers-$(uname -r) build-essential dkms
# CUDA repository (Debian 12).
wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/cuda-keyring_1.1-1_all.deb
dpkg -i cuda-keyring_1.1-1_all.deb
rm -f cuda-keyring_1.1-1_all.deb
apt update
# Container Toolkit repository.
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' > /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt update
# Install driver 580.
apt install -y nvidia-driver-580 nvidia-settings nvidia-compute-utils-580 nvidia-container-toolkit
# Disable nouveau.
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
update-grub
nodeGroups:
- gpu
weight: 5After applying the configuration, reboot the nodes, then check the driver on the node with nvidia-smi.
The Processes section in the nvidia-smi output must be empty before workloads start. On nodes with a graphical environment the GPU is often held by Xorg, gnome-shell, gdm, sddm, or lightdm. Those processes occupy GPU memory and block MIG reconfiguration and VFIO passthrough.
How do I verify a GPU node?
The module adds labels to every prepared node.
To list the labels, run:
d8 k get node <node-name> -o json | jq -r '.metadata.labels | with_entries(select(.key | test("gpu")))'A working GPU node must have all of the following labels:
| Label | Meaning |
|---|---|
node.deckhouse.io/gpu-setup-complete |
Node preparation is complete. Until this label is present, no GPU component is scheduled onto the node. |
gpu.deckhouse.io/vendor |
A supported GPU was discovered; the value is its vendor. The matching adapter is scheduled by this label. |
node.deckhouse.io/gpu-vfio-ready |
true if the node is ready for VFIO passthrough. false is acceptable when passthrough is not needed. |
To see the stack running on the node, run:
d8 k -n d8-nvidia-gpu get pods -o wideExample output:
NAME READY STATUS RESTARTS AGE
gpu-controller-7d9f8b6c4-xk2lp 2/2 Running 0 5m
gpu-dcgm-2gflv 1/1 Running 0 5m
gpu-dcgm-exporter-vnjrq 1/1 Running 0 5m
gpu-node-agent-54gk8 1/1 Running 0 5m
nvidia-adapter-8qgqx 3/3 Running 0 5m
To see discovered hardware, run:
d8 k get physicalgpusEvery expected card must be Ready with HEALTHY=True.
To see devices offered to the scheduler, run:
d8 k get resourceslicesIf PhysicalGPU objects exist but no ResourceSlice does, the cards are not in phase Ready: devices are published only in that phase. See What to do if PhysicalGPU objects exist but no ResourceSlice does.
How do I publish GPUs for users?
To publish GPUs for users, define a GPUClass. Users can then request a GPU by name:
apiVersion: gpu.deckhouse.io/v1alpha1
kind: GPUClass
metadata:
name: a100
spec:
selector:
matchLabels:
gpu.deckhouse.io/device: a100-sxm4-40gbTo list all DeviceClasses that the GPU controller created automatically from the GPUClass, run:
d8 k get gpuclass a100 -o jsonpath='{.status.deviceClassNames}' | jqShare those names with users. Partition and sharing filters are described in GPU classes.
How do I migrate from Device Plugin mode to DRA?
To migrate, change the dra.enabled parameter from false to true. Migration runs automatically:
- The
check_migrationhook detects the existingd8-nvidia-gpunamespace and adds the labelgpu.deckhouse.io/managed-by=gputo it. - On the next reconciliation
migrationReadybecomestrue, and the DRA templates are rendered. - Helm removes the Device Plugin stack — device plugin, GFD, MIG manager, DCGM — and deploys the DRA stack in the same namespace.
After migration:
spec.gpuon the NodeGroup no longer takes effect: nothing manages it anymore. Sharing is now chosen at the workload level. You can remove this field.- Node labels from Device Plugin mode, such as
node.deckhouse.io/device-gpu.configand thenvidia.com/*labels published by NFD, may linger for a while after NFD is removed. In DRA mode they are ignored. - Workloads that request
nvidia.com/gpuwill stop scheduling because that resource is no longer published. Move them to claims before you switch, or plan for downtime.
Why did the DRA stack not deploy at all?
If after setting dra.enabled: true the d8-nvidia-gpu namespace has no gpu-controller, gpu-node-agent, or nvidia-adapter, one of the conditions required to render the DRA templates may not hold. All of the following must be true at once: dra.enabled, migrationReady, and draFeatureGatesReady.
Check the module status:
d8 k get module gpu -o jsonpath='{.status.conditions}' | jqThe usual cause is missing DRA feature gates. That shows up as FeatureGatesReady=False with reason FeatureGatesNotEnabled and the alert D8GpuDraFirstEnablementBlocked. The gates are enabled by the platform, not by this module, so the fix is to upgrade DKP. See DRA feature gates readiness.
What happens if a feature gate is disabled while DRA is running?
The alert D8GpuDraFeatureGateDisabledOnComponent fires when a required gate is switched off on the apiserver, the scheduler, or an individual kubelet. The kubelet case is the important one: the readiness check reads the apiserver only, so a kubelet that lost the gate is caught by this alert rather than by the readiness check.
What to do if a GPU node has no DRA pods?
Check the node labels:
d8 k get node <node-name> -o json | jq -r '.metadata.labels | with_entries(select(.key | test("gpu")))'| Missing or wrong | Meaning |
|---|---|
no node.deckhouse.io/gpu-setup-complete |
Node preparation did not finish. The driver check most likely failed — run nvidia-smi on the node. |
no gpu.deckhouse.io/vendor |
No GPU was discovered. Check that the card is visible on the PCI bus and that gpu-node-agent is running. |
gpu.deckhouse.io/enabled=false |
The node was explicitly excluded from GPU management. |
gpu.deckhouse.io/maintenance=true |
The node is in maintenance and excluded on purpose. |
What to do if PhysicalGPU objects exist but no ResourceSlice does?
Devices are published only while a card is in phase Ready. Check the phase and conditions.
To list all physical GPUs in the cluster, run:
d8 k get physicalgpusTo inspect conditions for a specific GPU, run:
d8 k get physicalgpu <name> -o jsonpath='{.status.conditions}' | jqAdapterReady=False with reason DriverNotReady and the message NVML capabilities not available for this device means the adapter could not talk to the card through NVML. Check the driver on the node and the nvidia-adapter container logs:
d8 k -n d8-nvidia-gpu logs daemonset/nvidia-adapter -c nvidia-adapterA card in phase Passthrough is bound to vfio-pci and is intentionally not offered as a CUDA device.
What to do if a Pod stays Pending and the claim is never allocated?
No published device satisfies the claim. List all devices from ResourceSlice resources with their names and attributes:
d8 k get resourceslices -o json | jq '.items[].spec.devices[]? | {name, attributes}'Common causes:
- The CEL selector matches nothing. A frequent mistake is asking for
sharePercentwhile the selector excludes sharing variants with!has(...sharingStrategy)— those two conditions contradict each other. See Usage. - An unguarded attribute in CEL. Referencing an optional attribute without
has(...)makes the whole expression fail rather than evaluate to false. - The requested MIG profile conflicts with a layout that another workload is currently holding. Partitions exist only while claimed, so the Pod waits until the holder finishes. This is the scheduler refusing to double-allocate the card’s slices, not an error.
What to do if GPU workloads fail although the driver and toolkit are installed?
Installed packages do not guarantee a working stack. In DRA mode (dra.enabled: true) the module does not
just look for the components of the GPU host stack, it checks that the stack can actually serve containers:
the kernel module is loaded, nvidia-smi answers, the kernel module and the driver are the same version,
the container toolkit is present, nvidia-container-cli really sees the GPU, and the toolkit versions agree.
The check runs as an early bashible step on the node and never blocks node convergence: the node keeps receiving updates, and the step neither fails nor hangs (every call to a vendor binary is time-bounded, so a wedged kernel module cannot stall the run). What changes is:
-
the readiness label
node.deckhouse.io/gpu-setup-completeis removed, so the GPU stack components do not roll onto the broken node. The removal alone would not be enough — a later node-preparation step of the same run applies that label from its own state — so the verdict also raises a node-local bashbooster flaggpu-host-stack-unhealthy, and that flag is what makes the later step (gpu-setup.shin DRA mode,gpu-sysctl.shin Device Plugin mode) leave the label off. Nothing latches: both the label and the flag are recomputed from scratch by every node-preparation run, so no manual cleanup is needed after a fix; -
the node gets two labels with the verdict:
d8 k get nodes -L node.deckhouse.io/gpu-host-stack-error,node.deckhouse.io/gpu-host-stack-vendor -
the module exports the
gpu_node_host_stack_unhealthy{node,vendor,reason}metric and fires theD8GpuNodeHostStackUnhealthyalert after 15 minutes; -
the
gpu_node_host_stack_unhealthy_nodesmetric reports how many nodes are unhealthy and is always published, including with the value0: it shows that the check is running even when nothing is broken.
The driver and the container toolkit are external prerequisites: the module diagnoses them, but never installs, upgrades or repairs them.
When does the check run, and how do I force a re-check?
This is a node preparation check, not a monitoring probe. bashible skips all steps when the node configuration checksum is unchanged, there is no reboot annotation and the node uptime has grown, so the verdict is recomputed on node bootstrap, on a configuration change (module settings, NodeGroup, a Deckhouse update) and after a reboot — not on a timer.
Two consequences:
- a stack that breaks after a successful converge — a driver/toolkit version drift introduced by a package update without a reboot, for example — is not noticed until the next preparation run;
- a repair does not clear the verdict by itself; the check has to run again.
To force a re-check on a node, either reboot it, or remove the checksum file and wait for the next bashible tick:
rm -f /var/lib/bashible/configuration_checksumThis limitation applies to the host software stack only. The state of the GPU devices themselves is
tracked continuously and independently of node preparation: it is published in PhysicalGPU
(status.driverBinding.mode for the observed driver, status.driverBinding.managementState for whether
that binding is intended), and a card that lost its driver binding without a reason raises
D8GPUPhysicalGPUOrphaned.
In what order should I check the node?
Run this on the node itself, in this order — the checks go from the lowest layer of the stack upwards, and the first failing one is the root cause:
cat /proc/driver/nvidia/version # is the kernel module loaded?
nvidia-smi -L # does the driver answer?
nvidia-smi --query-gpu=driver_version --format=csv,noheader
nvidia-container-cli info # does the container path work?
nvidia-ctk --version
nvidia-container-cli --version # do the toolkit versions match?Details that the reason code does not carry are in the step log on the node. Where that log lives depends on the Deckhouse version, and this module supports both:
# Deckhouse 1.77 and newer — one file per bashible step:
cat /var/log/d8/bashible/step.006_gpu-check.sh.logOn Deckhouse 1.75 and 1.76 per-step logs do not exist. The step wrote to
/var/lib/bashible/step.log, which every later step overwrites, and gpu-check runs at
weight 6 — so by the end of a run its output is gone. Re-run the probes above by hand
instead.
What do the NVIDIA reason codes mean?
gpu-host-stack-error |
What it means | What to do |
|---|---|---|
host-stack-not-installed |
The node belongs to a GPU NodeGroup, but there is no trace of the vendor host stack on it at all: no kernel module, no nvidia-smi, no container toolkit. |
Install the NVIDIA driver and the NVIDIA Container Toolkit on the node, or remove the node from the GPU NodeGroup. |
driver-not-loaded |
The NVIDIA kernel module is not loaded or reports no version. | Check dmesg | grep -i nvidia, rebuild DKMS for the running kernel, reboot the node. |
nvidia-smi-failed |
The kernel module is loaded, but nvidia-smi fails or lists no GPU. |
Reinstall the driver of the same version as the loaded module. |
driver-version-mismatch |
The loaded kernel module and the installed driver are different versions — the usual trace of DKMS after a kernel change. | Rebuild the kernel module for the installed driver and reboot the node. |
driver-too-old |
The driver is older than the required 450.80.02. |
Upgrade the driver. |
compute-cap-unsupported |
The GPU compute capability is not above 6.0. |
The card is not supported by the module; use a newer GPU. |
toolkit-not-installed |
nvidia-container-runtime or nvidia-container-cli is missing. |
Install the NVIDIA Container Toolkit. On a GPU NodeGroup containerd uses the nvidia runtime by default, so without the toolkit no container starts at all. |
container-cli-failed |
nvidia-container-cli info fails: libnvidia-container cannot see the GPU. |
Check SELinux, the /dev/nvidia* devices, the CDI specification and the nvidia-container* packages. |
toolkit-version-mismatch |
nvidia-ctk and nvidia-container-cli differ in major.minor. |
Align the nvidia-container-toolkit, nvidia-container and nvidia-container-tools packages to one version. |
Reason codes are vendor-specific: the pair (gpu-host-stack-vendor, gpu-host-stack-error) identifies the
failure. GPUs of vendors for which the module has no checks yet are not probed and produce no labels.
The check makes no statement at all about a node that is outside the GPU scope: one that does not belong to a
GPU NodeGroup and merely happens to carry a card (a control-plane node with a spare GPU, a virtualization host
holding GPUs for passthrough), one where the GPUs are handed over to vfio-pci for passthrough, or one where
there is no GPU of a supported vendor. Such a node gets no reason code and no alert, and any verdict left from
a previous run is dropped silently, so a node does not keep alerting after its card has been pulled. A node
that does belong to a GPU NodeGroup and has no vendor host stack is a different case and is reported as
host-stack-not-installed.
After the fix, the next node-preparation run removes both gpu-host-stack-* labels, drops the
gpu-host-stack-unhealthy flag, restores the readiness label, and the alert stops firing — see above on how
to trigger that run if the fix required neither a reboot nor a configuration change.
What to do on RED OS 7.3 and other distributions with mixed package versions?
On RED OS MUROM 7.3 the updates repository ships several versions of the NVIDIA container packages at
once (for example nvidia-container-toolkit 1.14.4 next to nvidia-container and nvidia-container-tools
1.18.0~rc). A plain installation happily mixes them, nvidia-smi on the host works, the card is visible —
and GPU still does not work in containers. That is exactly the toolkit-version-mismatch case.
What to check:
rpm -qa 'nvidia-container*'
nvidia-ctk --version
nvidia-container-cli --versionAll nvidia-container* packages must be of one version; pin the version explicitly during installation
instead of relying on the repository default. Install the driver following the
NVIDIA driver installation guide
and the container toolkit following the
NVIDIA Container Toolkit installation guide.
What to do if the container runs but has no GPU?
The most common bug in DRA manifests: spec.resourceClaims is declared, but the container has no matching resources.claims entry. The GPU is allocated and simply not handed to the container — with no error, no event, and no warning.
spec:
resourceClaims:
- name: gpu
resourceClaimTemplateName: my-claim
containers:
- name: app
resources:
claims:
- name: gpu # Make sure this part is present.What to do if nvidia-smi finds nothing inside a Pod?
If the Pod has no claim, this is correct behavior. DRA workloads run under the default container runtime and get devices only through a claim, so a Pod without one sees no /dev/nvidia* at all. Setting NVIDIA_VISIBLE_DEVICES=all does not change this — the variable is inert in this module.
If the Pod does have a claim, check the resources.claims entry, then confirm which device was allocated:
d8 k -n <namespace> get resourceclaim -o json \
| jq '.items[].status.allocation.devices.results[] | {device, pool}'What to do if an extended-resource request is ignored?
A Pod requests gpu.deckhouse.io/<class> and stays Pending, with the resource still visible in the Pod spec.
The mutating webhook that turns extended resources into claims deliberately skips kube-system and every d8-* namespace. In those namespaces the request is left untouched, and since no node advertises it, the Pod never schedules. Run GPU workloads in your own namespaces.
If the class name does not exist, you get an explicit rejection:
Error from server (Forbidden): admission webhook "podgpuresourceclaim.gpu.deckhouse.io" denied
the request: get DeviceClass "no-such-class": deviceclasses.resource.k8s.io "no-such-class" not found
Check the available names with d8 k get gpuclass <name> -o jsonpath='{.status.deviceClassNames}'.
What to do if creating or updating a GPUClass is rejected?
The GPUClass webhook has failurePolicy: Fail, so while gpu-controller is unavailable, writes are rejected. Check that it is running:
d8 k -n d8-nvidia-gpu get deploy gpu-controllerA validation error you may hit:
partitionFilter value "1g.5gb" must be a DNS label: must not contain dots
Profile names in partitionFilter are DNS labels — write 1g5gb (no dot). The dotted form is used only in CEL selectors and in PhysicalGPU capabilities.
What to do if an expected DeviceClass is missing from a GPUClass?
If a name you expected is absent from .status.deviceClassNames, the hardware most likely does not support that combination. Filters restrict what is published; they cannot create capabilities. Check what the card can actually do:
d8 k get physicalgpu <name> -o jsonpath='{.status.capabilities}' | jqAlso check .status.matchingPhysicalGPUCount — 0 means the selector matched nothing.
What to do if GPU allocation is stuck?
Every published device declares bindingConditions: ["Ready"] and bindingFailureConditions: ["BindingFailed"], so a failed preparation surfaces on the claim rather than leaving the Pod silently pending. Check the ResourceClaim:
d8 k -n <namespace> get resourceclaim <name> -o yamlPreparation errors for VFIO claims are listed in Usage.
What to do if time-slicing was received instead of MPS sharing?
This is expected behavior and it is reported. MPS and time-slicing are advertised per node, based on whether the vendor’s binaries are present — the card model is never checked. So a claim asking for MPS is accepted even on hardware that cannot run it.
The mismatch surfaces during preparation. The module falls back to time-slicing and records an event. To view the events, run:
d8 k -n d8-nvidia-gpu get events --field-selector reason=MPSFallbackToTimeSlicingThe workload still gets sharing, just not the kind it requested: without MPS there is no per-client thread limit, so sharePercent no longer bounds compute the way it would under MPS. If the fallback itself fails, preparation stops with MPSPrepareFailed.
MPS requires compute capability 7.0 or higher. See Module capabilities by GPU vendor.
Why do DCGM pods disappear during a MIG change?
This is expected behavior. Before reconfiguring MIG, nvidia-adapter sets gpu.deploy.dcgm and gpu.deploy.dcgm-exporter to paused-for-mig-change so that DCGM stops holding the GPU. The DaemonSets return once the operation completes.
What to do if MIG reconfiguration or VFIO passthrough will not proceed?
The card must be free of running processes. Check on the node:
nvidia-smiThe Processes section must be empty. On nodes with a graphical environment the GPU is often held by Xorg, gnome-shell, gdm, sddm, or lightdm; they occupy GPU memory and block reconfiguration.
For VFIO specifically, the node also needs IOMMU enabled in BIOS and on the kernel command line, reflected by node.deckhouse.io/gpu-vfio-ready=true.
What to do if uninstalling or disabling the module hangs?
On uninstall, a pre-delete hook deletes every PhysicalGPU object and waits up to 600 seconds so gpu-controller can clear its finalizers before the CRD is removed. If the hook times out, look for leftover PhysicalGPU objects:
d8 k get physicalgpus -AWhat to do if a GPU shows as Orphaned?
If d8 k get physicalgpus reports Management=Orphaned, a driver binding exists with no owning ResourceClaim and no deliberate opt-out — typically a passthrough binding left behind by a workload that vanished. The classification only lands after the stray binding has persisted for two minutes: it is not a transient state.
Check the reason on the Managed condition:
d8 k get physicalgpu <name> -o jsonpath='{.status.conditions[?(@.type=="Managed")]}' | jqOnly Orphaned makes a device reclaim-eligible. If you see OrphanPending, the grace period is still running. Indeterminate means identity could not be resolved at all — no Node or PCI address — and no clock is started.
To hand the device back, clear the stray binding and let discovery reclassify it, or request a reset as below.
What to do if a reclaim request is refused?
The gpu.deckhouse.io/reclaim annotation asks the controller to reset and rediscover a device. While an active ResourceClaim still references the card, the request is refused rather than silently ignored. The refusal and its cause land on the Reclaim condition:
d8 k get physicalgpu <name> -o jsonpath='{.status.conditions[?(@.type=="Reclaim")]}' | jqRelease the claim first, then re-annotate.
Why does a passthrough request fail instead of entering Passthrough?
VFIO passthrough requires a bare-metal node. On a virtualised node the vendor adapter reports AdapterReady=False with reason VFIORequiresBareMetal, which derives to phase Failed — not Passthrough.
How do I take a GPU out of module management?
Annotate it with gpu.deckhouse.io/unmanaged. While the binding is still alive the device stays Managed with reason ExcludedReleasePending; once released it settles at Excluded.
How do I monitor GPUs?
DCGM Exporter is deployed automatically, its metrics are scraped by Prometheus, and dashboards are available in Grafana — cluster overview, per-node and per-namespace views, GPU inventory, and per-workload allocations. The module also exports its own metrics (gpu_inventory_*, gpu_bootstrap_*, gpu_resourceclaim_*). See Monitoring.
Are AMD or Intel GPUs supported?
Not for allocation. Adapters ship for NVIDIA and MetaX, and an adapter is what turns a discovered card into something a workload can claim.
What already works for other vendors is everything above the adapter. Discovery recognises AMD (1002) and Intel (8086) devices, so their cards appear as PhysicalGPU objects with PCI details filled in, and the node gets gpu.deckhouse.io/vendor=amd or =intel. Without an adapter those objects stay in phase Pending: nothing reads their capabilities, no ResourceSlice is published, and the scheduler never sees them.
d8 k get physicalgpusNAME NODE VENDOR DEVICE MODEL PHASE HEALTHY
worker-2-0-1002-7408 worker-2 amd 7408 Pending Unknown
Adding a vendor does not require changes to the CRD or to any core component: it needs a container implementing the adapter contract, a PCI vendor ID in the gpu-supported-vendors ConfigMap, and an adapter DaemonSet.
Device Plugin mode
The following applies only to Device Plugin mode (dra.enabled: false).
How do I verify the Device Plugin mode components?
NVIDIA Pods in d8-nvidia-gpu:
d8 k -n d8-nvidia-gpu get podExpected healthy output (example):
NAME READY STATUS RESTARTS AGE
gpu-feature-discovery-80ceb7d-r842q 2/2 Running 0 2m53s
nvidia-dcgm-exporter-w9v9h 1/1 Running 0 2m53s
nvidia-dcgm-njqqb 1/1 Running 0 2m53s
nvidia-device-plugin-80ceb7d-8xt8g 2/2 Running 0 2m53s
NFD Pods in d8-nvidia-gpu:
d8 k -n d8-nvidia-gpu get pods | egrep '^(NAME|node-feature-discovery)'Expected healthy output (example):
NAME READY STATUS RESTARTS AGE
node-feature-discovery-gc-6d845765df-45vpj 1/1 Running 0 3m6s
node-feature-discovery-master-74696fd9d5-wkjk4 1/1 Running 0 3m6s
node-feature-discovery-worker-5f4kv 1/1 Running 0 3m8s
What to do if there is an Incompatible strategy detected auto error in the device plugin or GFD logs?
Errors such as Incompatible strategy detected auto, failed to create resource manager: unsupported strategy auto, or invalid device discovery strategy mean the component cannot detect the NVML platform inside the container — usually libnvidia-ml.so.* is unavailable because the NVIDIA Container Toolkit runtime is not in use.
Check (and fix if needed) the following:
nvidia-smiworks on the node.- NVIDIA Container Toolkit is installed (
/usr/bin/nvidia-container-runtimeexists). - containerd is configured to use the
nvidiaruntime on GPU nodes. The module does this after the driver and toolkit are installed and containerd is restarted or the node is rebooted.
Afterwards, recreate the nvidia-device-plugin-* and gpu-feature-discovery-* pods in the d8-nvidia-gpu namespace.
How do I view available MIG profiles?
Predefined profiles live in the mig-parted-config ConfigMap:
d8 k -n d8-nvidia-gpu get cm mig-parted-config -o json | jq -r '.data["config.yaml"]'The mig-configs: section lists GPU models by PCI ID and the profiles each card supports, such as all-1g.5gb, all-2g.10gb, and all-balanced. Set the chosen name in spec.gpu.mig.partedConfig.
How do I define a custom MIG profile per GPU on a node?
Use partedConfig: custom and describe the partitioning per GPU index:
gpu:
sharing: MIG
mig:
partedConfig: custom
customConfigs:
- index: 0
slices:
- profile: "1g.10gb"
count: 7
- index: 1
slices:
- profile: "2g.20gb"
count: 3The module then generates a unique MIG config name for the NodeGroup and sets it in the nvidia.com/mig.config label, renders mig-enabled: true with the declared slices for the listed GPUs, and renders mig-enabled: false for every unlisted index so the remaining cards stay in full mode.
What to do if a MIG profile does not activate?
If a MIG profile does not activate, do the following:
-
Check the GPU model. MIG is supported on H100, A100, and A30, and not on V100 or T4. See the NVIDIA profile tables.
-
Make sure the GPU is not in use. See What to do if MIG reconfiguration or VFIO passthrough will not proceed.
-
Check the NodeGroup configuration.
-
Wait for
nvidia-mig-managerto drain the node and reconfigure the GPU. This can take several minutes, during which the node carries themig-reconfiguretaint. -
Track progress through the
nvidia.com/mig.config.statelabel:pending,rebooting,success, orfailed. -
If
nvidia.com/mig-*resources still do not appear, run:d8 k -n d8-nvidia-gpu logs daemonset/nvidia-mig-manager nvidia-smi -L