GPU Scheduling on Kubernetes

Khimananda Oli 8 min read Virtualization
GPU Scheduling on Kubernetes

By Khimananda Oli | Last reviewed: August 2026

Running AI or ML workloads without proper GPU scheduling on Kubernetes leads to stranded hardware, pending pods, and unpredictable training times. GPUs are expensive, stateful resources that the default kube-scheduler cannot see without specific extensions and configuration. This guide covers the exact device plugin architecture, resource declaration patterns, and sharing mechanisms you need to run production-grade accelerated workloads reliably.

KubeletNode AgentDevice Pluginnvidia-device-pluginAPI ServerExtended ResourcesSchedulerFilter & ScorePod Specrequests: nvidia.com/gpu: 1GPU Scheduling on Kubernetes Flow
Architecture of GPU scheduling on Kubernetes: device plugins register hardware capacity with the API server, enabling the scheduler to bind GPU-requesting pods to valid nodes.

How does GPU scheduling on Kubernetes actually work?

Kubernetes does not natively understand GPUs. The core scheduler only knows about CPU, memory, and ephemeral storage. To enable GPU scheduling on Kubernetes, you must deploy a Device Plugin that implements the DevicePlugin gRPC interface. This plugin runs as a DaemonSet on every GPU-equipped node and performs three critical functions: discovery, registration, and allocation.

During discovery, the plugin scans the PCIe bus or uses vendor libraries (like NVML for NVIDIA) to identify available accelerators. It then registers these devices with the kubelet via a Unix domain socket at /var/lib/kubelet/device-plugins/. The kubelet advertises this capacity to the API server as an extended resource, typically nvidia.com/gpu, amd.com/gpu, or similar. When a pod specifies this resource in its spec, the scheduler's filter phase eliminates nodes lacking sufficient capacity, and the allocate phase calls back into the device plugin to mount device files (/dev/nvidia*) and inject necessary environment variables into the container runtime.

A common mistake is assuming GPU resources behave like CPU. They do not. GPUs are non-compressible and generally non-shareable by default. If a pod requests one whole GPU but uses only 10% of its VRAM, the remaining 90% sits idle. Understanding this constraint is fundamental before configuring your cluster. For teams managing mixed workloads, reviewing Kubernetes resource limits and requests provides essential context for how extended resources differ from standard compute primitives.

How do you install and configure the NVIDIA GPU Operator?

In 2026, the NVIDIA GPU Operator remains the standard for production clusters. It automates driver installation, container toolkit setup, device plugin deployment, and optional components like DCGM exporter for monitoring. Avoid manual driver installs on Kubernetes nodes; they break during kernel upgrades and node recycling.

Prerequisites and Installation

Ensure your nodes have a compatible Linux kernel, secure boot disabled (or properly signed modules), and no pre-existing NVIDIA drivers. Install the operator via Helm:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

helm install gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator --create-namespace \
  --set driver.enabled=true \
  --set toolkit.enabled=true \
  --set dcgmExporter.enabled=true \
  --version=24.9.0

The operator creates a validator pod first. Watch for nvidia-operator-validator to reach Ready status before deploying workloads. If validation fails, check kubectl logs -n gpu-operator nvidia-operator-validator for driver compatibility or kernel header issues. On Ubuntu nodes specifically, ensure you've completed proper Ubuntu security hardening while keeping necessary kernel modules loadable.

Verifying GPU Registration

After installation, confirm GPUs are visible to the scheduler:

kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, gpus: .status.allocatable["nvidia.com/gpu"]}'

You should see integer GPU counts per node. If values are missing or zero, the device plugin has not successfully registered. Check the device plugin daemonset logs and verify the /var/lib/kubelet/device-plugins/kubelet.sock exists on the host.

When should you use MIG versus time-slicing for GPU sharing?

Whole-GPU allocation wastes resources when workloads don't saturate hardware. Two primary sharing strategies exist for GPU scheduling on Kubernetes: Multi-Instance GPU (MIG) and time-slicing. Choosing correctly depends on your workload isolation requirements and hardware generation.

MIG StrategyGPU Slice 1g.10gb (Pod A)GPU Slice 2g.20gb (Pod B)GPU Slice 3g.40gb (Pod C)Hardware-isolated partitionsTime-SlicingFull GPU (Pod X - Active)Full GPU (Pod Y - Queued)Full GPU (Pod Z - Queued)Software-multiplexed access
MIG provides hardware-level isolation for multi-tenant GPU scheduling on Kubernetes, while time-slicing allows oversubscription with potential contention.
CriteriaMIG (Multi-Instance GPU)Time-Slicing
IsolationHardware-enforced (separate SMs, memory, caches)None (shared memory space, context switching)
Supported HardwareA100, H100, H200, B-series (Ampere+)Any NVIDIA GPU (Kepler+)
OversubscriptionNo (fixed slices)Yes (configurable replica count)
Performance PredictabilityDeterministic, no noisy neighborsVariable under contention
Use CaseProduction inference, multi-tenant trainingDev/test, batch jobs, low-priority experimentation
Configuration ComplexityHigher (requires MIG profile planning)Lower (single config flag)

To enable time-slicing with the GPU Operator, create a custom resource:

apiVersion: nvidia.com/v1alpha1
kind: ClusterPolicy
metadata:
  name: gpu-operator-policy
spec:
  devicePlugin:
    config:
      name: time-slicing-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 4

This makes each physical GPU appear as four schedulable units. Pods requesting nvidia.com/gpu: 1 now receive 25% of a GPU's time. Monitor closely: if multiple replicas run simultaneously, latency increases non-linearly. For production serving workloads where tail latency matters, prefer MIG or whole-GPU allocation.

How do you write correct pod specs and node affinity for GPU workloads?

Requesting GPUs incorrectly is the most frequent cause of scheduling failures. Always specify GPU resources under resources.limits, not just requests. Kubernetes enforces that GPU limits equal requests; you cannot overcommit GPUs at the pod level.

apiVersion: v1
kind: Pod
metadata:
  name: llm-inference
spec:
  containers:
    - name: vllm
      image: vllm/vllm-openai:v0.6.3
      resources:
        limits:
          nvidia.com/gpu: 1
          memory: "32Gi"
        requests:
          nvidia.com/gpu: 1
          memory: "32Gi"
      env:
        - name: NVIDIA_VISIBLE_DEVICES
          value: "all"
  nodeSelector:
    nvidia.com/gpu.product: "NVIDIA-H100-80GB-HBM3"
  tolerations:
    - key: nvidia.com/gpu
      operator: Exists
      effect: NoSchedule

Note the nodeSelector. Without it, your pod may land on any GPU node, including older generations incompatible with your workload. Label your GPU nodes during provisioning with instance type, GPU model, and memory size. For large-scale clusters, consider using horizontal pod autoscaling with custom metrics from DCGM exporter to scale inference deployments based on actual GPU utilization rather than CPU or memory proxies.

Always include the nvidia.com/gpu toleration. GPU nodes typically carry a NoSchedule taint to prevent non-GPU workloads from consuming expensive instances. Missing this toleration causes immediate scheduling failure even when GPUs are available.

What observability and troubleshooting practices prevent GPU waste?

GPUs are too valuable to run blind. Deploy the DCGM Exporter (included in GPU Operator) to emit metrics like DCGM_FI_DEV_GPU_UTIL, DCGM_FI_DEV_FB_USED, and DCGM_FI_PROF_PIPE_TENSOR_ACTIVE. Integrate these with your existing Prometheus and Grafana monitoring stack to build dashboards showing real utilization per node, per pod, and per GPU slice.

Common issues and resolutions:

  • Pods stuck in Pending: Run kubectl describe pod <name> and check Events. "Insufficient nvidia.com/gpu" means all GPUs are allocated. "Node didn't match node selector" indicates label mismatch. Verify actual node labels with kubectl get nodes --show-labels.
  • Container fails with CUDA error: Usually a driver/runtime version mismatch. Ensure the GPU Operator's toolkit version matches your application's CUDA base image. Pin versions explicitly.
  • Low utilization despite high allocation: Your workload is CPU-bound or data-loading bound. Profile with nsys or DCGM profiling metrics before adding more GPUs.
  • MIG configuration errors: MIG profiles must sum exactly to physical GPU capacity. An A100-80GB supports specific combinations (e.g., seven 1g.10gb slices OR two 3g.40gb + one 1g.10gb). Invalid configurations cause the device plugin to crash-loop.

Set up alerts for sustained low utilization (<30% over 15 minutes) and persistent pending GPU pods. These signals indicate either over-provisioning or capacity constraints requiring cluster scaling. In compliance-sensitive environments, log GPU allocation events as part of your audit trail; SOC 2 auditors increasingly scrutinize expensive resource governance.

GPU NodeDCGM ExporterDevice PluginGPU WorkloadPrometheusMetrics StorageGrafanaDashboardsAlertmanagerNotificationsObservability Pipeline for GPU Scheduling on Kubernetes
End-to-end observability for GPU scheduling on Kubernetes: DCGM metrics flow through Prometheus to Grafana dashboards and Alertmanager for proactive capacity management.

Optimizing GPU Scheduling on Kubernetes for Production

Effective GPU scheduling on Kubernetes demands intentional architecture: deploy the GPU Operator instead of manual drivers, choose MIG for isolated multi-tenancy or time-slicing for flexible dev environments, enforce strict pod specs with node affinity and tolerations, and instrument everything with DCGM metrics. Treat GPUs as scarce, stateful infrastructure—not interchangeable compute. Audit allocations regularly, right-size workloads against actual utilization data, and automate scaling decisions based on real GPU signals rather than heuristics. If your team needs help designing a compliant, cost-efficient GPU platform or debugging persistent scheduling issues, reach out to discuss your specific workload requirements.

Frequently Asked Questions

You need NVIDIA device plugins, compatible GPU drivers, and container runtime support. Ensure kubelet is configured with the correct device plugin socket path and nodes have the nvidia.com/gpu resource label applied correctly before deploying workloads in 2026 clusters.

Deploy the official NVIDIA device plugin DaemonSet via Helm or kubectl apply using the latest stable release. Verify installation by checking that nodes report nvidia.com/gpu resources in kubectl describe node output and that the plugin pods are running without errors.

Yes, use NVIDIA MPS or time-slicing configurations in the device plugin to enable fractional GPU allocation. This allows multiple inference workloads to share physical GPUs efficiently while maintaining isolation boundaries suitable for development and non-production environments in 2026.

Multi-Instance GPU provides hardware-level isolation with dedicated memory and compute slices, while time-slicing offers software-based sharing without memory protection. MIG suits multi-tenant production workloads requiring strict isolation, whereas time-slicing works better for development or trusted batch processing tasks.

Specify nvidia.com/gpu under resources.limits in your container spec. Never place GPU requests under resources.requests since Kubernetes treats GPUs as extended resources that must be fully allocated. Always match limits exactly to avoid scheduling failures or unexpected behavior.

Check if nodes advertise nvidia.com/gpu capacity and if the device plugin runs correctly. Verify taints tolerations match GPU node labels and confirm no resource quota blocks allocation. Inspect scheduler logs and device plugin pod events for specific failure reasons.

Deploy DCGM exporter alongside Prometheus to collect GPU metrics like utilization, memory usage, and temperature. Configure Grafana dashboards using the official NVIDIA mixins to visualize per-pod GPU consumption and identify underutilized resources across your cluster efficiently.

Yes, use node labels like nvidia.com/gpu.product to distinguish GPU types. Add corresponding node selectors or affinity rules in pod specs to target specific models. This enables heterogeneous clusters where training jobs use A100s while inference uses L4 instances.

Use the NVIDIA GPU Operator which manages driver lifecycle through DaemonSets with rolling updates. Cordon GPU nodes before upgrades, drain workloads gracefully, and verify driver compatibility with your CUDA version. Test upgrades in staging first to prevent production outages during maintenance windows.

Containers access raw GPU devices bypassing some namespace isolation. Malicious code could exploit shared GPU memory or side-channel attacks. Apply PodSecurity standards, restrict privileged containers, use seccomp profiles, and prefer MIG over time-slicing for untrusted multi-tenant workloads in production.

Right-size GPU allocations using profiling data from DCGM metrics. Implement cluster autoscaler with GPU-specific node groups and use spot instances for fault-tolerant training jobs. Schedule batch workloads during off-peak hours and consolidate underutilized GPUs through time-slicing to reduce waste.

Yes, AMD provides the ROCm device plugin and Intel offers the GPU device plugin for their respective hardware. Configuration differs from NVIDIA setups but follows similar extended resource patterns. Check vendor documentation for 2026 compatibility matrices and feature parity status.

Monitor persistent GPU memory growth via DCGM metrics over time. Use nvidia-smi inside containers to inspect process-level allocation. Profile applications with CUDA memcheck or PyTorch profiler to identify unfreed tensors. Restart policies help mitigate leaks temporarily while fixing root causes.

The device plugin marks the GPU unhealthy and evicts affected pods automatically. Workloads reschedule to healthy nodes if replicas exist. Implement checkpointing for long-running training jobs and configure pod disruption budgets to maintain availability during hardware failures in production clusters.

Most managed serverless offerings now support GPU node pools with automatic provisioning. AWS EKS, GKE Autopilot, and Azure AKS provide GPU options in 2026. Verify regional availability, pricing models, and whether custom device plugin configurations are supported before committing to serverless GPU architectures.