The module lifecycle stageGeneral 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-hello

This 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: 5

Installing 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: 5

Installing 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: 5

Installing 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: 5

Installing 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: 5

After 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 wide

Example 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 physicalgpus

Every expected card must be Ready with HEALTHY=True.

To see devices offered to the scheduler, run:

d8 k get resourceslices

If 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-40gb

To list all DeviceClasses that the GPU controller created automatically from the GPUClass, run:

d8 k get gpuclass a100 -o jsonpath='{.status.deviceClassNames}' | jq

Share 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:

  1. The check_migration hook detects the existing d8-nvidia-gpu namespace and adds the label gpu.deckhouse.io/managed-by=gpu to it.
  2. On the next reconciliation migrationReady becomes true, and the DRA templates are rendered.
  3. Helm removes the Device Plugin stack — device plugin, GFD, MIG manager, DCGM — and deploys the DRA stack in the same namespace.

After migration:

  • spec.gpu on 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.config and the nvidia.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/gpu will 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}' | jq

The 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 physicalgpus

To inspect conditions for a specific GPU, run:

d8 k get physicalgpu <name> -o jsonpath='{.status.conditions}' | jq

AdapterReady=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-adapter

A 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 sharePercent while 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-complete is 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 flag gpu-host-stack-unhealthy, and that flag is what makes the later step (gpu-setup.sh in DRA mode, gpu-sysctl.sh in 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 the D8GpuNodeHostStackUnhealthy alert after 15 minutes;

  • the gpu_node_host_stack_unhealthy_nodes metric reports how many nodes are unhealthy and is always published, including with the value 0: 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_checksum

This 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.log

On 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 --version

All 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-controller

A 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}' | jq

Also check .status.matchingPhysicalGPUCount0 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 yaml

Preparation 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=MPSFallbackToTimeSlicing

The 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-smi

The 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 -A

What 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")]}' | jq

Only 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")]}' | jq

Release 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 physicalgpus
NAME                   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 pod

Expected 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:

  1. nvidia-smi works on the node.
  2. NVIDIA Container Toolkit is installed (/usr/bin/nvidia-container-runtime exists).
  3. containerd is configured to use the nvidia runtime 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: 3

The 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:

  1. Check the GPU model. MIG is supported on H100, A100, and A30, and not on V100 or T4. See the NVIDIA profile tables.

  2. Make sure the GPU is not in use. See What to do if MIG reconfiguration or VFIO passthrough will not proceed.

  3. Check the NodeGroup configuration.

  4. Wait for nvidia-mig-manager to drain the node and reconfigure the GPU. This can take several minutes, during which the node carries the mig-reconfigure taint.

  5. Track progress through the nvidia.com/mig.config.state label: pending, rebooting, success, or failed.

  6. If nvidia.com/mig-* resources still do not appear, run:

    d8 k -n d8-nvidia-gpu logs daemonset/nvidia-mig-manager
    nvidia-smi -L