Skip to content
Back to skills

Kubernetes Gpu

ASecurity

Use when getting NVIDIA GPUs onto Kubernetes — GPU Operator, container toolkit, RuntimeClass, CDI, or nvidia.com/gpu missing from node capacity.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
ai-agentsbashnodekubernetes

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add Getty/skills --skill kubernetes-gpu --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Kubernetes Gpu?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Kubernetes Gpu
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/getty-kubernetes-gpu/badge)](https://www.skillsdirectory.com/skills/getty-kubernetes-gpu)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: kubernetes-gpu
description: "Use when getting NVIDIA GPUs onto Kubernetes — GPU Operator, container toolkit, RuntimeClass, CDI, or nvidia.com/gpu missing from node capacity."
---

# NVIDIA GPUs on Kubernetes

Four things must line up before a pod can use a GPU: a **kernel driver** on the
host, a **container runtime** that can inject devices, a **device plugin** that
advertises `nvidia.com/gpu`, and a **scheduling request** for it. Every failure
below is one of the four missing, or two of them installed twice.

## Detect the card from sysfs, never from a model list

```bash
# vendor 0x10de = NVIDIA; class 0x0300xx = VGA, 0x0302xx = 3D controller
for d in /sys/bus/pci/devices/*; do
  printf '%s %s %s\n' "$(cat $d/vendor)" "$(cat $d/class)" "$d"
done | grep -E '^0x10de 0x030[02]'
```

`lspci` renders names from the host's `pci.ids`, which is always older than the
newest card — a GB10 shows up as `Device [10de:2e12]` and matches no marketing
name. Vendor and class come from the hardware and keep working.

Exclude virtual display adapters by vendor (`1af4` virtio, `1b36` QEMU, `15ad`
VMware, `80ee` VirtualBox), and let a real NVIDIA card win over them: a VM with
a passed-through GPU has both.

Whether a **driver** is already there is a different question, and package state
is the wrong way to ask it — vendor appliances (DGX OS) install outside the
distro's package namespace. Ask the system: `nvidia-smi` enumerates GPUs *and*
`libcuda.so` is in the linker cache.

## Host driver, or operator driver — never both

`driver.enabled` in the ClusterPolicy and a host-installed driver are two
implementations of the same job. Pick one:

- **Host driver** (`driver.enabled: false`): the node owns it, survives operator
  changes, and is the only option for vendor images that ship their own.
- **Operator driver** (`driver.enabled: true`): a DaemonSet builds and loads it.
  Its init container detects a preinstalled driver, labels the node, and
  terminates itself — so the mistake is survivable, but it means the pin in your
  ClusterPolicy is silently doing nothing.

On Ubuntu, install by letting the system resolve the package:

```bash
apt install -y linux-headers-$(uname -r)
ubuntu-drivers install          # let it fail loudly rather than guessing a name
```

Never hardcode a branch number. The branch, open-vs-proprietary, and the
architecture are three separate questions and all three are part of the package
*name*: Grace Hopper and Blackwell run only the open kernel modules, Maxwell
through Volta only the proprietary ones, arm64 packages come from ports, and a
transitional package like `nvidia-driver-535` on 24.04 pulls something entirely
different. `ubuntu-drivers` resolves all three from the card's PCI modalias.

Never install `linux-headers-generic`: on a vendor kernel (`6.17.0-1029-nvidia`)
it pulls headers for a *different* kernel and DKMS builds against the wrong tree.

## The container toolkit and the runtime handler

The toolkit gives containerd an `nvidia` runtime that injects devices. K3s and
RKE2 scan `PATH` for `nvidia-container-runtime` **at service start only** —
including `/usr/local/nvidia/toolkit`, where the operator's toolkit DaemonSet
installs it — write the matching containerd config themselves, and ship the
`nvidia` RuntimeClass. So: install the runtime *before* the service, or restart
the service afterwards. RKE2's unit sets no `PATH` at all; add one in
`/etc/default/rke2-{server,agent}` (see skill `kubernetes-rke2`).

Neither distribution makes nvidia the **default** runtime, and you should not
either. Workloads opt in:

```yaml
spec:
  runtimeClassName: nvidia
  containers:
    - name: cuda
      resources:
        limits:
          nvidia.com/gpu: 1      # a limit, not a request — GPUs are not shareable by default
```

With CDI enabled — the operator's default — the operator deliberately stops
configuring `nvidia` as the default runtime handler and hands the toolkit
`NVIDIA_RUNTIME_SET_AS_DEFAULT=false`. That, not the absence of
`CONTAINERD_SET_AS_DEFAULT`, is what keeps runc the default: the toolkit's own
default for `--set-as-default` is *true*. Leaving the variable out is a
preference, not a guard.

## Telling the operator where containerd lives

`toolkit.env` needs `CONTAINERD_SOCKET` and `CONTAINERD_CONFIG` on K3s/RKE2,
because the defaults (`/run/containerd/...`, `/etc/containerd/...`) are right on
neither. The operator derives its `RUNTIME_*` variables from them and mounts the
**directory** of each into the toolkit DaemonSet — which is why a wrong-but-
existing path is the worst case: the hostPath mounts cleanly and the failure
only appears when the toolkit tries to reach containerd through it.

## Discovery labels

Node Feature Discovery labels nodes from PCI data:
`feature.node.kubernetes.io/pci-0300_10de.present` (or `pci-0302_10de`) — the
same evidence as the sysfs scan, from inside the cluster. GPU Feature Discovery
adds product-level labels (`nvidia.com/gpu.product`, `.memory`, `.count`).

Run **one** of NFD and the operator's bundled `nfd.enabled` — two NFD masters
fight over the same labels. Same for `gfd`.

## Verify from the outside in

Everything before this is a prediction; these two are proof:

```bash
kubectl get nodes -o jsonpath='{.items[*].status.capacity.nvidia\.com/gpu}'
kubectl run cuda --rm -it --restart=Never --overrides='{"spec":{"runtimeClassName":"nvidia"}}' \
  --image=nvidia/cuda:12.4.0-base-ubuntu22.04 --limits=nvidia.com/gpu=1 -- nvidia-smi
```

An empty capacity means the device plugin never started or found no driver.
`Init:ImagePullBackOff` across several DaemonSets means the **validator** image
tag is wrong — from operator v25.10 the validator lives in the operator image
and carries the operator's own version; a separately pinned
`gpu-operator-validator` stops at v25.3.4 and every operand's init container
fails on it.

## Related

- `kubernetes-rke2` — the empty unit PATH and the containerd config paths.
- `kubernetes-concepts` — scheduling, DaemonSets, node capacity.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…