Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

GPU Scheduling on Kubernetes

By Kokil Thapa | Last reviewed: August 2026

GPU scheduling on Kubernetes fails silently when device plugins are misconfigured or resource requests don’t match physical hardware topology. Unlike CPU and memory, GPUs are extended resources that require explicit vendor-specific setup before the scheduler can allocate them. Getting this right prevents expensive idle hardware and failed training jobs.

How does GPU scheduling on Kubernetes actually work?

Kubernetes doesn’t natively understand GPU hardware. It relies on the device plugin framework to discover accelerators and report them as extended resources. When a node starts, the device plugin daemonset registers available GPUs with the kubelet. The scheduler then sees nvidia.com/gpu: 4 (for example) in the node’s allocatable resources and can satisfy pod requests against that inventory.

NVIDIA DevicePlugin DaemonSetDiscovers GPUsKubeletNode Statusnvidia.com/gpu: 4SchedulerBind DecisionMatches RequestPod Specresources.limitsnvidia.com/gpu: 1GPU scheduling on Kubernetes flow
Device plugin registers GPUs → kubelet reports capacity → scheduler matches pod requests to available devices

Critical detail: GPUs are allocated as whole units by default. You cannot request 0.5 GPU unless you enable Multi-Instance GPU (MIG) or time-slicing. A pod requesting nvidia.com/gpu: 2 will only land on a node with at least two unallocated physical GPUs. This is why node labeling and affinity rules matter as much as the resource request itself.

How do you install and configure the NVIDIA device plugin in 2026?

The official NVIDIA device plugin Helm chart is the standard deployment method for Kubernetes 1.29+ clusters running in 2026. Avoid legacy static manifest deployments; they lack MIG support and proper upgrade paths.

Prerequisites

  • NVIDIA driver ≥ 550 installed on all GPU nodes
  • nvidia-container-toolkit ≥ 1.14 configured as the container runtime
  • Kubernetes cluster ≥ 1.29 with feature gate DevicePlugins enabled (default)
  • Helm 3.14+ installed locally

Installation steps

  1. Add the NVIDIA Helm repository:
    helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
    helm repo update
  2. Create a values file for production configuration:
    # nvidia-device-plugin-values.yaml
    version: v0.16.2
    migStrategy: mixed
    compatWithCPUManager: true
    failOnInitError: false
    nodeSelector:
      nvidia.com/gpu.present: "true"
    resources:
      limits:
        memory: 256Mi
      requests:
        cpu: 100m
        memory: 128Mi
  3. Install the plugin:
    helm install nvidia-device-plugin nvdp/nvidia-device-plugin \
      --namespace kube-system \
      --values nvidia-device-plugin-values.yaml \
      --wait
  4. Verify GPU detection:
    kubectl get nodes -o json | jq '.items[] | select(.status.allocatable["nvidia.com/gpu"] != null) | {name: .metadata.name, gpus: .status.allocatable["nvidia.com/gpu"]}'

If GPUs don’t appear in allocatable resources within 60 seconds, check the device plugin pod logs in kube-system. Common failures include missing NVIDIA driver, incorrect container runtime config, or SELinux/AppArmor blocking device access. On Ubuntu 24.04 nodes I’ve managed, the most frequent issue was nvidia-container-runtime not being set as the default runtime in /etc/containerd/config.toml.

How do you request GPUs correctly in pod specifications?

GPU resources must be specified in both requests and limits, and the values must match exactly. Kubernetes rejects pods where GPU requests differ from limits because GPUs aren’t compressible resources.

apiVersion: v1
kind: Pod
metadata:
  name: training-job
spec:
  containers:
  - name: trainer
    image: nvcr.io/nvidia/pytorch:24.08-py3
    resources:
      requests:
        nvidia.com/gpu: 2
        memory: 32Gi
        cpu: "8"
      limits:
        nvidia.com/gpu: 2
        memory: 32Gi
        cpu: "8"
    env:
    - name: NVIDIA_VISIBLE_DEVICES
      value: "all"
  nodeSelector:
    nvidia.com/gpu.product: "NVIDIA-A100-SXM4-80GB"
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule

Key points from real deployments:

  • Never omit limits. Pods without GPU limits may be scheduled but fail at runtime with “device not found” errors.
  • Use node selectors or affinity to target specific GPU models. Mixing A100s and T4s in the same pool causes unpredictable performance.
  • Set tolerations if GPU nodes have taints (common practice to prevent non-GPU workloads from landing on expensive hardware).
  • Avoid NVIDIA_VISIBLE_DEVICES=all in multi-tenant clusters. Let the device plugin handle isolation; overriding it breaks MIG partitioning.
✓ Correct Specrequests.nvidia.com/gpu: 2limits.nvidia.com/gpu: 2✓ Matches exactly✓ Scheduler + runtime agree✗ Broken Specrequests.nvidia.com/gpu: 2limits.nvidia.com/gpu: 1✗ Mismatch rejected✗ Pod stuck PendingCommon Mistake: Omitting Limits EntirelyPod schedules → container starts → runtime error: "no GPU devices available"Root cause: kubelet allocated GPU, but container runtime didn't receive device mountAlways specify identical requests AND limits for nvidia.com/gpu
GPU resource requests must exactly match limits; mismatches or omissions cause scheduling failures or runtime errors

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

Multi-Instance GPU (MIG) physically partitions A100/H100/A30 GPUs into isolated slices with dedicated memory, cache, and compute cores. Time-slicing virtually shares a single GPU across multiple pods using CUDA MPS or time-sharing. Choose based on workload isolation requirements, not just utilization metrics.

CriteriaMIG StrategyTime-Slicing
Hardware supportA100, H100, A30 onlyAll NVIDIA GPUs
Memory isolationHard partitionedShared (OOM risk)
Performance predictabilityDeterministicVariable under contention
Setup complexityRequires GPU reconfigurationConfig flag only
Best forProduction inference, multi-tenantDev/test, batch experimentation
Kubernetes resource namenvidia.com/mig-*nvidia.com/gpu (shared count)

In practice, I enable MIG mixed strategy on production clusters serving legal-tech document processing pipelines where latency SLAs matter. For internal ML experimentation environments, time-slicing with replicas: 4 per GPU gives researchers faster iteration without waiting for exclusive access. Never use time-slicing for latency-sensitive inference; one noisy neighbor stalls all co-located pods.

How do you debug GPU scheduling failures in production?

GPU scheduling issues manifest as pods stuck in Pending state with events like “Insufficient nvidia.com/gpu”. Systematic debugging requires checking four layers in order.

Diagnostic checklist

  1. Verify node GPU capacity:
    kubectl describe node <gpu-node> | grep -A5 "Allocatable:"
    If nvidia.com/gpu is missing, the device plugin isn’t running or failed initialization.
  2. Check device plugin health:
    kubectl logs -n kube-system -l app=nvidia-device-plugin --tail=200
    Look for “Failed to initialize NVML” or “permission denied” errors.
  3. Inspect pending pod events:
    kubectl describe pod <pod-name> | grep -A10 Events:
    “Unschedulable” with GPU resource shortage means capacity exists but affinity/tolerations block placement.
  4. Validate runtime configuration:
    ssh <gpu-node> 'cat /etc/containerd/config.toml | grep -A3 nvidia'
    Missing [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia] section causes silent allocation failures.
Pod Pending: Insufficient GPUDoes node show nvidia.com/gpu?NOYESFix Device Plugin• Check driver version• Verify containerd configCheck Affinity/Tolerations• Node selector mismatch?• Missing GPU taint toleration?Restart plugin + verifyUpdate pod spec labels
Decision tree for diagnosing GPU scheduling failures: start with node capacity, then branch to plugin or affinity fixes

On a recent client project involving computer vision model training, pods remained pending despite four free A100s showing in node status. The root cause was a stale node label nvidia.com/gpu.memory=40960 left over from a previous T4 deployment. The pod’s node selector targeted 81920 MB, so the scheduler correctly refused placement even though raw GPU count was sufficient. Always validate labels match actual hardware after node replacements or upgrades.

What are the operational best practices for GPU clusters?

Running GPUs in production requires discipline beyond initial setup. These patterns come from maintaining CI/CD pipelines and infrastructure for ML workloads across multiple environments.

  • Label nodes by GPU model and memory. Use nvidia.com/gpu.product and nvidia.com/gpu.memory consistently. Automated labeling via NVIDIA GPU Operator is preferred over manual annotation.
  • Taint GPU nodes. Apply nvidia.com/gpu=true:NoSchedule to prevent CPU-only workloads from consuming expensive instances. Add matching tolerations only to GPU-aware workloads.
  • Monitor allocation, not just utilization. High GPU utilization with zero pending pods indicates good packing. Low utilization with pending pods signals fragmentation or overly restrictive affinity rules.
  • Pre-pull large container images. PyTorch/TensorFlow images exceed 8GB. Use ImagePullPolicy: IfNotPresent and consider a local registry mirror to avoid 5-minute pull times during scaling events.
  • Test MIG reconfiguration offline. Changing MIG profiles requires GPU reset and takes 2–5 minutes per device. Never reconfigure production nodes during active workloads; drain first.

For teams evaluating whether to manage GPU infrastructure in-house versus using managed services, the break-even point typically arrives around 8–12 persistent GPUs. Below that threshold, cloud spot/preemptible instances with proper checkpointing often cost less than dedicated hardware plus engineering time. Above it, owned hardware with local hosting or reserved cloud capacity becomes economical, especially for steady-state inference workloads common in Nepal-based legal-tech and e-commerce platforms.

Implementing Reliable GPU Scheduling on Kubernetes

GPU scheduling on Kubernetes succeeds when you treat device plugins, resource specs, and node topology as interconnected system components rather than isolated configuration items. Install the NVIDIA device plugin via Helm with explicit version pinning, enforce matching requests and limits in every pod spec, use node labels and taints to control placement, and choose MIG or time-slicing based on isolation needs rather than convenience. Debug systematically from node capacity upward through plugin health, pod events, and runtime configuration.

If your team is setting up GPU workloads and needs hands-on implementation support, reach out to discuss your specific infrastructure requirements. Whether you’re deploying ML pipelines, inference services, or compute-intensive applications, getting the scheduling foundation right prevents costly rework later.

Frequently Asked Questions

It is the mechanism allowing Kubernetes to allocate and manage NVIDIA or AMD GPU resources across pods using device plugins and extended resource definitions for workload isolation.

Cloud GPU nodes range from USD 2–8 per hour (NPR 265–1,060), plus standard cluster management fees and storage costs depending on provider and instance type selected.

Use it when running ML training, inference, video transcoding, or batch processing workloads requiring hardware acceleration that cannot run efficiently on standard CPU-only compute nodes.

Deploy the official NVIDIA device plugin DaemonSet via Helm or kubectl apply against a cluster with compatible drivers and container runtime configured. Verify installation by checking node allocatable resources include nvidia.com/gpu entries matching physical card counts. Without this plugin, Kubernetes cannot see or schedule GPU workloads regardless of driver status. Always pin plugin versions to match your specific CUDA and driver combination to avoid silent scheduling failures in production environments.

Exclusive mode assigns entire GPUs to single pods, ensuring performance isolation but wasting capacity during idle periods. Shared mode uses time-slicing or MIG to partition one GPU among multiple pods, improving utilization density at the cost of potential contention. Choose exclusive for latency-sensitive inference or large training jobs. Use sharing for development, testing, or small batch workloads where predictable peak performance matters less than overall cluster efficiency and cost reduction.

Yes, using NVIDIA Multi-Instance GPU (MIG) on A100/H100 cards or time-slicing configuration in the device plugin. MIG creates isolated hardware partitions with dedicated memory and compute cores, offering true quality-of-service guarantees. Time-slicing shares the full GPU context-switching between pods, which risks noisy-neighbor issues under load. For production multi-tenant clusters serving legal-tech portals or e-commerce backends, MIG provides safer isolation than software-based sharing alone.

Native Kubernetes scheduling only tracks whole-GPU or MIG slice counts, not VRAM usage. Pods can exceed expected memory and trigger OOM kills without scheduler awareness. Enforce limits using GPU operator memory constraints, cgroups v2, or application-level frameworks like PyTorch that respect CUDA_VISIBLE_DEVICES boundaries. Monitor actual memory pressure via DCGM exporter metrics fed into Prometheus. Relying solely on pod resource requests leaves you vulnerable to runtime failures despite successful scheduling decisions.

The pod enters Failed state and reschedules elsewhere only if tolerations and node selectors permit migration. GPU workloads often lack checkpointing, so progress since last save is lost. Configure pod disruption budgets carefully and implement application-level resilience through periodic model checkpoints or job queue idempotency. Node auto-repair helps replace faulty hardware, but recovery time depends on cloud provider SLAs. Assume transient GPU failures will occur and design workflows accordingly rather than expecting infrastructure perfection.

Check kubectl describe pod for Insufficient nvidia.com/gpu events indicating resource exhaustion or node taints blocking placement. Verify device plugin pods are Running on target nodes and that nvidia-smi returns valid output inside those containers. Confirm node labels match pod nodeSelector requirements exactly. Review kubelet logs for driver or plugin errors. Common causes include mismatched CUDA versions, exhausted MIG profiles, or stale resource accounting after node reboots requiring manual plugin restart.

Yes, via the AMD GPU Operator and ROCm device plugin, exposing amd.com/gpu as a schedulable resource. Support is less mature than NVIDIA's ecosystem, with fewer pre-built containers and community tooling. Driver installation requires careful kernel compatibility checks on Ubuntu 22.04/24.04 nodes. Performance parity varies by workload; benchmark thoroughly before committing. For Nepal-based teams evaluating cost alternatives to NVIDIA, AMD offers viable options for inference and certain training tasks, but expect more hands-on debugging during initial setup.

Isolate tenants using namespaces with ResourceQuotas limiting GPU access per team. Apply network policies restricting inter-pod communication. Use PodSecurityAdmission to prevent privileged containers from accessing host GPU devices directly. Enable audit logging for all GPU resource requests. With sensitive domains like legal-tech portals handling client documents, ensure GPU memory is zeroed between workloads via MIG or explicit cleanup hooks. Never share GPUs across untrusted tenants without hardware-level partitioning providing genuine isolation guarantees beyond software enforcement.

Deploy NVIDIA DCGM Exporter as a DaemonSet to expose GPU utilization, memory, temperature, and ECC error metrics to Prometheus. Visualize dashboards in Grafana tracking per-node and per-pod consumption patterns. Set alerts for sustained high utilization indicating contention or low utilization signaling waste. Integrate with Kubernetes event exporters to correlate scheduling decisions with hardware state. On production systems I maintain, combining DCGM metrics with custom application telemetry reveals whether GPU allocation actually translates to business throughput versus idle resource reservation.

Yes, using Cluster Autoscaler with GPU-aware scaling policies or Karpenter for faster provisioning. Configure scale-up thresholds tied to pending nvidia.com/gpu resource requests rather than CPU/memory pressure alone. Pre-warm node pools with appropriate instance types to reduce cold-start latency for bursty workloads. Be aware that GPU nodes take longer to initialize due to driver loading and health checks. Test scaling behavior under realistic loads; theoretical capacity planning often misses real-world initialization delays affecting user-facing services like booking platforms or API endpoints.

Upgrading CUDA drivers, device plugins, or Kubernetes itself typically requires draining GPU nodes, terminating active workloads. Plan maintenance windows around job completion or checkpoint intervals. Use node pools with different driver versions to enable rolling updates without full-cluster downtime. Pin critical workloads to stable node pools while testing upgrades on separate infrastructure. In my experience maintaining production systems, coordinating GPU upgrades demands more lead time than CPU-only clusters due to hardware dependencies and longer validation cycles required to confirm workload compatibility post-upgrade.

Requesting GPUs without setting corresponding memory/CPU ratios causes bin-packing inefficiencies and stranded resources. Forgetting node taints allows non-GPU pods to land on expensive accelerator nodes. Running outdated device plugins incompatible with current drivers silently breaks scheduling. Neglecting to configure topology awareness leads to cross-NUMA GPU access penalties on multi-socket servers. Overlooking MIG profile exhaustion results in pending pods despite available raw GPU capacity. Validate configurations end-to-end with representative workloads before declaring production readiness, as subtle misconfigurations surface only under realistic scheduling pressure.

Share this article

Quick Contact Options
Choose how you want to connect me: