<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Kubenatives]]></title><description><![CDATA[Production Kubernetes for ML/AI workloads: GPU infrastructure, control plane internals, and model serving patterns for engineers running inference at scale.]]></description><link>https://www.kubenatives.com</link><image><url>https://substackcdn.com/image/fetch/$s_!q9ha!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31bffe4b-fc8e-4c9e-a75f-32431dcb5469_1080x1080.png</url><title>Kubenatives</title><link>https://www.kubenatives.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 27 Jul 2026 19:21:51 GMT</lastBuildDate><atom:link href="https://www.kubenatives.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Sharon Sahadevan]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[kubenatives@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[kubenatives@substack.com]]></itunes:email><itunes:name><![CDATA[Sharon Sahadevan]]></itunes:name></itunes:owner><itunes:author><![CDATA[Sharon Sahadevan]]></itunes:author><googleplay:owner><![CDATA[kubenatives@substack.com]]></googleplay:owner><googleplay:email><![CDATA[kubenatives@substack.com]]></googleplay:email><googleplay:author><![CDATA[Sharon Sahadevan]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[RDMA and InfiniBand: Why GPU Networking Is Different on Kubernetes]]></title><description><![CDATA[A 70B model split across 8 GPUs moves hundreds of gigabytes per second between nodes. Standard Kubernetes pod networking handles zero of that well. Here is what to run instead.]]></description><link>https://www.kubenatives.com/p/rdma-infiniband-kubernetes-gpu-networking</link><guid isPermaLink="false">https://www.kubenatives.com/p/rdma-infiniband-kubernetes-gpu-networking</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Mon, 27 Jul 2026 17:22:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oZey!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Single-node GPU workloads are simple. The GPU talks to CPU memory over PCIe, to other GPUs in the same box over NVLink. No network involved. Kubernetes networking never enters the picture.</p><p>Multi-node GPU workloads are different. A Llama 70B model split across 2 nodes with 4 GPUs each requires all-reduce operations at every training step or tensor-parallel inference call. That is hundreds of gigabytes per second of cross-node traffic.</p><p>The default Kubernetes network stack uses kernel TCP over a CNI like Calico or Cilium. Maximum practical throughput: 10 to 25 Gbps. Round-trip latency: 100 to 500 microseconds.</p><p>For distributed GPU training, those numbers are 10 to 100x too slow. The GPUs finish their math and wait on the network. Your $30K per hour H100 cluster becomes a $30K per hour network stall.</p><p>This article covers what RDMA and InfiniBand actually are, why they matter for GPU workloads, and how to integrate them with Kubernetes.</p><div><hr></div><p><strong>What RDMA Actually Does</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oZey!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oZey!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 424w, https://substackcdn.com/image/fetch/$s_!oZey!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 848w, https://substackcdn.com/image/fetch/$s_!oZey!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!oZey!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oZey!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png" width="1456" height="927" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:927,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:232397,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194907931?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oZey!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 424w, https://substackcdn.com/image/fetch/$s_!oZey!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 848w, https://substackcdn.com/image/fetch/$s_!oZey!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!oZey!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F040f6c3d-870a-4418-ab8e-7b57207d0055_1668x1062.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>RDMA stands for Remote Direct Memory Access. The name describes the mechanic exactly. One machine reads or writes the memory of another machine directly, without involving either machine&#8217;s CPU or operating system kernel.</p><p>In a normal TCP send, data goes from application memory, into kernel buffers, through the TCP stack, across the network card, into the other machine&#8217;s network card, back up through its kernel, into its application memory. Every hop copies data and switches context.</p><p>RDMA skips all of that. The application pins a memory region, hands a pointer to the NIC, and the NIC does DMA directly. The remote NIC receives and writes straight into the target memory region. No kernel involvement on either side.</p><p>The result: 200+ Gbps throughput, single digit microsecond latency, and near zero CPU usage on both ends.</p><p>For GPU workloads, there is a second layer. GPUDirect RDMA lets the NIC read from and write to GPU memory directly, bypassing host CPU memory entirely. The GPU sends data. The NIC picks it up from GPU memory. It lands in GPU memory on the other side. CPUs and host RAM are not in the path.</p><p>This is how NCCL (NVIDIA Collective Communications Library) achieves the kind of throughput distributed training needs.</p><div><hr></div><p><strong>InfiniBand vs RoCE: The Two Ways to Get RDMA</strong></p><p>RDMA is a protocol. You need hardware that supports it. Two options exist in practice.</p><p><strong>InfiniBand.</strong> A dedicated network technology designed from the start for RDMA. Purpose built switches, cables, and adapters. NVIDIA Quantum-2 InfiniBand delivers 400 Gbps per port. You run a completely separate network alongside your Ethernet for RDMA traffic.</p><p>Pros: best performance, lowest latency, deterministic behavior under load. Cons: dedicated hardware, expensive switches, separate cabling, separate management plane.</p><p><strong>RoCE (RDMA over Converged Ethernet).</strong> Runs RDMA over standard Ethernet hardware. Specifically RoCE v2, which runs over IP and can be routed. Requires Ethernet switches that support priority flow control (PFC) and explicit congestion notification (ECN).</p><p>Pros: uses existing Ethernet skills, single network fabric, easier to integrate with existing infrastructure. Cons: performance depends heavily on switch configuration, more sensitive to network congestion, harder to debug.</p><p>For greenfield GPU clusters at real scale (256+ GPUs), InfiniBand remains the default. For smaller clusters or shared general purpose infrastructure, RoCE v2 is the pragmatic choice.</p><p>Most cloud providers expose RDMA via RoCE. AWS EFA, Azure InfiniBand (rebranded RoCE in some SKUs), GCP compact placement with RoCE. You rarely touch native InfiniBand unless you run bare metal.</p><div><hr></div><p><strong>The Problem with Standard Kubernetes Pod Networking</strong></p><p>A standard Kubernetes pod gets one network interface, typically <code>eth0</code>, configured by the CNI plugin. The interface runs over kernel TCP. Bandwidth is capped by the CNI overlay and the kernel stack.</p><p>GPU workloads need three things the default setup does not provide.</p><p><strong>Multiple network interfaces.</strong> Pods need the standard <code>eth0</code> for Kubernetes traffic (API server, service discovery, application calls) plus additional high speed interfaces for RDMA traffic. The CNI only gives you one.</p><p><strong>Host network level access to the RDMA device.</strong> The NIC exposes RDMA resources (queue pairs, protection domains, memory regions) through a device file like <code>/dev/infiniband/uverbs0</code>. Pods need this device mounted and the right capabilities to use it.</p><p><strong>CPU pinning and NUMA awareness.</strong> RDMA performance depends on the memory being allocated on the same NUMA node as the NIC. GPU workloads already need NUMA affinity to the GPU. The GPU, the NIC, and the pinned memory all need to be on the same NUMA node for peak throughput.</p><p>Default Kubernetes ignores all of this. The CNI gives you one interface. Devices are not mounted. NUMA is invisible to the scheduler.</p><div><hr></div><p><strong>The Stack That Actually Works</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GnIY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GnIY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 424w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 848w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 1272w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GnIY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png" width="1456" height="1197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/abc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1197,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:346035,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194907931?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GnIY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 424w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 848w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 1272w, https://substackcdn.com/image/fetch/$s_!GnIY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabc86e77-7258-4e07-8482-e0a9658495d4_1674x1376.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Production RDMA on Kubernetes requires four components working together.</p><p><strong>Multus CNI.</strong> A meta plugin that lets pods have multiple network interfaces. Multus delegates the primary interface to your normal CNI (Calico, Cilium, whatever) and lets secondary interfaces use different plugins. This is how pods get <code>eth0</code> for Kubernetes traffic and <code>net1</code> through <code>net4</code> for RDMA.</p><pre><code><code>apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: rdma-net
spec:
  config: |
    {
      "cniVersion": "0.3.1",
      "type": "macvlan",
      "master": "ib0",
      "mode": "bridge",
      "ipam": {
        "type": "whereabouts",
        "range": "10.56.0.0/16"
      }
    }
</code></code></pre><p>Pods reference this definition via annotation.</p><pre><code><code>metadata:
  annotations:
    k8s.v1.cni.cncf.io/networks: rdma-net
</code></code></pre><p><strong>NVIDIA Network Operator.</strong> Automates the host setup. Installs the MOFED drivers (Mellanox OpenFabrics Enterprise Distribution), configures NICs, sets up SR-IOV if using virtualized NICs, and deploys the RDMA device plugin. Without this, every node requires manual driver installation.</p><p><strong>SR-IOV Device Plugin.</strong> Exposes virtual functions (VFs) of the RDMA NIC as Kubernetes extended resources. Pods request RDMA VFs the same way they request GPUs.</p><pre><code><code>resources:
  requests:
    nvidia.com/gpu: 8
    nvidia.com/hostdev: 4
    rdma/shared_ib: 1
</code></code></pre><p><strong>Topology Manager.</strong> Kubernetes feature (stable since 1.27) that coordinates scheduling decisions across CPU, memory, GPU, and device plugins. Enable it with <code>--topology-manager-policy=single-numa-node</code> to ensure the GPU, the NIC, and the pinned memory all land on the same NUMA node.</p><pre><code><code># kubelet configuration
topologyManagerPolicy: "single-numa-node"
cpuManagerPolicy: "static"
memoryManagerPolicy: "Static"
reservedMemory:
  - numaNode: 0
    limits:
      memory: 1100Mi
</code></code></pre><p>This is where most teams get stuck. The components exist. The integration between them is fragile. Single NUMA policy can cause pods to fail scheduling if the NUMA node is full. Relaxing to <code>best-effort</code> gives scheduling flexibility but loses guaranteed performance.</p><div><hr></div><p><strong>Validating the Setup</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!20Xr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!20Xr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 424w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 848w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 1272w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!20Xr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png" width="1456" height="1083" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1083,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:278457,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194907931?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!20Xr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 424w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 848w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 1272w, https://substackcdn.com/image/fetch/$s_!20Xr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18341a2a-c89e-4b62-90b4-9e181d6e4b4d_1678x1248.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Once the stack is deployed, a pod should see multiple interfaces and RDMA devices.</p><pre><code><code># Inside the pod
$ ip link show
1: lo: ...
2: eth0: ...                    # Kubernetes pod network
3: net1@if123: ...              # RDMA interface via Multus

$ ibv_devices
    device          &#9;   node GUID
    ------          &#9;----------------
    mlx5_0          &#9;ec0d9a0300xxxxxx

$ ibv_devinfo mlx5_0
hca_id:&#9;mlx5_0
&#9;transport:&#9;&#9;&#9;InfiniBand (0)
&#9;fw_ver:&#9;&#9;&#9;&#9;28.40.1000
&#9;link_layer:&#9;&#9;&#9;Ethernet     # or InfiniBand for native IB
</code></code></pre><p>Then benchmark. <code>perftest</code> is the standard tool.</p><pre><code><code># On one pod
$ ib_write_bw -d mlx5_0 --report_gbits

# On another pod
$ ib_write_bw -d mlx5_0 --report_gbits &lt;server_pod_ip&gt;
</code></code></pre><p>Healthy numbers: 180 to 200 Gbps on 200 Gbps fabrics. 380+ Gbps on 400 Gbps fabrics. If you see 10 to 25 Gbps, the traffic is falling back to TCP over the default network. The pods are not actually using RDMA.</p><div><hr></div><p><strong>NCCL Tests as the Real Benchmark</strong></p><p><code>perftest</code> proves RDMA works. NCCL tests prove GPU workloads actually use it.</p><pre><code><code># Inside a pod with 8 GPUs and RDMA NICs
$ mpirun -np 8 --allow-run-as-root \
  -mca btl_tcp_if_include eth0 \
  -x NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3 \
  -x NCCL_IB_GID_INDEX=3 \
  -x NCCL_DEBUG=INFO \
  all_reduce_perf -b 8 -e 8G -f 2 -g 1
</code></code></pre><p>The output shows bus bandwidth. On an 8x H100 node with 4x 400 Gbps NICs, you should see all reduce bandwidth around 350 to 400 GB/s for large messages. If you see 40 GB/s, NCCL is not using RDMA. Check the environment variables, check the NIC assignments, check that the RDMA interfaces are actually exposed to the pod.</p><div><hr></div><h2>When You Need This</h2><p>Not every GPU workload needs RDMA. Single node training and single node inference do not. The GPUs in a single box talk to each other over NVLink, which is already faster than any network.</p><p>RDMA matters when:</p><p><strong>Distributed training across nodes.</strong> Any model where the training job spans more than one physical machine. The all reduce after each step is the bottleneck. RDMA cuts it by 10 to 100x.</p><p><strong>Tensor parallel inference across nodes.</strong> Very large models (70B, 405B) that do not fit on a single node split their layers across multiple machines. Every forward pass requires cross node communication. RDMA is table stakes.</p><p><strong>Parameter server architectures.</strong> Less common now but still used in some recommendation model training.</p><p><strong>High bandwidth data loading.</strong> Feeding training jobs from a distributed object store at line rate. GPUDirect Storage with RDMA lets GPUs read training data from NVMe arrays without going through CPU memory.</p><p>If your GPU workloads fit entirely on single nodes, skip this complexity entirely. Kubernetes default networking is fine. Come back when you need to scale past one machine.</p><div><hr></div><h2>The Bottom Line</h2><p>RDMA is not optional for multi node GPU workloads. It is the difference between a cluster that computes and a cluster that waits on the network.</p><p>The stack is real but workable. Multus for multiple interfaces. NVIDIA Network Operator for host setup. SR-IOV Device Plugin for resource scheduling. Topology Manager for NUMA alignment. Production deployments use all four.</p><p>The hardest part is not any single component. It is the integration. A working RDMA cluster has 20 different things configured correctly in the right order. Miss one and traffic silently falls back to TCP. Your cluster computes. Nothing errors. The throughput is just 10x lower than it should be.</p><p>Validate with <code>perftest</code> for the fabric. Validate with NCCL tests for the GPU workload. Only trust the setup once both confirm the numbers you expected.</p><div><hr></div><p><em>This concludes the first 26 weeks of KubeNatives. The journey has covered control plane internals, GPU infrastructure, model serving, LLMOps patterns, and now the network fabric that holds it all together. Next quarter goes deeper into each pillar with advanced topics including Dynamic Resource Allocation, pod priority for ML workloads, LLM gateway patterns, and multi cluster GPU federation. Thank you for reading.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[A/B Testing LLM Models in Production with Kubernetes]]></title><description><![CDATA[Swapping Llama 3.1 70B for your old 8B model in one deploy is how you break production. Here is how to do it safely with traffic splitting and real quality metrics.]]></description><link>https://www.kubenatives.com/p/ab-testing-llm-models-kubernetes</link><guid isPermaLink="false">https://www.kubenatives.com/p/ab-testing-llm-models-kubernetes</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 17 Jul 2026 13:01:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3ut3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your team just finished evaluating a new model. Llama 3.3 70B outperforms your current Llama 3.1 8B on every internal benchmark. The engineering review is done. The cost analysis is done. Time to ship.</p><p>The wrong way: update the Deployment image, wait for the rollout, monitor Grafana for 10 minutes, call it a success.</p><p>The right way: split 5% of production traffic to the new model, compare quality metrics side by side for 48 hours, ramp traffic if quality holds, roll back instantly if it drops.</p><p>This article covers the 4 things you need to A/B test LLM models in production on Kubernetes. Traffic splitting. Quality metrics. Shadow traffic. Rollback automation.</p><div><hr></div><p><strong>Why LLM A/B Testing Is Different</strong></p><p>Traditional web service A/B testing compares latency, error rates, and conversion. All of those are server side metrics. You look at Grafana and know the answer in an hour.</p><p>LLM A/B testing compares response quality. Which model gave better answers? More helpful? More accurate? Less hallucinated? Those questions cannot be answered by server side metrics alone.</p><p>Three things are different about LLM A/B testing:</p><p><strong>Quality is subjective.</strong> Latency is a number. Response quality needs human evaluation, LLM as judge evaluation, or proxy metrics like thumbs up rate.</p><p><strong>The distribution matters.</strong> A new model might be better on average but catastrophically worse on 1% of prompts. You cannot see that in aggregate metrics.</p><p><strong>Cost is variable.</strong> Llama 70B costs more GPU time per request than 8B. You are not just testing quality. You are testing whether the quality improvement justifies the cost increase.</p><p>A good A/B testing setup measures all three at once.</p><div><hr></div><p><strong>Pattern 1: Traffic Splitting with KServe InferenceService</strong></p><p>KServe supports traffic splitting natively. You define one InferenceService with multiple revisions. Each revision points to a different model deployment. KServe routes incoming requests across them based on percentage.</p><pre><code><code>apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: llm-router
spec:
  predictor:
    canaryTrafficPercent: 5
    model:
      modelFormat:
        name: vllm
      storageUri: "pvc://models/llama-3.3-70b"
  # Previous revision (llama-3.1-8b) keeps the other 95%
</code></code></pre><p>The <code>canaryTrafficPercent: 5</code> field sends 5% of traffic to the new model. The other 95% still hits the previous revision. You can dial that number without redeploying.</p><p><strong>Ramp pattern:</strong> 5% &#8594; 25% &#8594; 50% &#8594; 100%. Hold each stage for at least 24 hours. Monitor quality metrics between each step. If any metric degrades, roll back by setting <code>canaryTrafficPercent: 0</code>.</p><p>If you are not on KServe, Istio VirtualService supports the same pattern with <code>weight</code> fields across two routes.</p><div><hr></div><p><strong>Pattern 2: Quality Metrics You Can Actually Measure</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3ut3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3ut3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 424w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 848w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 1272w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3ut3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png" width="1456" height="802" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:802,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:210988,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194880647?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3ut3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 424w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 848w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 1272w, https://substackcdn.com/image/fetch/$s_!3ut3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70606a15-9136-4b5c-9580-7709a0c96baf_1666x918.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;Better quality&#8221; is not a metric. You need something you can put in Prometheus and alert on. Four options, ranked by how quickly they return signal.</p><p><strong>Thumbs up rate.</strong> If your product has a feedback widget, thumbs up divided by total responses. Cheap. Fast. Lags by minutes. Best for consumer products with high traffic.</p><p><strong>Response acceptance rate.</strong> For code completion or suggestion systems, the percentage of suggestions the user actually accepts. Also cheap, also fast.</p><p><strong>LLM as judge.</strong> Run a second, stronger model (like Claude or GPT 4o) to grade responses from both A and B. Give it a rubric. Track win rate. Costs money per request but gives signal on quality dimensions users never report on.</p><p><strong>Human eval.</strong> Most expensive, slowest, highest quality. Sample 50 to 100 responses per model per day and have a human grader score them. Reserve for the final ramp decision before going to 100%.</p><p>A good setup uses at least two of these. Thumbs up rate for real time signal. LLM as judge for deeper evaluation. Human eval as the final gate.</p><div><hr></div><p><strong>Pattern 3: Shadow Traffic for Risk Free Testing</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6bzd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6bzd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 424w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 848w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 1272w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6bzd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png" width="1456" height="991" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:991,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:230680,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194880647?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6bzd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 424w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 848w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 1272w, https://substackcdn.com/image/fetch/$s_!6bzd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fded545fe-1c8f-4bd5-b043-04f308663371_1678x1142.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Sometimes you want to test a new model without risking user experience at all. Shadow traffic mirrors real requests to the new model, compares the responses, and throws the new response away. Users never see it.</p><pre><code><code>apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: llm-shadow
spec:
  hosts:
  - llm.inference.svc.cluster.local
  http:
  - route:
    - destination:
        host: llama-3-1-8b
        port:
          number: 8000
      weight: 100
    mirror:
      host: llama-3-3-70b
      port:
        number: 8000
    mirrorPercentage:
      value: 10.0
</code></code></pre><p>Istio <code>mirror</code> with <code>mirrorPercentage: 10.0</code> sends a copy of 10% of requests to the new model. The original response from the production model is what the user sees. The shadow response is logged for comparison.</p><p>This is the safest way to run any new model. Zero user risk. You gather weeks of comparison data before you send any real traffic.</p><p><strong>The catch:</strong> shadow traffic doubles GPU cost on the shadowed percentage. If you shadow 100% of traffic, you pay for 2x the compute. Shadow 5 to 10% and it is a reasonable investment for the risk reduction.</p><div><hr></div><p><strong>Pattern 4: Automated Rollback on Quality Regression</strong></p><p>Manual rollback at 3 AM is how you end up with a worse model in production for 6 hours before someone notices. Automate it.</p><p>The idea: alert on quality regression, automatically drop <code>canaryTrafficPercent</code> to 0 when the alert fires.</p><pre><code><code># Prometheus alert
- alert: CanaryQualityRegression
  expr: |
    (
      rate(llm_thumbs_up_total{revision="canary"}[10m]) /
      rate(llm_responses_total{revision="canary"}[10m])
    )
    &lt;
    (
      rate(llm_thumbs_up_total{revision="stable"}[10m]) /
      rate(llm_responses_total{revision="stable"}[10m])
    ) * 0.9
  for: 15m
  annotations:
    summary: "Canary model thumbs up rate is 10%+ below stable"
</code></code></pre><p>Pair the alert with a Kubernetes controller that watches Alertmanager and patches the InferenceService when the alert fires. Argo Rollouts has this built in. You define the metric, the threshold, and the rollback action. It does the rest.</p><p><strong>The threshold matters.</strong> Too tight and every random variation triggers rollback. Too loose and real regressions slip through. Start at 10% below stable for consumer products. 5% for anything customer facing or paid.</p><div><hr></div><h2>Putting It Together: A Production Ramp Plan</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lXEq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lXEq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 424w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 848w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 1272w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lXEq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:785,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:186229,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194880647?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lXEq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 424w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 848w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 1272w, https://substackcdn.com/image/fetch/$s_!lXEq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F631e8102-ec55-4eea-bd2d-e279f7172ca3_1700x916.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what a safe model upgrade looks like in practice. Llama 3.1 8B currently serving 100%. You want to move to Llama 3.3 70B.</p><p><strong>Week 1. Shadow.</strong> Deploy Llama 3.3 70B alongside 8B. Mirror 10% of traffic. No user impact. Collect LLM as judge scores comparing responses. Look for win rate above 60% on your prompts.</p><p><strong>Week 2. Canary 5%.</strong> Shadow data looked good. Move 5% of traffic to the new model. Watch thumbs up rate for both. Watch p99 latency (70B is slower than 8B). Watch cost per request.</p><p><strong>Week 3. Ramp.</strong> Move to 25%, then 50%. Hold each step 24 to 48 hours. Quality metrics must stay within 5% of the stable revision. Automated rollback on regression.</p><p><strong>Week 4. Full.</strong> 100% to new model. Keep the old revision around for one more week as a one click rollback option. Then retire it.</p><p>Total time: 4 weeks. Annoying? Yes. But this is how you ship a model change without breaking production.</p><div><hr></div><h2>What to Not Test A/B</h2><p>Not everything needs A/B testing. Three cases where you should just deploy:</p><p><strong>Bug fixes.</strong> If the current model is producing broken output and the new one fixes it, just deploy. A/B testing a fix prolongs the bug.</p><p><strong>Security patches.</strong> Same logic. The current version has a known issue. Ship the patched version.</p><p><strong>Identical model with config changes.</strong> Tuning <code>max_tokens</code>, adjusting the system prompt, changing batch size. A/B testing is overkill for changes that are mathematically unlikely to affect response distribution.</p><p>Reserve A/B testing for actual model swaps and major prompt rewrites. Everything else is just cautious engineering.</p><div><hr></div><h2>The Bottom Line</h2><p>LLM A/B testing on Kubernetes is not hard. It is the same traffic splitting patterns you use for microservices, with quality metrics swapped in for error rate.</p><p>Four patterns. Traffic splitting via KServe or Istio. Quality metrics you can actually measure (thumbs up, LLM as judge, human eval). Shadow traffic for the highest risk changes. Automated rollback on regression.</p><p>Skip these and every model upgrade is a coin flip. Use them and you ship better models with confidence.</p><p>The hardest part is not the infrastructure. It is getting product and engineering to agree on what &#8220;better&#8221; means before you start the test.</p><div><hr></div><p><em>Next week: Triton Inference Server on Kubernetes: Multi-Model Serving.</em></p><p><em>If you are running production Kubernetes clusters, I cover control plane internals, GPU infrastructure, and model serving every week. Subscribe at kubenatives.com.</em></p><div><hr></div><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[API Server Latency: What Is Normal and What Is a Red Flag]]></title><description><![CDATA[Your API server p99 is 400ms. Is that fine or is the cluster about to fall over? Here are the numbers that matter and what to do when they go wrong.]]></description><link>https://www.kubenatives.com/p/kubernetes-api-server-latency-thresholds</link><guid isPermaLink="false">https://www.kubenatives.com/p/kubernetes-api-server-latency-thresholds</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 10 Jul 2026 13:01:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oi6n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every Kubernetes operation goes through the API server. kubectl commands, controller reconciliation loops, kubelet heartbeats, webhook calls, custom operators. Thousands of requests per minute on a busy cluster.</p><p>When the API server slows down, everything slows down. Deployments stall. Pods stay in Pending longer. Controllers fall behind their desired state. The cluster feels sluggish even though CPU and memory on the nodes look fine.</p><p>The hard part is knowing when slow is slow. A 200ms p99 on a list operation is great. A 200ms p99 on a GET pod call is a fire.</p><p>This article covers the latency numbers that matter, the thresholds that signal trouble, and the 4 most common causes of API server latency spikes.</p><div><hr></div><p><strong>The Metric That Matters: apiserver_request_duration_seconds</strong></p><p>The API server exposes request latency as a Prometheus histogram broken down by verb, resource, and scope. The raw metric name is <code>apiserver_request_duration_seconds</code>.</p><p>You never look at the average. You look at p99 by verb and resource.</p><p>A single cluster can have hundreds of different verb plus resource combinations. The ones that matter in practice are small in number. Focus on these.</p><pre><code><code># GET requests on individual objects
histogram_quantile(0.99,
  sum by (le, resource) (
    rate(apiserver_request_duration_seconds_bucket{verb="GET"}[5m])
  )
)

# LIST requests (the ones most likely to be slow)
histogram_quantile(0.99,
  sum by (le, resource) (
    rate(apiserver_request_duration_seconds_bucket{verb="LIST"}[5m])
  )
)

# WATCH requests (should be near zero latency after setup)
histogram_quantile(0.99,
  sum by (le, resource) (
    rate(apiserver_request_duration_seconds_bucket{verb="WATCH"}[5m])
  )
)
</code></code></pre><p>Three verbs. Three different expectations. Treating them the same is why most teams get API server alerting wrong.</p><div><hr></div><p><strong>What Normal Looks Like</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oi6n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oi6n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 424w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 848w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 1272w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oi6n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:190513,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194808266?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oi6n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 424w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 848w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 1272w, https://substackcdn.com/image/fetch/$s_!oi6n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58a85b6e-33c4-406c-987c-6ae7ef22b9d3_1682x916.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The thresholds below are production numbers from healthy clusters in the 100 to 500 node range. Smaller clusters run faster. Larger clusters with heavy CRD usage run slower. Use these as a starting point and adjust for your baseline.</p><p><strong>GET on individual objects:</strong> p99 under 50ms is healthy. Under 100ms is acceptable. Over 200ms means something is wrong.</p><p><strong>LIST on small collections (under 1000 objects):</strong> p99 under 200ms is healthy. Under 500ms is acceptable. Over 1s is a problem.</p><p><strong>LIST on large collections (pods across all namespaces, all events):</strong> p99 under 1s is healthy. Under 3s is acceptable. Over 5s means etcd or pagination is broken.</p><p><strong>CREATE and UPDATE:</strong> p99 under 100ms is healthy. Under 250ms is acceptable. Over 500ms usually means etcd write latency, not API server CPU.</p><p><strong>WATCH:</strong> the duration metric is misleading for watches. They are long lived. Look at <code>apiserver_current_inflight_requests</code> and <code>etcd_server_has_leader</code> instead.</p><p>Memorize the shape of these numbers. When you hit an incident at 3 AM, you need to know if 400ms on LIST pods is normal or is the alarm.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZUnK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZUnK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 424w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 848w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 1272w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZUnK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png" width="1456" height="1018" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1018,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:321760,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194808266?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZUnK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 424w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 848w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 1272w, https://substackcdn.com/image/fetch/$s_!ZUnK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a6a0654-4b9a-4ba0-836b-a568e8ecf7ea_1674x1170.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Cause 1: etcd Write Latency</strong></p><p>The API server is stateless. Every write it accepts must be committed to etcd before the API server returns success. If etcd is slow, the API server is slow. No amount of API server scaling fixes this.</p><p>The metric to check: <code>etcd_disk_wal_fsync_duration_seconds</code>.</p><p>Healthy etcd: p99 under 10ms. Warning: p99 between 10ms and 50ms. Broken: p99 over 50ms.</p><p>When fsync hits 100ms, every write operation on the API server gets queued behind a slow disk. p99 API latency explodes.</p><p><strong>The fix:</strong> dedicated NVMe storage for etcd. Not network attached disks. Not shared storage. Local NVMe on the etcd nodes. This is the single biggest performance improvement available.</p><p>If you are on a cloud provider, use the fastest local instance storage option. On AWS that is i4i or im4gn instances. On GCP that is local SSD. On Azure that is Lsv3 series.</p><div><hr></div><p><strong>Cause 2: List Operations Against Huge Collections</strong></p><p>A controller that lists all pods across all namespaces every 30 seconds is a common cause of API server pressure. Each list pulls the full object from etcd through the API server, serializes it, and ships it to the controller.</p><p>On a 500 pod cluster, this is fine. On a 50,000 pod cluster, every list takes several seconds and consumes significant API server CPU.</p><p>The metrics to watch:</p><p><code>apiserver_request_total</code> broken down by verb, resource, and subresource. Look for verb=&#8221;LIST&#8221; counts that dominate the total.</p><p><code>apiserver_response_sizes_bucket</code> shows the size of responses. If LIST pods is returning 50MB responses on every call, you have a problem.</p><p><strong>The fix:</strong> use label selectors, field selectors, and watch with resource version instead of periodic lists. Every Kubernetes controller written in the last 5 years uses informers that watch and cache rather than list. Audit your custom operators and any home grown controllers. The old pattern of periodic list is what kills clusters.</p><div><hr></div><p><strong>Cause 3: Admission Webhooks</strong></p><p>Every create and update request passes through admission webhooks. Policy controllers, sidecar injectors, mutating webhooks for secrets. A slow webhook adds its latency to every matching request.</p><p>A 500ms webhook with <code>failurePolicy: Fail</code> is not just slow. It can deadlock cluster operations. If the webhook pod is down, no pods can be created. Including the webhook pod itself.</p><p>The metric: <code>apiserver_admission_webhook_request_total</code> and <code>apiserver_admission_webhook_rejection_count</code>.</p><p>The better metric for latency: <code>apiserver_admission_webhook_admission_duration_seconds</code>.</p><p>Healthy webhooks run under 100ms p99. Anything over 1s is a production risk.</p><p><strong>The fix:</strong> set <code>timeoutSeconds: 5</code> on every webhook at most. Use <code>failurePolicy: Ignore</code> for non critical webhooks. Scope webhooks with <code>objectSelector</code> and <code>namespaceSelector</code> so they only fire on the objects that actually need them. Default webhook scope is everything, which means every single create passes through.</p><div><hr></div><p><strong>Cause 4: Too Many Watches</strong></p><p>The API server holds open connections for every watch client. Controllers, kubelet, custom operators, kubectl commands with watch mode. A single kubelet holds multiple watches.</p><p>On large clusters, watch count can reach tens of thousands of concurrent connections. Each one consumes memory and a file descriptor.</p><p>The metric: <code>apiserver_registered_watchers</code> broken down by resource.</p><p>When watch count spikes, the API server runs out of file descriptors. Every new connection fails. Latency explodes on everything.</p><p><strong>The fix:</strong> set ulimits on the API server to at least 65535. Use watch with resource version and compaction to reduce memory pressure. Audit custom operators for watches that are never closed. Watch leaks are a real problem in handwritten controllers.</p><div><hr></div><h2>The Alert Set That Catches Real Problems</h2><p>Most teams alert on p99 latency thresholds and nothing else. That catches slow symptoms, not slow causes.</p><p>A good alert set has 4 rules:</p><pre><code><code># 1. API server latency itself
- alert: APIServerHighLatency
  expr: histogram_quantile(0.99,
    sum by (le, verb) (
      rate(apiserver_request_duration_seconds_bucket{verb!="WATCH"}[5m])
    )
  ) &gt; 1
  for: 5m

# 2. etcd disk latency (leading indicator)
- alert: EtcdSlowDisk
  expr: histogram_quantile(0.99,
    rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])
  ) &gt; 0.05
  for: 5m

# 3. Webhook latency
- alert: SlowAdmissionWebhook
  expr: histogram_quantile(0.99,
    sum by (le, name) (
      rate(apiserver_admission_webhook_admission_duration_seconds_bucket[5m])
    )
  ) &gt; 1
  for: 5m

# 4. Watch count spike
- alert: TooManyWatchers
  expr: sum(apiserver_registered_watchers) &gt; 50000
  for: 15m
</code></code></pre><p>Rule 1 tells you there is a problem. Rules 2, 3, and 4 tell you what the problem is. Without the leading indicators, you wake up at 3 AM to a slow cluster and no idea where to look.</p><div><hr></div><h2>The Debugging Flow</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s8gF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s8gF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 424w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 848w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s8gF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png" width="1456" height="1111" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22629631-828c-4040-a55e-296173b66d85_1652x1260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1111,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:270579,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194808266?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s8gF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 424w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 848w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!s8gF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22629631-828c-4040-a55e-296173b66d85_1652x1260.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When the API server gets slow in production, work through the causes in order of likelihood.</p><p><strong>Step 1:</strong> check etcd. <code>etcd_disk_wal_fsync_duration_seconds</code> p99. If it is high, the problem is disk. Fix the disk or move etcd to external NVMe nodes.</p><p><strong>Step 2:</strong> check webhooks. <code>apiserver_admission_webhook_admission_duration_seconds</code> p99 by webhook name. A slow webhook affects every matching operation. Disable or fix the slow one.</p><p><strong>Step 3:</strong> check watch count. <code>apiserver_registered_watchers</code> by resource. A spike means a controller is leaking watches.</p><p><strong>Step 4:</strong> check list patterns. <code>apiserver_request_total</code> by verb. If LIST is dominating, find the controller doing it and fix it to use informers.</p><p>80% of API server slowness resolves at step 1 or step 2. Do not start by scaling the API server. Scaling hides the symptom and makes the bill worse.</p><div><hr></div><h2>The Bottom Line</h2><p>API server latency is the health signal for the entire control plane. Everything flows through it, so it sees every problem first.</p><p>Know the normal numbers. p99 under 50ms on GET, under 200ms on LIST for small collections, under 1s on LIST for large ones. Alert on etcd disk latency, webhook latency, and watch count. Investigate those before you investigate the API server itself.</p><p>And if you take one thing away: etcd on NVMe. The single highest leverage change you can make for API server performance on a busy cluster.</p><div><hr></div><p><em>Next week: A/B Testing LLM Models in Production with Kubernetes.</em></p><p><em>If you are running production Kubernetes clusters, I cover control plane internals, GPU infrastructure, and model serving every week. Subscribe at kubenatives.com.</em></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[GPU Monitoring with DCGM Exporter: The Metrics That Matter]]></title><description><![CDATA[Your GPU nodes are running. nvidia-smi shows green. But are your GPUs healthy, efficient, and about to fail? DCGM tells you.]]></description><link>https://www.kubenatives.com/p/gpu-monitoring-dcgm-exporter-kubernetes</link><guid isPermaLink="false">https://www.kubenatives.com/p/gpu-monitoring-dcgm-exporter-kubernetes</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 03 Jul 2026 13:02:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lc69!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>nvidia-smi is the first tool engineers reach for when checking GPU status. It shows utilization, temperature, memory, and power draw. It works for a single node.</p><p>It does not work for a 20 node GPU cluster. You cannot SSH into 20 nodes every time someone reports slow inference. You need GPU metrics in Prometheus, dashboards in Grafana, and alerts that page you before users notice.</p><p>DCGM Exporter is the NVIDIA Data Center GPU Manager running as a DaemonSet on every GPU node. It collects GPU health and performance metrics and exports them in Prometheus format. This article covers which metrics matter, how to set up the monitoring stack, and what to alert on.</p><div><hr></div><p><strong>What DCGM Exporter Collects</strong></p><p>DCGM Exporter runs as part of the GPU Operator (component 6 of 8). It scrapes GPU metrics at a configurable interval and exposes them on a /metrics endpoint.</p><p>The metrics fall into 4 categories:</p><p><strong>Utilization metrics</strong> tell you how busy the GPU is.</p><pre><code><code>DCGM_FI_DEV_GPU_UTIL          GPU compute utilization (%)
DCGM_FI_DEV_MEM_COPY_UTIL     Memory controller utilization (%)
DCGM_FI_DEV_ENC_UTIL          Encoder utilization (%)
DCGM_FI_DEV_DEC_UTIL          Decoder utilization (%)
</code></code></pre><p><strong>Memory metrics</strong> tell you how much VRAM is in use.</p><pre><code><code>DCGM_FI_DEV_FB_USED           Framebuffer (VRAM) used (MB)
DCGM_FI_DEV_FB_FREE           Framebuffer free (MB)
DCGM_FI_DEV_FB_TOTAL          Framebuffer total (MB)
</code></code></pre><p><strong>Temperature and power metrics</strong> tell you about thermal and electrical health.</p><pre><code><code>DCGM_FI_DEV_GPU_TEMP          GPU temperature (&#176;C)
DCGM_FI_DEV_MEMORY_TEMP       Memory temperature (&#176;C)
DCGM_FI_DEV_POWER_USAGE       Current power draw (W)
DCGM_FI_DEV_TOTAL_ENERGY      Total energy consumed (mJ)
</code></code></pre><p><strong>Error metrics</strong> tell you about hardware problems before they cause failures.</p><pre><code><code>DCGM_FI_DEV_ECC_SBE_VOL       Single-bit ECC errors (correctable)
DCGM_FI_DEV_ECC_DBE_VOL       Double-bit ECC errors (uncorrectable)
DCGM_FI_DEV_RETIRED_SBE       Retired pages (single-bit)
DCGM_FI_DEV_RETIRED_DBE       Retired pages (double-bit)
DCGM_FI_DEV_XID_ERRORS        XID error codes
</code></code></pre><div><hr></div><p><strong>The 6 Metrics That Actually Matter</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lc69!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lc69!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 424w, https://substackcdn.com/image/fetch/$s_!lc69!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 848w, https://substackcdn.com/image/fetch/$s_!lc69!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 1272w, https://substackcdn.com/image/fetch/$s_!lc69!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lc69!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png" width="1456" height="1002" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1002,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:248570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193356940?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lc69!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 424w, https://substackcdn.com/image/fetch/$s_!lc69!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 848w, https://substackcdn.com/image/fetch/$s_!lc69!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 1272w, https://substackcdn.com/image/fetch/$s_!lc69!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe484edbe-868d-4ec2-bd7c-5bfe4ebaae42_1682x1158.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most teams monitor GPU utilization and nothing else. That is like monitoring CPU utilization and ignoring memory, disk, and network. Here are the 6 metrics that predict real problems.</p><p><strong>Metric 1: GPU Temperature (DCGM_FI_DEV_GPU_TEMP)</strong></p><p>GPUs thermal throttle at 83&#176;C (A100) or 80&#176;C (H100). When throttled, the GPU reduces clock speed to prevent damage. Performance drops 20 to 40% with no warning in application logs.</p><p>Alert threshold: warning at 78&#176;C, critical at 82&#176;C.</p><p><strong>Metric 2: ECC Errors (DCGM_FI_DEV_ECC_DBE_VOL)</strong></p><p>Double-bit ECC errors are uncorrectable memory errors. They indicate failing GPU memory. A single double-bit error means the GPU is producing incorrect computation results. Your model outputs may be wrong.</p><p>Alert threshold: any double-bit error is critical. Drain the node immediately.</p><p>Single-bit errors are correctable and normal in small quantities. Alert if the rate exceeds 10 per hour.</p><p><strong>Metric 3: GPU Memory Used (DCGM_FI_DEV_FB_USED)</strong></p><p>Track the ratio of used to total framebuffer. If memory usage is consistently above 95%, one spike will cause a CUDA out of memory crash.</p><p>Alert threshold: warning at 90%, critical at 95%.</p><p><strong>Metric 4: GPU Utilization (DCGM_FI_DEV_GPU_UTIL)</strong></p><p>For vLLM workloads, GPU utilization is normally high (80 to 95%). A sudden drop to 0% indicates the serving process crashed. A sustained drop to 30 to 40% indicates the model is not receiving requests (traffic routing issue).</p><p>Alert on sudden changes, not absolute values.</p><p><strong>Metric 5: Power Usage (DCGM_FI_DEV_POWER_USAGE)</strong></p><p>A100 TDP is 400W. H100 TDP is 700W. If power draw is consistently at TDP, the GPU is running at maximum capacity. If it drops to near zero while workloads are running, the GPU may have entered a fault state.</p><p>Useful for capacity planning and cost attribution.</p><p><strong>Metric 6: XID Errors (DCGM_FI_DEV_XID_ERRORS)</strong></p><p>XID errors are NVIDIA driver error codes. Each code indicates a specific type of failure.</p><pre><code><code>XID 13:  Graphics Engine Exception (GPU hang)
XID 31:  GPU memory page fault
XID 43:  GPU stopped processing
XID 48:  Double-bit ECC error
XID 63:  ECC page retirement (row remapping)
XID 79:  GPU fallen off the bus (hardware failure)
</code></code></pre><p>XID 79 is the worst. It means the GPU is no longer communicating with the system. The node needs to be drained and the GPU replaced.</p><p>Alert threshold: any XID error is at minimum a warning. XID 48, 63, 79 are critical.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qNHR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qNHR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 424w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 848w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qNHR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png" width="1456" height="944" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:944,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:231588,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193356940?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qNHR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 424w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 848w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!qNHR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab997c9a-ec9d-431c-9848-8ba412d2dfed_1682x1090.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p><strong>Setting Up the Monitoring Stack</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TWa6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TWa6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 424w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 848w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 1272w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TWa6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:234413,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193356940?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TWa6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 424w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 848w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 1272w, https://substackcdn.com/image/fetch/$s_!TWa6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde5b4fe8-36a3-471e-b2ae-39fdd783a2ac_1666x930.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you installed the GPU Operator, DCGM Exporter is already running. Verify:</p><pre><code><code>kubectl get pods -n gpu-operator -l app=nvidia-dcgm-exporter
</code></code></pre><p>All pods should be Running with one pod per GPU node.</p><p><strong>ServiceMonitor for Prometheus:</strong></p><pre><code><code>apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: dcgm-exporter
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics
</code></code></pre><p><strong>Verify metrics are being scraped:</strong></p><pre><code><code># Check Prometheus targets
kubectl port-forward -n monitoring svc/prometheus 9090:9090

# In the Prometheus UI, check Status &#8594; Targets
# Look for dcgm-exporter targets. All should show UP.
</code></code></pre><div><hr></div><p><strong>Prometheus Alert Rules</strong></p><pre><code><code>groups:
- name: gpu-health
  rules:
  - alert: GpuTemperatureHigh
    expr: DCGM_FI_DEV_GPU_TEMP &gt; 78
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "GPU temperature above 78&#176;C on {{ $labels.instance }}"
      description: "GPU {{ $labels.gpu }} is at {{ $value }}&#176;C. Thermal throttling starts at 83&#176;C."

  - alert: GpuTemperatureCritical
    expr: DCGM_FI_DEV_GPU_TEMP &gt; 82
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "GPU temperature critical on {{ $labels.instance }}"

  - alert: GpuDoubleBitEcc
    expr: DCGM_FI_DEV_ECC_DBE_VOL &gt; 0
    for: 0m
    labels:
      severity: critical
    annotations:
      summary: "Uncorrectable ECC error on GPU {{ $labels.gpu }}"
      description: "Double-bit ECC error detected. GPU memory is producing incorrect results. Drain the node."

  - alert: GpuMemoryNearFull
    expr: (DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL) &gt; 0.95
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "GPU memory above 95% on {{ $labels.gpu }}"

  - alert: GpuUtilizationDrop
    expr: DCGM_FI_DEV_GPU_UTIL &lt; 10 and DCGM_FI_DEV_GPU_UTIL offset 10m &gt; 50
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "GPU utilization dropped suddenly on {{ $labels.gpu }}"
      description: "GPU util was above 50% and is now below 10%. Serving process may have crashed."

  - alert: GpuXidError
    expr: DCGM_FI_DEV_XID_ERRORS &gt; 0
    for: 0m
    labels:
      severity: warning
    annotations:
      summary: "XID error {{ $value }} on GPU {{ $labels.gpu }}"

  - alert: GpuHighSingleBitEcc
    expr: rate(DCGM_FI_DEV_ECC_SBE_VOL[1h]) &gt; 10
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Elevated single-bit ECC error rate on {{ $labels.gpu }}"
</code></code></pre><div><hr></div><p><strong>Grafana Dashboard: Essential Panels</strong></p><p>Build a dashboard with these panels. Group by node and GPU index.</p><p><strong>Row 1: Overview</strong></p><ul><li><p>GPU utilization per GPU (time series, 0 to 100%)</p></li><li><p>GPU memory used vs total per GPU (bar gauge)</p></li><li><p>GPU temperature per GPU (time series with 78&#176;C and 82&#176;C threshold lines)</p></li></ul><p><strong>Row 2: Health</strong></p><ul><li><p>ECC errors (single-bit and double-bit) per GPU (stat panel, instant value)</p></li><li><p>XID errors per GPU (table, last 24 hours)</p></li><li><p>Power draw per GPU (time series, with TDP line)</p></li></ul><p><strong>Row 3: Cluster Overview</strong></p><ul><li><p>Total GPUs allocated vs available (stat panel)</p></li><li><p>Average GPU utilization across all GPUs (gauge)</p></li><li><p>Nodes with GPU temperature above 78&#176;C (table, filtered)</p></li></ul><div><hr></div><p><strong>Common Issues DCGM Catches Early</strong></p><p><strong>Cooling failure.</strong> Temperature creeps up over days. DCGM shows the trend. You schedule maintenance before the GPU throttles.</p><p><strong>Failing GPU memory.</strong> Single-bit ECC errors increase gradually. Then a double-bit error occurs. DCGM catches the trend. You drain the node before the GPU produces incorrect model outputs.</p><p><strong>GPU fallen off the bus.</strong> XID 79. The GPU stops responding. DCGM reports the error. Without DCGM, you find out when users report that inference is not working and someone SSHs into the node.</p><p><strong>Silent performance degradation.</strong> GPU utilization drops from 85% to 40% but no application errors. The serving process is healthy but something changed (model was reloaded, batch size decreased, traffic routing shifted). DCGM&#8217;s utilization trend catches the change.</p><div><hr></div><p><strong>The Bottom Line</strong></p><p>nvidia-smi is for debugging one node. DCGM Exporter is for monitoring a cluster. If you run GPU workloads in production, DCGM metrics in Prometheus with alert rules are not optional.</p><p>Monitor 6 metrics: temperature, ECC errors, memory usage, utilization, power, and XID errors. Alert on the thresholds above. Build a Grafana dashboard for visibility.</p><p>The GPU that fails silently costs you more than the GPU that fails loudly. DCGM makes silent failures visible.</p><div><hr></div><p><em>Next week: Service Mesh Debugging on Kubernetes: When Istio Breaks Your Inference Pipeline.</em></p><p><em>If you are running GPU clusters in production, I cover monitoring, model serving, and infrastructure operations every week. Subscribe at kubenatives.com.</em></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Resource Requests and Limits for GPU Workloads]]></title><description><![CDATA[Get requests wrong and your pods are Pending. Get limits wrong and they OOM. Here is how to size them correctly for GPU inference.]]></description><link>https://www.kubenatives.com/p/resource-requests-limits-gpu-kubernetes</link><guid isPermaLink="false">https://www.kubenatives.com/p/resource-requests-limits-gpu-kubernetes</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 26 Jun 2026 13:01:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bzE5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every Kubernetes pod spec has two resource fields: requests and limits. For CPU and memory, the difference is well understood. For GPU workloads, most teams get them wrong.</p><p>The result: pods stuck in Pending because requests are too high. Or pods crashing with OOMKilled because memory limits are too low. Or GPU nodes running at 30% utilization because every pod over-requests resources.</p><p>This article covers how to set requests and limits correctly for inference workloads, training jobs, and mixed GPU environments.</p><div><hr></div><h3><strong>Requests vs Limits: The Basics</strong></h3><p>Requests are what the scheduler uses to find a node. The scheduler looks at every node and asks: &#8220;Does this node have enough unrequested resources to fit this pod?&#8221; If no node has enough, the pod stays Pending.</p><p>Limits are the ceiling. If the pod tries to use more than the limit, Kubernetes enforces it. For memory, the pod is OOMKilled. For CPU, the pod is throttled.</p><pre><code><code>resources:
  requests:
    cpu: "4"
    memory: 32Gi
    nvidia.com/gpu: "1"
  limits:
    memory: 48Gi
    nvidia.com/gpu: "1"
</code></code></pre><p><strong>Critical difference for GPUs:</strong> GPU requests and limits must be equal. You cannot request 0.5 GPUs. You cannot set a limit of 2 GPUs with a request of 1. The NVIDIA device plugin requires requests = limits for nvidia.com/gpu. Always.</p><div><hr></div><h3><strong>Why CPU Limits Are Dangerous for Inference</strong></h3><p>Do not set CPU limits on vLLM or Triton pods. This is counterintuitive but important.</p><p>CPU limits cause throttling. When a pod hits its CPU limit, Kubernetes throttles it. The pod is not killed. It is slowed down. For inference workloads, this means slower tokenization, slower request handling, and higher TTFT.</p><p>vLLM uses CPU for tokenization, request scheduling, and output buffering. These are bursty workloads. During model loading, CPU usage spikes. During inference, it drops. A static CPU limit penalizes the spikes.</p><pre><code><code># WRONG: CPU limit causes throttling during tokenization spikes
resources:
  requests:
    cpu: "4"
    memory: 32Gi
    nvidia.com/gpu: "1"
  limits:
    cpu: "8"        # Remove this
    memory: 48Gi
    nvidia.com/gpu: "1"

# RIGHT: CPU request only, no limit
resources:
  requests:
    cpu: "4"
    memory: 32Gi
    nvidia.com/gpu: "1"
  limits:
    memory: 48Gi
    nvidia.com/gpu: "1"
    # No CPU limit
</code></code></pre><p>Set CPU requests (for scheduling) but leave CPU limits unset. The pod can burst above its request when the node has spare CPU. No throttling. No latency spikes.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/resource-requests-limits-gpu-kubernetes?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Kubenatives! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/resource-requests-limits-gpu-kubernetes?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/resource-requests-limits-gpu-kubernetes?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p><div><hr></div><h3><strong>Memory Sizing for Inference Pods</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pMAV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pMAV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 424w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 848w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 1272w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pMAV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png" width="849" height="514" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:514,&quot;width&quot;:849,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:84963,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193257270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pMAV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 424w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 848w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 1272w, https://substackcdn.com/image/fetch/$s_!pMAV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aafb46-b1f0-4533-90b5-5c2622d74a72_849x514.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Memory limits are essential. Without them, a memory leak crashes the node instead of just the pod.</p><p>The memory a vLLM pod needs:</p><pre><code><code>Model loading buffer:     ~1x model size (temporary, during download/load)
Tokenizer:                200MB to 2GB (depends on vocab size)
Request handling:          ~100MB per concurrent request
vLLM engine overhead:     1-4GB
Python runtime:           500MB to 1GB
</code></code></pre><p><strong>Sizing guide:</strong></p><pre><code><code>Model Size     Memory Request     Memory Limit     Headroom
8B FP16        16Gi               24Gi             50%
13B FP16       24Gi               36Gi             50%
70B FP16       48Gi               64Gi             33%
70B INT4       24Gi               36Gi             50%
</code></code></pre><p>The request is what the pod typically uses. The limit provides headroom for spikes during model loading, large batch requests, and garbage collection.</p><p>Set the limit at 30 to 50% above the request. This gives the pod room to breathe without letting a runaway process consume the entire node.</p><div><hr></div><h3><strong>GPU Resource Rules</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bzE5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bzE5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 424w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 848w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 1272w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bzE5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png" width="835" height="569" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:569,&quot;width&quot;:835,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105110,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193257270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bzE5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 424w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 848w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 1272w, https://substackcdn.com/image/fetch/$s_!bzE5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68f76eda-81db-42c8-a497-45fd6a79a745_835x569.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>GPUs in Kubernetes have unique constraints that CPU and memory do not:</p><p><strong>No fractional GPUs.</strong> You cannot request 0.5 GPUs. The device plugin treats GPUs as integers. 0 or 1. If you need fractional GPUs, use MIG, Time-Slicing, or MPS.</p><p><strong>Requests must equal limits.</strong> The NVIDIA device plugin requires nvidia.com/gpu requests and limits to be identical. Setting requests: 1 and limits: 2 causes a validation error.</p><p><strong>No overcommit.</strong> Unlike CPU (where you can request 1 core and burst to 4), GPUs are exclusively allocated. If you request 1 GPU, that entire physical GPU is reserved for your pod. No other pod can use it, even if your pod only uses 10% of the GPU.</p><p><strong>No resource quotas on GPU memory.</strong> Kubernetes has no concept of GPU memory. You cannot request &#8220;20GB of GPU VRAM.&#8221; You request 1 GPU and get the entire device (40GB, 80GB, whatever the hardware has). GPU memory management is the serving framework&#8217;s job (vLLM&#8217;s gpu-memory-utilization flag).</p><div><hr></div><h3><strong>Common Patterns</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mzfl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mzfl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 424w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 848w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 1272w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mzfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png" width="845" height="776" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:776,&quot;width&quot;:845,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131023,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193257270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Mzfl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 424w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 848w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 1272w, https://substackcdn.com/image/fetch/$s_!Mzfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F614da52a-e55f-4f6d-bf17-152b77ea86c9_845x776.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Pattern 1: Single GPU inference pod</strong></p><pre><code><code>resources:
  requests:
    cpu: "4"
    memory: 24Gi
    nvidia.com/gpu: "1"
  limits:
    memory: 36Gi
    nvidia.com/gpu: "1"
</code></code></pre><p>For an 8B model on a single GPU. No CPU limit. Memory limit 50% above request.</p><p><strong>Pattern 2: Multi-GPU inference with tensor parallelism</strong></p><pre><code><code>resources:
  requests:
    cpu: "8"
    memory: 64Gi
    nvidia.com/gpu: "2"
  limits:
    memory: 96Gi
    nvidia.com/gpu: "2"
</code></code></pre><p>For a 70B model across 2 GPUs. Higher CPU request because tensor parallelism increases CPU usage for inter-GPU coordination. /dev/shm must also be mounted (16Gi minimum).</p><p><strong>Pattern 3: Training job (batch)</strong></p><pre><code><code>resources:
  requests:
    cpu: "16"
    memory: 128Gi
    nvidia.com/gpu: "4"
  limits:
    memory: 160Gi
    nvidia.com/gpu: "4"
</code></code></pre><p>Training is more CPU-intensive than inference. Data loading, gradient computation, and checkpointing all use CPU. Higher CPU request is appropriate. Still no CPU limit.</p><p><strong>Pattern 4: Jupyter notebook (development)</strong></p><pre><code><code>resources:
  requests:
    cpu: "2"
    memory: 8Gi
    nvidia.com/gpu: "1"
  limits:
    cpu: "4"       # OK for dev: predictable, non-production
    memory: 16Gi
    nvidia.com/gpu: "1"
</code></code></pre><p>For development workloads, CPU limits are acceptable. Notebooks are interactive and predictable. The limit prevents a runaway notebook from consuming an entire node. This is the one exception to the &#8220;no CPU limits&#8221; rule.</p><div><hr></div><h3><strong>Monitoring Resource Usage</strong></h3><p>Set requests based on actual usage, not guesses. Monitor your pods and adjust.</p><pre><code><code># Check actual CPU and memory usage
kubectl top pod -n inference

# Check GPU utilization and memory
kubectl exec -it &lt;vllm-pod&gt; -- nvidia-smi

# Check resource requests vs actual usage over time
# Use Prometheus queries:
# container_cpu_usage_seconds_total
# container_memory_usage_bytes
</code></code></pre><p>If your pod consistently uses 2 CPU cores but requests 8, you are wasting 6 cores of scheduling capacity. Other pods cannot use those 6 cores even though they are idle.</p><p>If your pod&#8217;s memory usage is consistently at 95% of the limit, increase the limit. You are one spike away from OOMKilled.</p><div><hr></div><h3><strong>The Right-Sizing Workflow</strong></h3><ol><li><p>Start with the sizing guide above for initial deployment.</p></li><li><p>Run the workload for 48 hours with production traffic.</p></li><li><p>Check actual usage with kubectl top and Prometheus.</p></li><li><p>Set requests to the p95 of actual usage (covers normal spikes).</p></li><li><p>Set limits to 30 to 50% above requests (covers unusual spikes).</p></li><li><p>Review monthly and adjust as traffic patterns change.</p></li></ol><div><hr></div><h3><strong>The Bottom Line</strong></h3><p>For GPU inference pods: set CPU requests but not CPU limits. Set memory requests and limits with 30 to 50% headroom. Set GPU requests equal to limits (required by the device plugin).</p><p>The most common mistake is over-requesting CPU and memory. This wastes scheduling capacity and prevents other pods from running on the same node. The second most common mistake is not setting memory limits, which lets a memory leak crash the entire node.</p><p>Right-size based on actual usage. Monitor. Adjust. Repeat.</p><div><hr></div><p><em>Next week: GPU Monitoring with DCGM Exporter: The Metrics That Matter.</em></p><p><em>If you are building GPU infrastructure on Kubernetes, I cover resource management, model serving, and production operations every week. Subscribe at kubenatives.com.</em></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Autoscaling Inference Workloads: HPA and KEDA for GPU Pods]]></title><description><![CDATA[GPU pods are expensive. Running 4 replicas at 3 AM when traffic is zero wastes thousands per month. Here is how to scale them automatically.]]></description><link>https://www.kubenatives.com/p/autoscaling-gpu-inference-kubernetes-hpa-keda</link><guid isPermaLink="false">https://www.kubenatives.com/p/autoscaling-gpu-inference-kubernetes-hpa-keda</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 19 Jun 2026 13:00:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W3Ss!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your vLLM deployment runs 4 replicas 24/7. Each replica uses an A100 80GB. That is 4 GPUs reserved around the clock.</p><p>At peak (2 PM), all 4 replicas are busy. Queue depth is high. Users are waiting. You need more capacity.</p><p>At 3 AM, traffic drops to near zero. All 4 replicas are idle. GPU utilization is under 5%. You are paying $12/hour for GPUs that are doing nothing.</p><p>The Horizontal Pod Autoscaler (HPA) solves this. But GPU workloads have unique challenges that make standard HPA configurations fail. This article covers how to autoscale inference pods correctly.</p><div><hr></div><h3><strong>Why Standard HPA Does Not Work for GPU Pods</strong></h3><p>The default HPA scales on CPU utilization. For GPU inference pods, CPU utilization is meaningless. The real work happens on the GPU. CPU stays low even when the model is fully saturated.</p><p>GPU utilization is also a bad scaling metric for vLLM. PagedAttention keeps GPU utilization high by design. A vLLM pod at 85% GPU utilization might be handling 5 requests or 500. The utilization number does not tell you if the pod is overloaded.</p><p>The right metrics for scaling inference pods are request-level metrics, not hardware metrics.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sBRv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sBRv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 424w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 848w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 1272w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sBRv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png" width="838" height="507" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e10951d-f204-42a0-8fff-d049548aa323_838x507.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:507,&quot;width&quot;:838,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98450,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193241594?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sBRv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 424w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 848w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 1272w, https://substackcdn.com/image/fetch/$s_!sBRv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e10951d-f204-42a0-8fff-d049548aa323_838x507.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3><strong>The Right Scaling Metrics</strong></h3><p><strong>Queue depth (vllm:num_requests_waiting).</strong> This is the number of requests waiting to be processed. If requests are queuing, the pod is overloaded. Scale up.</p><pre><code><code># HPA scaling on queue depth
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: vllm-hpa
  namespace: inference
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: vllm-llama3
  minReplicas: 2
  maxReplicas: 8
  metrics:
  - type: Pods
    pods:
      metric:
        name: vllm_num_requests_waiting
      target:
        type: AverageValue
        averageValue: "5"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Pods
        value: 2
        periodSeconds: 120
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Pods
        value: 1
        periodSeconds: 180
</code></code></pre><p>When the average queue depth across pods exceeds 5 requests, the HPA adds replicas. When it drops below 5, it removes them.</p><p><strong>Time to First Token (TTFT).</strong> This measures how long users wait before seeing the first token. If TTFT exceeds your SLO (say 500ms), scale up.</p><p><strong>Active requests (vllm:num_requests_running).</strong> The number of requests currently being processed. This indicates current load, not queued load. Useful as a secondary metric alongside queue depth.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/autoscaling-gpu-inference-kubernetes-hpa-keda?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Kubenatives! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/autoscaling-gpu-inference-kubernetes-hpa-keda?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/autoscaling-gpu-inference-kubernetes-hpa-keda?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p><div><hr></div><h3><strong>Setting Up Custom Metrics</strong></h3><p>The HPA needs to read vLLM metrics through the Kubernetes metrics API. This requires a metrics adapter.</p><p><strong>Option 1: Prometheus Adapter</strong></p><p>The Prometheus Adapter exposes Prometheus metrics as Kubernetes custom metrics. vLLM exports metrics to Prometheus. The adapter makes them available to the HPA.</p><pre><code><code># Prometheus Adapter configuration
rules:
- seriesQuery: 'vllm:num_requests_waiting{namespace!="",pod!=""}'
  resources:
    overrides:
      namespace: {resource: "namespace"}
      pod: {resource: "pod"}
  name:
    matches: "^(.*)"
    as: "vllm_num_requests_waiting"
  metricsQuery: 'avg(&lt;&lt;.Series&gt;&gt;{&lt;&lt;.LabelMatchers&gt;&gt;}) by (&lt;&lt;.GroupBy&gt;&gt;)'
</code></code></pre><p><strong>Option 2: KEDA</strong></p><p>KEDA (Kubernetes Event Driven Autoscaling) is simpler to set up than the Prometheus Adapter. It can query Prometheus directly without a separate adapter.</p><pre><code><code>apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: vllm-scaledobject
  namespace: inference
spec:
  scaleTargetRef:
    name: vllm-llama3
  minReplicaCount: 2
  maxReplicaCount: 8
  cooldownPeriod: 300
  pollingInterval: 15
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.monitoring:9090
      metricName: vllm_queue_depth
      query: |
        avg(vllm:num_requests_waiting{namespace="inference",pod=~"vllm-llama3.*"})
      threshold: "5"
      activationThreshold: "1"
</code></code></pre><p>KEDA also supports scaling to zero, which the standard HPA does not. For development environments, this means your GPU pods shut down completely when unused.</p><div><hr></div><h3><strong>The Scale-Up Problem: Model Loading Time</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W3Ss!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W3Ss!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 424w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 848w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 1272w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W3Ss!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png" width="1456" height="726" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:726,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:204068,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193241594?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!W3Ss!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 424w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 848w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 1272w, https://substackcdn.com/image/fetch/$s_!W3Ss!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdaa5930-d12d-4833-8266-df2f2a36dd62_1676x836.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When the HPA triggers a scale-up, a new pod starts. The pod needs to load the model before it can serve requests.</p><p>For a 70B model without a PVC cache, that is 15 to 30 minutes. During that time, traffic is still hitting the existing (overloaded) pods. The new replica is not helping yet.</p><p>This is the fundamental challenge of autoscaling inference workloads. The scale-up latency is measured in minutes, not seconds.</p><p><strong>Fixes:</strong></p><p>PVC-backed model cache reduces startup to 1 to 3 minutes. Pre-populated shared storage makes it even faster. These are covered in the vLLM Model Loading article.</p><p>Startup probes must have a large enough window. If the startup probe fails before the model loads, the pod gets killed and restarted. An infinite restart loop.</p><pre><code><code>startupProbe:
  httpGet:
    path: /health
    port: 8000
  failureThreshold: 120     # 120 * 10s = 20 minutes
  periodSeconds: 10
</code></code></pre><p><strong>Proactive scaling.</strong> Instead of reacting to high queue depth, scale up before traffic arrives. If your traffic pattern is predictable (peak at 2 PM, low at 3 AM), use a CronJob to adjust minReplicas on a schedule.</p><pre><code><code># Scale up before peak hours (1:30 PM)
kubectl patch hpa vllm-hpa -n inference \
  -p '{"spec":{"minReplicas":4}}'

# Scale down after peak (10 PM)
kubectl patch hpa vllm-hpa -n inference \
  -p '{"spec":{"minReplicas":2}}'
</code></code></pre><div><hr></div><h3><strong>The Scale-Down Problem: Disrupting Active Requests</strong></h3><p>Scaling down terminates a pod. If that pod is in the middle of generating a response, the user gets an error.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wOO3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wOO3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 424w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 848w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wOO3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png" width="1456" height="1113" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1113,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:361615,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193241594?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wOO3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 424w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 848w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!wOO3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8e29fde-8724-4b6c-b770-a2ad73b287a0_1682x1286.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Fixes:</strong></p><p>Set a generous terminationGracePeriodSeconds. This gives the pod time to finish active requests before shutting down.</p><pre><code><code>spec:
  terminationGracePeriodSeconds: 120
</code></code></pre><p>The HPA behavior section controls how aggressively it scales down. Use stabilization windows and slow scale-down policies.</p><pre><code><code>behavior:
  scaleDown:
    stabilizationWindowSeconds: 300    # Wait 5 min of low load
    policies:
    - type: Pods
      value: 1                         # Remove 1 pod at a time
      periodSeconds: 180               # Every 3 minutes
</code></code></pre><p>This means: wait for 5 minutes of sustained low load before scaling down. Then remove 1 pod every 3 minutes. Gradual and safe.</p><p>A PodDisruptionBudget ensures at least one pod is always running:</p><pre><code><code>apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: vllm-pdb
  namespace: inference
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: vllm-llama3
</code></code></pre><div><hr></div><h3><strong>KEDA vs HPA: When to Use Each</strong></h3><p><strong>Use standard HPA when:</strong> You already have the Prometheus Adapter installed. You do not need scale-to-zero. Your scaling logic is simple (one or two metrics).</p><p><strong>Use KEDA when:</strong> You want to query Prometheus directly without an adapter. You need scale-to-zero for dev environments. You want to scale on external metrics (SQS queue length, Kafka lag, HTTP request rate from an external load balancer).</p><p>For most production inference workloads, KEDA is the simpler choice. It handles the metrics pipeline internally and supports more trigger types.</p><div><hr></div><h3><strong>The Complete Autoscaling Stack</strong></h3><pre><code><code>vLLM exports metrics &#8594; Prometheus scrapes &#8594; KEDA queries &#8594;
HPA adjusts replicas &#8594; New pod starts &#8594; Model loads from PVC &#8594;
Pod passes startup probe &#8594; Traffic routes to new pod
</code></code></pre><p>The full stack requires: vLLM with metrics enabled, a ServiceMonitor for Prometheus scraping, KEDA or Prometheus Adapter, an HPA or ScaledObject, PVC-backed model storage for fast startup, and a PodDisruptionBudget for safe scale-down.</p><div><hr></div><h3><strong>The Bottom Line</strong></h3><p>Do not scale GPU pods on CPU or GPU utilization. Scale on queue depth. Use KEDA for simplicity or Prometheus Adapter for flexibility.</p><p>The hard part is not the autoscaling configuration. It is the model loading time on scale-up. Solve that with PVCs and shared storage first. Then add autoscaling on top.</p><p>Scale up fast (within 1 to 3 minutes with cached models). Scale down slowly (stabilization windows and one pod at a time). Never scale to zero in production unless you can tolerate cold start latency.</p><div><hr></div><p><em>Next week: Resource Requests and Limits for GPU Workloads.</em></p><p><em>If you are building inference infrastructure on Kubernetes, I cover model serving, GPU scheduling, and autoscaling every week. Subscribe at kubenatives.com.</em></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Kubernetes Upgrade Strategy: kubeadm Cluster Upgrades Without Downtime]]></title><description><![CDATA[Kubernetes drops support for old versions every 12 months. Here is how to upgrade without breaking production.]]></description><link>https://www.kubenatives.com/p/kubeadm-cluster-upgrades-production-playbook</link><guid isPermaLink="false">https://www.kubenatives.com/p/kubeadm-cluster-upgrades-production-playbook</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 12 Jun 2026 01:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Bon2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Kubernetes releases a new minor version every 4 months. Each version is supported for 14 months. After that, no more security patches. No more bug fixes.</p><p>If you are running 1.28 and the current version is 1.32, you are 4 versions behind. That is 3 sequential upgrades to catch up. Each one can break things.</p><p>Most teams put off upgrades because they are scared. The upgrade process is poorly documented for production environments. The official docs cover the happy path. They do not cover what happens when something goes wrong mid-upgrade.</p><p>This article covers the full upgrade strategy: planning, pre-flight checks, the upgrade itself, and what to do when things break.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bon2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bon2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 424w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 848w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 1272w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bon2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png" width="1456" height="1487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1487,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:298364,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193239995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bon2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 424w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 848w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 1272w, https://substackcdn.com/image/fetch/$s_!Bon2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89e533d-1c60-4446-9c9a-8bb002fbba76_1508x1540.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3><strong>Understanding the Upgrade Path</strong></h3><p>Kubernetes does not support skipping minor versions. You must upgrade one minor version at a time.</p><pre><code><code>1.28 &#8594; 1.29 &#8594; 1.30 &#8594; 1.31 &#8594; 1.32
</code></code></pre><p>You cannot go directly from 1.28 to 1.32. Each hop requires a full upgrade cycle: control plane first, then worker nodes.</p><p>Within each minor version, you can jump patch versions freely. Going from 1.30.2 to 1.30.8 is safe and does not require the full upgrade procedure. Just update the kubelet and kubectl binaries.</p><p><strong>The support window:</strong></p><pre><code><code>Version    Released     End of Support
1.30       Apr 2024     Jun 2025
1.31       Aug 2024     Oct 2025
1.32       Dec 2024     Feb 2026
1.33       Apr 2025     Jun 2026
</code></code></pre><p>If you are more than 2 versions behind the current release, prioritize upgrading. You are running on borrowed time.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/kubeadm-cluster-upgrades-production-playbook?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Kubenatives! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/kubeadm-cluster-upgrades-production-playbook?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/kubeadm-cluster-upgrades-production-playbook?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p><div><hr></div><h3><strong>Pre-Upgrade Checklist</strong></h3><p>Run these checks before every upgrade. Do not skip them. They catch 90% of upgrade failures before they happen.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HRpE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HRpE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 424w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 848w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HRpE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png" width="1456" height="984" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:984,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:238480,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193239995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HRpE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 424w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 848w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!HRpE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bad24a3-e0af-4d18-9485-7027f54d86a9_1506x1018.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Check 1: Read the changelog for breaking changes</strong></p><p>Every Kubernetes release removes deprecated APIs, changes default behaviors, and sometimes breaks addons.</p><pre><code><code># Check for deprecated APIs in your cluster
# Install kubent (kube-no-trouble)
kubectl krew install deprecations

# Or use the standalone tool
kubent
</code></code></pre><p>kubent scans your cluster for resources using deprecated or removed APIs. If it finds any, update those resources BEFORE upgrading. An API that was deprecated in 1.30 might be removed in 1.32. Your manifests will fail to apply after the upgrade.</p><p><strong>Check 2: Verify addon compatibility</strong></p><p>Your CNI plugin (Calico, Cilium, Flannel), CSI drivers, ingress controller, and cert-manager all have Kubernetes version requirements. Check each one against the target version.</p><pre><code><code># Check current versions of critical addons
kubectl get pods -n kube-system -o custom-columns=\
  NAME:.metadata.name,\
  IMAGE:.spec.containers[0].image
</code></code></pre><p>If your CNI plugin does not support the target Kubernetes version, upgrade the CNI first.</p><p><strong>Check 3: Back up etcd</strong></p><p>This is non-negotiable. If the upgrade fails catastrophically, the etcd backup is your recovery path.</p><pre><code><code>etcdctl snapshot save /var/backups/etcd/pre-upgrade-$(date +%Y%m%d).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Verify the snapshot
etcdctl snapshot status /var/backups/etcd/pre-upgrade-*.db --write-out=table
</code></code></pre><p>Copy the backup off the control plane node. If the node dies during upgrade, the backup on the node is useless.</p><p><strong>Check 4: Verify PodDisruptionBudgets</strong></p><p>PDBs control how many pods can be unavailable during node drains. If a PDB prevents draining, the upgrade stalls.</p><pre><code><code># List all PDBs
kubectl get pdb --all-namespaces

# Check for PDBs that might block drains
kubectl get pdb --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: minAvailable={.spec.minAvailable} maxUnavailable={.spec.maxUnavailable}{"\n"}{end}'
</code></code></pre><p>A PDB with minAvailable equal to the current replica count blocks all drains. Either increase replicas or temporarily relax the PDB during upgrades.</p><p><strong>Check 5: Dry run the upgrade</strong></p><pre><code><code># On the first control plane node
sudo kubeadm upgrade plan
</code></code></pre><p>This shows exactly what will be upgraded and flags any issues. If it reports errors, fix them before proceeding.</p><div><hr></div><h3><strong>Upgrading the Control Plane</strong></h3><p>The control plane upgrades one node at a time. Never upgrade all control plane nodes simultaneously.</p><p><strong>Step 1: Upgrade kubeadm on the first control plane node</strong></p><pre><code><code># Update the package repository
sudo apt-get update

# Install the target version of kubeadm
sudo apt-get install -y kubeadm=1.31.0-1.1

# Verify
kubeadm version
</code></code></pre><p><strong>Step 2: Apply the upgrade</strong></p><pre><code><code># On the FIRST control plane node only
sudo kubeadm upgrade apply v1.31.0
</code></code></pre><p>This upgrades the API server, controller manager, scheduler, and kube-proxy on this node. etcd is upgraded if it is managed by kubeadm (stacked topology).</p><p><strong>Expected output:</strong></p><pre><code><code>[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.31.0". Enjoy!
</code></code></pre><p>If it fails, do NOT proceed. Check the error. Common failures: etcd health check fails (fix etcd first), certificate issues (renew with kubeadm certs renew all), or insufficient disk space.</p><p><strong>Step 3: Upgrade kubelet and kubectl on the first node</strong></p><pre><code><code>sudo apt-get install -y kubelet=1.31.0-1.1 kubectl=1.31.0-1.1
sudo systemctl daemon-reload
sudo systemctl restart kubelet
</code></code></pre><p><strong>Step 4: Verify the first node</strong></p><pre><code><code>kubectl get nodes
# The upgraded node should show v1.31.0
# Other nodes still show the old version - this is expected
</code></code></pre><p><strong>Step 5: Upgrade remaining control plane nodes</strong></p><p>On each additional control plane node:</p><pre><code><code>sudo apt-get install -y kubeadm=1.31.0-1.1

# Note: use "upgrade node" not "upgrade apply" for subsequent nodes
sudo kubeadm upgrade node

sudo apt-get install -y kubelet=1.31.0-1.1 kubectl=1.31.0-1.1
sudo systemctl daemon-reload
sudo systemctl restart kubelet
</code></code></pre><p>Wait for each node to show Ready before moving to the next.</p><div><hr></div><h3><strong>Upgrading Worker Nodes</strong></h3><p>Worker nodes upgrade one at a time (or in batches if you have capacity).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6AR0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6AR0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 424w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 848w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 1272w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6AR0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png" width="1456" height="783" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:783,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:203881,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/193239995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6AR0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 424w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 848w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 1272w, https://substackcdn.com/image/fetch/$s_!6AR0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c34773-e6a4-49ee-8c2e-1ce1ebc461cb_1506x810.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Step 1: Drain the node</strong></p><pre><code><code>kubectl drain &lt;node-name&gt; \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=120 \
  --timeout=300s
</code></code></pre><p>This evicts all pods from the node. DaemonSets stay (they run on every node). Pods with emptyDir volumes lose their data.</p><p><strong>Step 2: Upgrade kubeadm, kubelet, kubectl</strong></p><pre><code><code># SSH into the worker node
sudo apt-get update
sudo apt-get install -y kubeadm=1.31.0-1.1
sudo kubeadm upgrade node
sudo apt-get install -y kubelet=1.31.0-1.1 kubectl=1.31.0-1.1
sudo systemctl daemon-reload
sudo systemctl restart kubelet
</code></code></pre><p><strong>Step 3: Uncordon the node</strong></p><pre><code><code>kubectl uncordon &lt;node-name&gt;
</code></code></pre><p>The node is now schedulable again. Pods will be scheduled back onto it.</p><p><strong>Step 4: Verify and move to the next node</strong></p><pre><code><code>kubectl get nodes
# Upgraded node shows v1.31.0 and Ready status
</code></code></pre><p>Repeat for each worker node. If you have GPU nodes, upgrade them last. GPU pods take longer to reschedule because of model loading times.</p><div><hr></div><h3><strong>Handling GPU Nodes During Upgrades</strong></h3><p>GPU nodes need special attention:</p><p>The GPU Operator must be compatible with the target Kubernetes version. Check the NVIDIA GPU Operator compatibility matrix before upgrading.</p><p>vLLM pods take 1 to 30 minutes to restart (model loading). Plan for this downtime per node. If you have a PVC-backed model cache, restart is faster (1 to 3 minutes instead of 30).</p><p>Drain GPU nodes with a longer grace period:</p><pre><code><code>kubectl drain gpu-node-1 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=300 \
  --timeout=600s
</code></code></pre><p>After uncordoning, verify the GPU Operator components restart correctly:</p><pre><code><code>kubectl get pods -n gpu-operator -o wide | grep gpu-node-1
</code></code></pre><p>All GPU Operator pods on that node should be Running before proceeding to the next GPU node.</p><div><hr></div><h3><strong>Post-Upgrade Validation</strong></h3><p>Run these checks after all nodes are upgraded:</p><pre><code><code># 1. All nodes on the new version and Ready
kubectl get nodes

# 2. All system pods healthy
kubectl get pods -n kube-system

# 3. etcd cluster healthy
etcdctl endpoint health --cluster

# 4. DNS working
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default

# 5. Create and delete a test resource
kubectl create configmap upgrade-test --from-literal=version=1.31
kubectl delete configmap upgrade-test

# 6. Check for pods in bad states
kubectl get pods --all-namespaces --field-selector status.phase!=Running,status.phase!=Succeeded

# 7. Verify GPU workloads (if applicable)
kubectl get pods -n inference
</code></code></pre><div><hr></div><h3><strong>Rollback Strategy</strong></h3><p>If the upgrade fails mid-way:</p><p>For control plane: restore from the etcd backup taken in the pre-upgrade checklist. This rolls back the cluster state to before the upgrade.</p><p>For worker nodes: the failed node can be drained and reimaged with the old version. Other nodes continue running normally.</p><p>The key: always have the etcd backup. Without it, there is no rollback. The pre-upgrade etcd snapshot is your safety net.</p><div><hr></div><h3><strong>The Bottom Line</strong></h3><p>Kubernetes upgrades are not optional. The support window is 14 months. After that, you are running unpatched software in production.</p><p>The process: back up etcd, check deprecated APIs, verify addon compatibility, upgrade control plane one node at a time, upgrade workers one node at a time, validate everything.</p><p>Do not skip the pre-flight checks. Do not upgrade all nodes at once. Do not skip the etcd backup. These three mistakes cause 90% of upgrade failures.</p><div><hr></div><p><em>Next week: Autoscaling Inference Workloads: HPA and KEDA for GPU Pods.</em></p><p><em>If you are running self-managed Kubernetes clusters, I cover operations, upgrades, and GPU infrastructure every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Network Policies in Practice: When Your Pods Cannot Talk to Each Other]]></title><description><![CDATA[You implemented network policies for security. Then DNS broke. Then inter-service communication broke. Here is how to do it without breaking everything.]]></description><link>https://www.kubenatives.com/p/kubernetes-network-policies-zero-trust</link><guid isPermaLink="false">https://www.kubenatives.com/p/kubernetes-network-policies-zero-trust</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 05 Jun 2026 13:00:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ENyx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>By default, every pod in Kubernetes can talk to every other pod. No restrictions. No isolation. A compromised pod in the frontend namespace can reach your database in the backend namespace.</p><p>Network policies fix this. They are firewall rules for pod-to-pod traffic. But most teams implement them wrong. They add a restrictive policy, DNS breaks silently, and they spend hours debugging before removing the policy and giving up.</p><p>This article covers how to implement network policies correctly. Starting with the one rule that prevents 90% of the problems.</p><div><hr></div><h2><strong>The Default Behavior</strong></h2><p>With no network policies, Kubernetes networking is fully open. Every pod can reach every other pod on any port. Every pod can reach external services. There are no restrictions.</p><p>The moment you create a NetworkPolicy in a namespace, the behavior changes. Pods selected by the policy are now restricted. Traffic not explicitly allowed by a policy is denied.</p><p>This is the part that catches people. Adding one policy does not just restrict what that policy covers. It implicitly denies everything else for the selected pods.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Q6E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Q6E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 424w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 848w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Q6E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png" width="1456" height="1136" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1136,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:264176,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/192759422?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0Q6E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 424w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 848w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!0Q6E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7f1dacc-53e0-4487-a89d-8580c3466ba1_1692x1320.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code># This policy allows port 8080 ingress from the frontend namespace.
# But it ALSO denies all other ingress to these pods.
# AND it denies all egress from these pods (if policyTypes includes Egress).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
    ports:
    - protocol: TCP
      port: 8080
</code></code></pre><div><hr></div><h2><strong>Rule Zero: Allow DNS First</strong></h2><p>This is the single most important rule. Before you create any other network policy, deploy this one in every namespace:</p><pre><code><code>apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: backend
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
</code></code></pre><p>Without this, pods lose DNS resolution the moment you add any egress policy. The failure is silent. No error message. No rejection. Queries are dropped and the application waits for a timeout.</p><p>This is the #1 cause of &#8220;network policies broke everything.&#8221; Deploy the DNS egress rule first. In every namespace. Before anything else.</p><div><hr></div><h2><strong>Building a Zero-Trust Network Step by Step</strong></h2><p>The safest approach is to start with a default deny policy and then explicitly allow what you need. Here is the order:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_nQT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_nQT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 424w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 848w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 1272w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_nQT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png" width="1456" height="1374" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1374,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:372045,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/192759422?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!_nQT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 424w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 848w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 1272w, https://substackcdn.com/image/fetch/$s_!_nQT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1128238-22b3-431f-9c9a-58d928afe11d_1678x1584.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Step 1: Default deny all traffic</strong></p><pre><code><code>apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: backend
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
</code></code></pre><p>This blocks all ingress and egress for every pod in the namespace. Nothing can communicate. This is intentionally extreme. You will add allow rules next.</p><p><strong>Step 2: Allow DNS egress (Rule Zero)</strong></p><p>Deploy the DNS policy from above. Pods can now resolve names but cannot reach anything else.</p><p><strong>Step 3: Allow inter-service communication</strong></p><pre><code><code>apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-ingress
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
      podSelector:
        matchLabels:
          app: web
    ports:
    - protocol: TCP
      port: 8080
</code></code></pre><p>This allows the web pod in the frontend namespace to reach the api-server pod in the backend namespace on port 8080. Nothing else can reach the api-server.</p><p><strong>Step 4: Allow database access</strong></p><pre><code><code>apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-ingress
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api-server
    ports:
    - protocol: TCP
      port: 5432
</code></code></pre><p>Only the api-server in the same namespace can reach postgres on port 5432. The frontend cannot reach the database directly. A compromised frontend pod cannot access your data.</p><p><strong>Step 5: Allow external egress for pods that need it</strong></p><pre><code><code>apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-egress
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
  - Egress
  egress:
  # DNS
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
  # Database
  - to:
    - podSelector:
        matchLabels:
          app: postgres
    ports:
    - protocol: TCP
      port: 5432
  # External APIs (HTTPS)
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8
        - 172.16.0.0/12
        - 192.168.0.0/16
    ports:
    - protocol: TCP
      port: 443
</code></code></pre><p>The api-server can reach DNS, the database, and external HTTPS endpoints. It cannot reach anything else inside the cluster. The ipBlock with except clauses blocks access to other internal services while allowing external API calls.</p><div><hr></div><h2><strong>Common Mistakes</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ENyx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ENyx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 424w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 848w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 1272w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ENyx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:265970,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/192759422?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ENyx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 424w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 848w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 1272w, https://substackcdn.com/image/fetch/$s_!ENyx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce47126d-a885-4b8e-9c88-f245550f7aec_1674x1256.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Mistake 1: Forgetting DNS egress.</strong> The #1 cause of &#8220;network policies broke everything.&#8221; Always deploy the DNS allow rule first.</p><p><strong>Mistake 2: Using podSelector without namespaceSelector.</strong> A podSelector alone only matches pods in the same namespace. To allow traffic from another namespace, you must include a namespaceSelector.</p><pre><code><code># WRONG: only matches pods in the SAME namespace
- from:
  - podSelector:
      matchLabels:
        app: web

# RIGHT: matches pods in the frontend namespace
- from:
  - namespaceSelector:
      matchLabels:
        name: frontend
    podSelector:
      matchLabels:
        app: web
</code></code></pre><p><strong>Mistake 3: AND vs OR logic.</strong> When namespaceSelector and podSelector are in the same <code>from</code> entry (same YAML block), they are AND logic. Both must match. When they are separate entries (separate list items), they are OR logic. Either can match.</p><pre><code><code># AND: must be in frontend namespace AND have app=web label
- from:
  - namespaceSelector:
      matchLabels:
        name: frontend
    podSelector:
      matchLabels:
        app: web

# OR: anything in frontend namespace OR anything with app=web label
- from:
  - namespaceSelector:
      matchLabels:
        name: frontend
  - podSelector:
      matchLabels:
        app: web
</code></code></pre><p>The difference is one hyphen. One wrong indent and your policy allows far more traffic than intended. This is the most dangerous mistake in network policies.</p><p><strong>Mistake 4: Not labeling namespaces.</strong> NetworkPolicies select namespaces by label, not by name. If your namespace does not have a label, the namespaceSelector cannot match it.</p><pre><code><code># Label your namespaces
kubectl label namespace frontend name=frontend
kubectl label namespace backend name=backend
</code></code></pre><p>Kubernetes 1.21+ automatically adds the label <code>kubernetes.io/metadata.name</code> to every namespace. Use that for reliability.</p><p><strong>Mistake 5: Forgetting monitoring and logging egress.</strong> Your pods need to reach Prometheus (for scraping) and your log aggregator. If you block egress without allowing these, you lose observability.</p><div><hr></div><h2><strong>Testing Network Policies</strong></h2><p>Never deploy network policies blind. Test them first.</p><pre><code><code># Deploy a test pod
kubectl run nettest --image=busybox:1.36 -n backend --rm -it --restart=Never -- sh

# Test DNS
nslookup kubernetes.default

# Test service connectivity
wget -qO- --timeout=3 http://api-server:8080/health

# Test external connectivity
wget -qO- --timeout=3 https://httpbin.org/get
</code></code></pre><p>Run these tests before and after applying each policy. If something breaks, you know exactly which policy caused it.</p><div><hr></div><p><strong>The Debug Checklist</strong></p><p>When a pod cannot reach a service after network policies are applied:</p><ol><li><p>Check if DNS works: <code>nslookup kubernetes.default</code> from inside the pod.</p></li><li><p>If DNS fails: the DNS egress rule is missing.</p></li><li><p>If DNS works but the service is unreachable: the ingress policy on the destination does not allow traffic from the source pod or namespace.</p></li><li><p>Check namespace labels: <code>kubectl get namespace &lt;ns&gt; --show-labels</code>.</p></li><li><p>Check pod labels: <code>kubectl get pod &lt;pod&gt; --show-labels</code>.</p></li><li><p>Verify the policy is selecting the right pods: <code>kubectl get networkpolicies -n &lt;ns&gt; -o yaml</code>.</p></li></ol><div><hr></div><p><strong>The Bottom Line</strong></p><p>Network policies are simple in concept and dangerous in practice. The implicit deny behavior catches everyone. The AND vs OR selector logic catches even experienced engineers.</p><p>Start with Rule Zero (allow DNS). Add default deny. Then explicitly allow each communication path. Test after every policy. Do not deploy all policies at once.</p><p>Five policies can secure a namespace. One missing DNS rule can break it.</p><div><hr></div><p><em>Next week: Kubernetes Upgrade Strategy: kubeadm Cluster Upgrades Without Downtime.</em></p><p><em>If you are running production Kubernetes clusters, I cover networking, GPU infrastructure, and operations every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3></h3>]]></content:encoded></item><item><title><![CDATA[Architecture Template: GPU Node Pool Setup]]></title><description><![CDATA[Complete YAML for a multi-tier GPU cluster with taints, tolerations, affinity, quotas, and priority classes. Copy, configure, deploy.]]></description><link>https://www.kubenatives.com/p/architecture-template-gpu-node-pool-setup</link><guid isPermaLink="false">https://www.kubenatives.com/p/architecture-template-gpu-node-pool-setup</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 29 May 2026 13:01:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sFJ_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>When to use this template:</strong></p><ul><li><p>Setting up GPU node isolation for the first time</p></li><li><p>Adding a new GPU tier to an existing cluster</p></li><li><p>Configuring per-team GPU quotas</p></li><li><p>Setting up priority classes for GPU workloads</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sFJ_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sFJ_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 424w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 848w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 1272w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sFJ_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png" width="829" height="972" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:972,&quot;width&quot;:829,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:135869,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191744186?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sFJ_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 424w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 848w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 1272w, https://substackcdn.com/image/fetch/$s_!sFJ_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632b8eb0-f7da-46ad-afea-9fa1fac294d0_829x972.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>File 1: gpu-node-taints.sh</h2><p>Apply taints to GPU nodes. Run once per node or set at the node pool level.</p><pre><code><code>#!/bin/bash
# gpu-node-taints.sh
# Apply taints to GPU nodes for workload isolation

set -euo pipefail

echo "=== Tainting Production GPU Nodes (Tier 1) ==="
for node in $(kubectl get nodes -l gpu-tier=production -o jsonpath='{.items[*].metadata.name}'); do
  kubectl taint nodes $node nvidia.com/gpu=present:NoSchedule --overwrite
  kubectl label nodes $node gpu-tier=production --overwrite
  echo "  Tainted: $node"
done

echo ""
echo "=== Tainting Development GPU Nodes (Tier 2) ==="
for node in $(kubectl get nodes -l gpu-tier=development -o jsonpath='{.items[*].metadata.name}'); do
  kubectl taint nodes $node nvidia.com/gpu=present:NoSchedule --overwrite
  kubectl label nodes $node gpu-tier=development --overwrite
  echo "  Tainted: $node"
done

echo ""
echo "=== Verification ==="
echo "Production GPU nodes:"
kubectl get nodes -l gpu-tier=production -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
echo ""
echo "Development GPU nodes:"
kubectl get nodes -l gpu-tier=development -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
</code></code></pre><div><hr></div><h2>File 2: priority-classes.yaml</h2><pre><code><code># gpu-priority-classes.yaml
# Three tiers of GPU workload priority

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-production
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: |
  Production GPU inference workloads.
  Highest priority. Will preempt development and batch workloads.
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-development
value: 100000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: |
  Development GPU workloads (notebooks, experiments).
  Preempted by production. Will preempt batch workloads.
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-batch
value: 10000
globalDefault: false
preemptionPolicy: Never
description: |
  Batch GPU jobs (training, data processing).
  Lowest priority. Will NOT preempt other workloads.
  Waits for available GPUs.
</code></code></pre><pre><code><code>kubectl apply -f priority-classes.yaml
</code></code></pre><p></p>
      <p>
          <a href="https://www.kubenatives.com/p/architecture-template-gpu-node-pool-setup">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[GPU Node Pools: Taints, Tolerations, and Cost Isolation]]></title><description><![CDATA[Stop CPU workloads from landing on GPU nodes. Taints, tolerations, node affinity, resource quotas, and priority classes for multi-tier GPU clusters.]]></description><link>https://www.kubenatives.com/p/gpu-node-pools-kubernetes-taints-tolerations</link><guid isPermaLink="false">https://www.kubenatives.com/p/gpu-node-pools-kubernetes-taints-tolerations</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 29 May 2026 13:01:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QpNG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You have a cluster with 3 types of nodes. CPU nodes for web applications. A100 nodes for inference. T4 nodes for development and testing.</p><p>Without any configuration, the Kubernetes scheduler treats them all the same. It sees available CPU and memory. It does not distinguish between a $200/month CPU node and a $30K/year GPU node.</p><p>A basic nginx pod with 100m CPU and 128Mi memory can land on your H100 node. The scheduler found resources. It scheduled the pod. It did exactly what it was designed to do.</p><p>This article covers how to stop that from happening. And how to build a multi-tier GPU cluster where the right workloads land on the right hardware every time.</p><div><hr></div><h2>The Problem: GPUs as Shared Resources</h2><p>By default, any pod can schedule on any node that has enough CPU and memory. GPU nodes have CPU and memory in addition to GPUs. Non-GPU workloads see the CPU and memory and schedule there.</p><p>The result: GPU nodes run a mix of GPU workloads and random CPU workloads. The CPU workloads consume memory and CPU that GPU workloads need. And you are paying GPU pricing for pods that do not use GPUs.</p><pre><code><code># Check what is running on your GPU nodes
kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=gpu-node-1
</code></code></pre><p>If you see system pods, monitoring agents, log collectors, and random application pods alongside your vLLM deployment, your GPU nodes are not isolated.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qTGf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qTGf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 424w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 848w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 1272w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qTGf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png" width="1456" height="1362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1362,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:380879,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191681104?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qTGf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 424w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 848w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 1272w, https://substackcdn.com/image/fetch/$s_!qTGf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a5e616b-ed3b-483a-b8a0-f63f246c3857_1674x1566.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Taints: Keep Non-GPU Workloads Off GPU Nodes</h2><p>A taint on a node tells the scheduler: &#8220;Do not place pods here unless they explicitly tolerate this taint.&#8221;</p><pre><code><code># Add a taint to all GPU nodes
kubectl taint nodes gpu-node-1 nvidia.com/gpu=present:NoSchedule
kubectl taint nodes gpu-node-2 nvidia.com/gpu=present:NoSchedule
</code></code></pre><p>After this, no pod can schedule on these nodes unless it has a matching toleration.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3No7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3No7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 424w, https://substackcdn.com/image/fetch/$s_!3No7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 848w, https://substackcdn.com/image/fetch/$s_!3No7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 1272w, https://substackcdn.com/image/fetch/$s_!3No7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3No7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png" width="1456" height="868" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:868,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:224256,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191681104?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3No7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 424w, https://substackcdn.com/image/fetch/$s_!3No7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 848w, https://substackcdn.com/image/fetch/$s_!3No7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 1272w, https://substackcdn.com/image/fetch/$s_!3No7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc804254d-d31d-4239-9dc9-b2b96d79a823_1680x1002.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>On managed Kubernetes (EKS, GKE, AKS), you set taints at the node pool level. Every node in the pool gets the taint automatically:</p><pre><code><code># GKE example: GPU node pool with taint
gcloud container node-pools create gpu-pool \
  --cluster=my-cluster \
  --machine-type=a2-highgpu-1g \
  --accelerator=type=nvidia-tesla-a100,count=1 \
  --num-nodes=3 \
  --node-taints=nvidia.com/gpu=present:NoSchedule
</code></code></pre><p><strong>Important:</strong> The NVIDIA GPU Operator automatically adds the taint <code>nvidia.com/gpu=present:NoSchedule</code> when it detects GPU hardware. If you are using the GPU Operator, the taints are already there. Check with:</p><pre><code><code>kubectl get nodes -l nvidia.com/gpu.present=true \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'
</code></code></pre><div><hr></div><h2>Tolerations: Allow GPU Workloads to Schedule</h2><p>Your GPU pods need a toleration that matches the taint:</p><pre><code><code>spec:
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
  containers:
  - name: vllm
    resources:
      limits:
        nvidia.com/gpu: "1"
</code></code></pre><p>The <code>operator: Exists</code> means &#8220;tolerate this taint regardless of value.&#8221; This is simpler than matching a specific value and works for any GPU taint.</p><p><strong>What about system pods?</strong> DaemonSets like the GPU Operator, node exporter, and log collectors need to run on GPU nodes too. They should also have the toleration. The GPU Operator DaemonSets already include it. Your monitoring stack may need it added manually.</p><div><hr></div><h2>Node Affinity: Target Specific GPU Types</h2><p>Taints prevent the wrong pods from landing on GPU nodes. But in a mixed GPU cluster (A100s and T4s), you also need to ensure the right pods land on the right GPU type.</p><p>GPU Feature Discovery (part of the GPU Operator) labels each node with its GPU model:</p><pre><code><code>nvidia.com/gpu.product=NVIDIA-A100-SXM4-80GB
nvidia.com/gpu.product=NVIDIA-T4
nvidia.com/gpu.product=NVIDIA-H100-80GB-HBM3
</code></code></pre><p>Use node affinity to target specific GPU types:</p><pre><code><code>spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: nvidia.com/gpu.product
            operator: In
            values:
            - NVIDIA-A100-SXM4-80GB
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
  containers:
  - name: vllm
    resources:
      limits:
        nvidia.com/gpu: "1"
</code></code></pre><p>This pod will only schedule on A100 nodes. Even if T4 nodes have available GPUs.</p><div><hr></div><h2>The Multi-Tier GPU Cluster Pattern</h2><p>Here is the pattern I use for production clusters with mixed GPU types:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QpNG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QpNG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 424w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 848w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 1272w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QpNG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png" width="1456" height="1005" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1005,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:264393,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191681104?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QpNG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 424w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 848w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 1272w, https://substackcdn.com/image/fetch/$s_!QpNG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17e5426a-9604-42ef-bd54-7ac6f69799d0_1678x1158.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each tier has its own taint and label. Workloads use tolerations to enter the GPU tiers and node affinity to target the right tier.</p><pre><code><code># Production inference: must land on Tier 1
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: gpu-tier
          operator: In
          values:
          - production
</code></code></pre><pre><code><code># Dev notebook: must land on Tier 2
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: gpu-tier
          operator: In
          values:
          - development
</code></code></pre><div><hr></div><h2>Cost Isolation with Resource Quotas</h2><p>Taints control where pods run. Resource quotas control how much each team can consume.</p><pre><code><code>apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-quota
  namespace: ml-team-a
spec:
  hard:
    requests.nvidia.com/gpu: "4"
    limits.nvidia.com/gpu: "4"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-quota
  namespace: ml-team-b
spec:
  hard:
    requests.nvidia.com/gpu: "2"
    limits.nvidia.com/gpu: "2"
</code></code></pre><p>Team A can use up to 4 GPUs. Team B can use up to 2. Neither team can consume more than their quota regardless of what is available in the cluster.</p><p>Combine resource quotas with LimitRanges to prevent individual pods from requesting too many GPUs:</p><pre><code><code>apiVersion: v1
kind: LimitRange
metadata:
  name: gpu-limits
  namespace: ml-team-a
spec:
  limits:
  - type: Container
    max:
      nvidia.com/gpu: "2"
    default:
      nvidia.com/gpu: "1"
</code></code></pre><p>No single container in team A&#8217;s namespace can request more than 2 GPUs. Default is 1 if not specified.</p><div><hr></div><h2>Priority Classes for GPU Workloads</h2><p>When GPU capacity is scarce, priority classes determine which pods get GPUs first and which get preempted.</p><pre><code><code>apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-production
value: 1000000
globalDefault: false
description: "Production GPU inference workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-development
value: 100000
globalDefault: false
description: "Development GPU workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-batch
value: 10000
globalDefault: false
preemptionPolicy: Never
description: "Batch GPU jobs - do not preempt others"
</code></code></pre><p>Production inference gets the highest priority. If a production pod needs a GPU and all are allocated, Kubernetes preempts a development or batch pod to make room.</p><p>The <code>preemptionPolicy: Never</code> on the batch class means batch jobs will wait for GPUs but will never kick out other workloads.</p><pre><code><code># Use in pod spec
spec:
  priorityClassName: gpu-production
</code></code></pre><div><hr></div><h2>Common Mistakes</h2><p><strong>Mistake 1: No taints on GPU nodes.</strong> Random CPU workloads consume resources on expensive GPU hardware. Always taint GPU nodes.</p><p><strong>Mistake 2: Forgetting tolerations on GPU Operator DaemonSets.</strong> If you add custom taints, the GPU Operator pods need matching tolerations. Otherwise the operator cannot run on GPU nodes, which means GPUs are never registered.</p><p><strong>Mistake 3: Using nodeSelector instead of nodeAffinity.</strong> nodeSelector is simpler but less flexible. You cannot express &#8220;schedule on A100 OR H100&#8221; with nodeSelector. nodeAffinity supports multiple values and complex expressions.</p><p><strong>Mistake 4: No resource quotas.</strong> Without quotas, one team can consume all GPUs in the cluster. This is fine with 2 engineers. It is chaos with 10 teams.</p><p><strong>Mistake 5: Same priority for all GPU workloads.</strong> When capacity is tight, production inference and a Jupyter notebook have the same priority. The notebook should yield to production. Use priority classes.</p><div><hr></div><h2>The Bottom Line</h2><p>GPU nodes are expensive. Treat them like expensive resources.</p><p>Taints keep non-GPU workloads off GPU hardware. Tolerations allow GPU workloads in. Node affinity targets specific GPU types. Resource quotas cap per-team consumption. Priority classes ensure production wins when capacity is scarce.</p><p>Five mechanisms. Together they turn a cluster where anything runs anywhere into a multi-tier platform where the right workloads land on the right hardware at the right priority.</p><div><hr></div><p><em>Next week: Network Policies in Practice: When Your Pods Cannot Talk to Each Other.</em></p><p><em>If you are building GPU infrastructure on Kubernetes, I cover scheduling, model serving, and production operations every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[LLMOps on Kubernetes: Patterns for Running LLMs in Production]]></title><description><![CDATA[Deploying the model is the easy part. Operating it in production is where most teams get stuck.]]></description><link>https://www.kubenatives.com/p/vllm-model-loading-kubernetes-pvc</link><guid isPermaLink="false">https://www.kubenatives.com/p/vllm-model-loading-kubernetes-pvc</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 22 May 2026 13:01:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!05kk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You deployed vLLM on Kubernetes. The model is serving requests. TTFT looks good. The HPA is scaling.</p><p>Then someone asks: &#8220;How do we roll out a new model version without downtime?&#8221; Or: &#8220;How do we know if the model&#8217;s responses are getting worse?&#8221; Or: &#8220;Can we test GPT-4o alongside Llama 3 and route traffic based on the use case?&#8221;</p><p>These are not model serving questions. These are LLMOps questions. And most teams have no framework for answering them.</p><p>This article covers the 6 patterns that turn a model deployment into a production LLM system.</p><div><hr></div><h2>Pattern 1: Model Versioning and Rollouts</h2><p>In traditional software, you version your code. In LLMOps, you version three things: the model weights, the serving configuration, and the prompt templates. All three change independently. All three affect output quality.</p><p><strong>The problem.</strong> You upgrade from Llama 3.1 8B to Llama 3.1 70B. The model is better. But your prompts were tuned for the 8B version. The 70B version interprets the system prompt differently. Response quality drops even though the model improved.</p><p><strong>The pattern.</strong> Version the model and the prompt together as a single deployment unit. A &#8220;model version&#8221; is not just the weights. It is the weights plus the serving config plus the prompt template.</p><p>On Kubernetes, this maps to a Deployment revision. Each revision locks in:</p><pre><code><code># Version 1: Llama 3.1 8B + prompt v1
containers:
- name: vllm
  image: vllm/vllm-openai:v0.6.0
  args:
  - --model
  - meta-llama/Llama-3.1-8B-Instruct
  env:
  - name: SYSTEM_PROMPT_VERSION
    value: "v1"

# Version 2: Llama 3.1 70B + prompt v2
containers:
- name: vllm
  image: vllm/vllm-openai:v0.6.0
  args:
  - --model
  - meta-llama/Llama-3.1-70B-Instruct
  - --tensor-parallel-size
  - "2"
  env:
  - name: SYSTEM_PROMPT_VERSION
    value: "v2"
</code></code></pre><p><strong>Rollout strategy.</strong> Never switch 100% of traffic at once. Use a canary deployment. Route 5% of traffic to the new version. Monitor quality metrics for 24 hours. If quality holds, increase to 25%, then 50%, then 100%.</p><p>KServe handles this natively with traffic splitting on InferenceService revisions. Without KServe, use Istio VirtualService or a gateway that supports weighted routing.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/vllm-model-loading-kubernetes-pvc?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Kubenatives! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/vllm-model-loading-kubernetes-pvc?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/vllm-model-loading-kubernetes-pvc?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p><div><hr></div><h2>Pattern 2: Prompt Management</h2><p>Prompts are configuration, not code. But most teams hardcode them in application source files. This means changing a prompt requires a code deployment, a build pipeline, a review cycle, and a rollout.</p><p><strong>The pattern.</strong> Store prompts in ConfigMaps or an external prompt store. The serving layer reads the prompt at request time, not at build time.</p><pre><code><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: prompt-templates
  namespace: inference
data:
  system-v1.txt: |
    You are a helpful assistant for DevOps engineers.
    Answer questions about Kubernetes, Docker, and cloud infrastructure.
    Be concise. Use code examples when relevant.
  system-v2.txt: |
    You are a senior infrastructure engineer assistant.
    Provide production-ready advice with specific commands.
    Always mention potential risks and rollback steps.
</code></code></pre><p>Mount the ConfigMap into the application pod. The application reads the prompt file at request time. To update a prompt, update the ConfigMap. The pods pick up the change without restarting (if using subPath mounts, a restart is needed).</p><p><strong>Why this matters.</strong> Prompt iteration is fast. Model deployment is slow (minutes to load weights). Decoupling prompts from deployments means you can iterate on prompts in seconds without touching the model.</p><div><hr></div><h2>Pattern 3: LLM Gateway and Routing</h2><p>Most production systems do not use a single model. They use different models for different tasks. A small fast model for classification. A large model for generation. A specialized model for code.</p><p><strong>The pattern.</strong> An LLM gateway sits between your application and the model backends. It handles routing, fallback, rate limiting, and load balancing across multiple models.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!05kk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!05kk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 424w, https://substackcdn.com/image/fetch/$s_!05kk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 848w, https://substackcdn.com/image/fetch/$s_!05kk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!05kk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!05kk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png" width="1456" height="986" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:986,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:258770,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191562995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!05kk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 424w, https://substackcdn.com/image/fetch/$s_!05kk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 848w, https://substackcdn.com/image/fetch/$s_!05kk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!05kk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7e4ce57-06e4-4cb4-b534-382c061c312e_1684x1140.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The gateway routes requests based on metadata in the request: model name, use case tag, user tier, or custom headers. If the primary model is overloaded or down, the gateway falls back to an alternative.</p><p>On Kubernetes, LiteLLM is the most common open source LLM gateway. It provides an OpenAI compatible API that proxies to multiple backends.</p><pre><code><code># LiteLLM config
model_list:
  - model_name: "generation"
    litellm_params:
      model: "hosted_vllm/meta-llama/Llama-3.1-70B-Instruct"
      api_base: "http://vllm-70b.inference:8000/v1"
  - model_name: "generation"
    litellm_params:
      model: "gpt-4o"
      api_key: "os.environ/OPENAI_API_KEY"
    # Fallback: if vLLM is down, route to OpenAI
</code></code></pre><p><strong>Why this matters.</strong> Without a gateway, your application has hardcoded model endpoints. Switching models requires application changes. With a gateway, you change the routing config. The application never knows.</p><div><hr></div><h2>Pattern 4: Quality Observability</h2><p>GPU metrics tell you if the model is running. They do not tell you if it is running well.</p><p>TTFT, throughput, and cache utilization are infrastructure metrics. They measure serving performance. But a model can serve fast responses that are completely wrong.</p><p><strong>The pattern.</strong> Add a quality observability layer that tracks response characteristics over time.</p><p>Metrics to track:</p><p><strong>Response length distribution.</strong> A sudden drop in average response length can indicate the model is generating truncated or degenerate responses. Plot a histogram of response token counts. Alert if the distribution shifts.</p><p><strong>Refusal rate.</strong> How often the model refuses to answer (returns &#8220;I cannot help with that&#8221; or similar). A spike in refusals after a prompt change indicates the guardrails are too aggressive.</p><p><strong>Latency per output token.</strong> Not just TTFT. Measure the time per token during decoding. If this increases without load changes, the model may be struggling with certain prompt patterns.</p><p><strong>User feedback signals.</strong> Thumbs up/down, regenerate clicks, copy events. These are noisy individually but powerful in aggregate. A drop in positive signals after a model change is a quality regression.</p><pre><code><code># Custom metrics to export alongside vLLM metrics
- name: llm_response_tokens_total
  type: histogram
  help: Distribution of response lengths in tokens
  buckets: [10, 50, 100, 200, 500, 1000, 2000]

- name: llm_refusal_total
  type: counter
  help: Number of refusal responses detected

- name: llm_user_feedback
  type: counter
  labels: [feedback_type]
  help: User feedback signals (positive, negative, regenerate)
</code></code></pre><p><strong>Why this matters.</strong> You cannot improve what you do not measure. Infrastructure metrics tell you the system is running. Quality metrics tell you it is working.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f224!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f224!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 424w, https://substackcdn.com/image/fetch/$s_!f224!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 848w, https://substackcdn.com/image/fetch/$s_!f224!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!f224!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f224!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png" width="1456" height="1187" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1187,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:320427,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191562995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f224!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 424w, https://substackcdn.com/image/fetch/$s_!f224!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 848w, https://substackcdn.com/image/fetch/$s_!f224!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!f224!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a17d9f3-b7b2-42e2-87cd-f862c766d6b7_1678x1368.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Pattern 5: Guardrails</h2><p>Production LLMs need input and output filtering. Without guardrails, a user can prompt to inject the model to ignore its system instructions. Or the model can generate harmful, incorrect, or off-topic content.</p><p><strong>The pattern.</strong> Two layers of filtering. Input guardrails before the model processes the request. Output guardrails after the model generates a response.</p><pre><code><code>Request &#8594; Input Filter &#8594; Model &#8594; Output Filter &#8594; Response
</code></code></pre><p>Input filtering catches prompt injection attempts, PII in user messages, and requests that are outside the model&#8217;s intended scope.</p><p>Output filtering catches harmful content, PII in responses, and responses that contradict known facts (hallucination detection).</p><p>On Kubernetes, guardrails run as a sidecar container or a separate microservice in the request path. Running them as a sidecar keeps latency low (no network hop). Running them as a separate service allows independent scaling and updates.</p><p><strong>The latency tradeoff.</strong> Every guardrail adds latency. Input filtering adds 10 to 50ms. Output filtering can add 50 to 200ms for content classification. For interactive chat applications, this matters. For batch processing, it does not.</p><p><strong>The pattern for production:</strong> Run lightweight keyword and regex filters in the request path (low latency). Run heavier ML-based content classifiers asynchronously. Flag problematic responses for review rather than blocking them in real time.</p><div><hr></div><h2>Pattern 6: Cost Attribution</h2><p>GPU infrastructure is expensive. When multiple teams share a model serving platform, you need to know who is using what and how much it costs.</p><p><strong>The pattern.</strong> Tag every request with a team or project identifier. Aggregate GPU-seconds and token counts per tag. Charge back to teams based on usage.</p><p>On Kubernetes, this maps to namespace-level resource quotas for GPU allocation and request-level tagging for usage tracking.</p><pre><code><code>Request headers:
  X-Team: search-team
  X-Project: product-search
  X-Budget-Code: eng-2024-q3
</code></code></pre><p>The LLM gateway logs these headers alongside token counts and GPU time. A billing pipeline aggregates usage per team per day.</p><pre><code><code>search-team: 2.4M input tokens, 800K output tokens, 48 GPU-hours
recommendation-team: 1.1M input tokens, 400K output tokens, 22 GPU-hours
</code></code></pre><p><strong>Why this matters.</strong> Without cost attribution, GPU spend is a shared cost that nobody owns. With attribution, teams optimize their own usage. The team generating 1M tokens per day will find ways to cache, batch, or use smaller models when they see their bill.</p><div><hr></div><h2>Putting It All Together</h2><p>The 6 patterns form a stack:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!r2kG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!r2kG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 424w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 848w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 1272w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!r2kG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png" width="1456" height="1272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1272,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:384661,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191562995?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!r2kG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 424w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 848w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 1272w, https://substackcdn.com/image/fetch/$s_!r2kG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee5de3b-25e9-4922-a79b-aa128522d0fb_1680x1468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Model serving is the foundation. You already have this from the vLLM and Triton articles.</p><p>Start with model versioning and prompt management. These are the minimum for operating in production. Without them, every change is a risky manual process.</p><p>Add the LLM gateway when you serve multiple models or need fallback routing.</p><p>Add guardrails when you serve external users or handle sensitive data.</p><p>Add quality observability and cost attribution as the system matures and multiple teams start using it.</p><p>Do not build all 6 on day one. Start with the bottom of the stack and work up.</p><div><hr></div><h2>The Bottom Line</h2><p>Deploying a model is the easy part. The hard part is versioning it, routing traffic to it, knowing if it is working well, keeping it safe, and understanding what it costs.</p><p>These 6 patterns are not theoretical. They are the operational layer that turns a vLLM deployment into a production LLM system. Every team running LLMs in production eventually builds all of them. The question is whether you build them intentionally or discover the need at 3 AM.</p><p>Start with model versioning and prompt management. Add the rest as your system grows.</p><div><hr></div><p><em>Next week: vLLM Model Loading Strategies: PVCs, Init Containers, and Shared Storage.</em></p><p><em>If you are building LLM infrastructure on Kubernetes, I cover the intersection of GPU infrastructure, model serving, and production operations every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Architecture Template: CoreDNS Debug ConfigMap]]></title><description><![CDATA[A production-ready CoreDNS configuration with logging, caching, and health checks for debugging DNS issues.]]></description><link>https://www.kubenatives.com/p/architecture-template-coredns-debug-configmap</link><guid isPermaLink="false">https://www.kubenatives.com/p/architecture-template-coredns-debug-configmap</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 15 May 2026 13:02:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!v3Zj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>When to use this template:</strong></p><ul><li><p>Setting up CoreDNS for a new cluster</p></li><li><p>Debugging intermittent DNS failures</p></li><li><p>Enabling DNS query logging temporarily</p></li><li><p>Optimizing DNS performance with caching</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v3Zj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v3Zj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 424w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 848w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 1272w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v3Zj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png" width="838" height="909" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f83aac51-093c-4587-8e79-fcb418588360_838x909.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:909,&quot;width&quot;:838,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:190405,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190917526?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!v3Zj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 424w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 848w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 1272w, https://substackcdn.com/image/fetch/$s_!v3Zj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff83aac51-093c-4587-8e79-fcb418588360_838x909.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Template 1: Production CoreDNS ConfigMap</h2><p>This replaces the default CoreDNS Corefile with production-ready settings.</p><pre><code><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
            max_concurrent 1000
        }
        cache 30 {
            success 9984 30
            denial 9984 5
        }
        loop
        reload
        loadbalance
    }
</code></code></pre>
      <p>
          <a href="https://www.kubenatives.com/p/architecture-template-coredns-debug-configmap">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Kubernetes DNS Troubleshooting: CoreDNS, ndots, and the 5-Second Timeout]]></title><description><![CDATA[Every DNS issue in Kubernetes traces back to one of 5 causes. Here is how to find which one in under 3 minutes.]]></description><link>https://www.kubenatives.com/p/kubernetes-dns-troubleshooting-coredns</link><guid isPermaLink="false">https://www.kubenatives.com/p/kubernetes-dns-troubleshooting-coredns</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 15 May 2026 13:02:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!UcYG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your pod cannot reach the database. The application logs say &#8220;connection timed out.&#8221; You check the Service. It exists. The Endpoints look correct. The pod is running.</p><p>You spend an hour checking network policies, firewall rules, and pod security settings. Then someone runs nslookup from inside the pod, and DNS does not resolve.</p><p>It was DNS. It is always DNS.</p><p>But &#8220;it is DNS&#8221; is not a diagnosis. There are exactly 5 causes of DNS failures in Kubernetes. This article covers all of them with the exact commands to identify each one.</p><div><hr></div><h2>How Kubernetes DNS Works</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UcYG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UcYG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 424w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 848w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 1272w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UcYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png" width="1456" height="896" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:896,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:265861,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191250572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UcYG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 424w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 848w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 1272w, https://substackcdn.com/image/fetch/$s_!UcYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5ad53f9-75be-4ea3-85bc-c4bf8f72d20a_1690x1040.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Before debugging, you need to understand the path a DNS query takes inside a cluster.</p><p>When a pod makes a DNS request, here is what happens:</p><p>The application calls getaddrinfo() or a similar resolver function. The resolver reads <code>/etc/resolv.conf</code> inside the container. That file points to the CoreDNS Service IP (typically <code>10.96.0.10</code>). The query goes to CoreDNS. CoreDNS looks up the answer and returns it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/kubernetes-dns-troubleshooting-coredns?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/kubernetes-dns-troubleshooting-coredns?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p><p>The <code>/etc/resolv.conf</code> inside every pod looks like this:</p><pre><code><code>nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
</code></code></pre><p>Three lines. Each one matters. Each one can break.</p><p><strong>nameserver</strong> is the CoreDNS ClusterIP. If CoreDNS is down, no DNS works.</p><p><strong>search</strong> is the list of domains Kubernetes appends to short names. When you call <code>my-service</code>, Kubernetes actually tries <code>my-service.default.svc.cluster.local</code> first, then <code>my-service.svc.cluster.local</code>, then <code>my-service.cluster.local</code>, then the bare name.</p><p><strong>ndots:5</strong> is the setting that causes the most confusion and the most wasted time in production. More on this below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XhGt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XhGt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 424w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 848w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 1272w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XhGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png" width="1456" height="1291" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1291,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:355637,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191250572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XhGt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 424w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 848w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 1272w, https://substackcdn.com/image/fetch/$s_!XhGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a668ce-6616-40c4-b50d-8215f12a9fbc_1676x1486.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Cause 1: CoreDNS Pods Are Not Running</h2><p>The simplest cause. If CoreDNS is down, nothing resolves.</p><pre><code><code># Check CoreDNS pods
kubectl get pods -n kube-system -l k8s-app=kube-dns
</code></code></pre><p>Every pod should be Running. If any pod is in CrashLoopBackOff, Pending, or Error, that is your DNS problem.</p><p>Common reasons CoreDNS pods fail:</p><p>CoreDNS ConfigMap has a syntax error. Someone edited the Corefile and introduced a typo. CoreDNS cannot start with an invalid configuration.</p><pre><code><code># Check the Corefile
kubectl get configmap coredns -n kube-system -o yaml
</code></code></pre><p>Resource limits are too low. On large clusters, CoreDNS needs more CPU and memory than the defaults. If it is OOMKilled, DNS fails intermittently under load.</p><pre><code><code># Check for OOMKilled events
kubectl describe pods -n kube-system -l k8s-app=kube-dns | grep -A5 "Last State"
</code></code></pre><p>The node running CoreDNS is unhealthy. CoreDNS runs as a Deployment (usually 2 replicas). If both land on the same node and that node has issues, DNS fails cluster-wide.</p><p><strong>The fix:</strong> Ensure CoreDNS replicas are spread across nodes with pod anti-affinity. Most managed K8s providers do this by default. Self-managed clusters often miss it.</p><div><hr></div><h2>Cause 2: The ndots Problem</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FKdl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FKdl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 424w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 848w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 1272w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FKdl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png" width="1456" height="1035" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:266119,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/191250572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FKdl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 424w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 848w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 1272w, https://substackcdn.com/image/fetch/$s_!FKdl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20625acc-c3eb-42f7-8ad3-53c433f03247_1674x1190.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the most common DNS performance issue in Kubernetes. It does not cause DNS to fail. It causes DNS to be slow.</p><p>The <code>ndots:5</code> setting in <code>/etc/resolv.conf</code> tells the resolver: &#8220;If the name has fewer than 5 dots, append the search domains before trying the name as-is.&#8221;</p><p>When your application calls <code>api.stripe.com</code>, the resolver counts the dots. Two dots. Fewer than 5. So it tries:</p><pre><code><code>1. api.stripe.com.default.svc.cluster.local  &#8594; NXDOMAIN
2. api.stripe.com.svc.cluster.local          &#8594; NXDOMAIN
3. api.stripe.com.cluster.local              &#8594; NXDOMAIN
4. api.stripe.com                            &#8594; SUCCESS
</code></code></pre><p>Four DNS queries for one lookup. The first three always fail. Each failure takes time. On a busy cluster with thousands of pods, this multiplies into millions of unnecessary DNS queries per hour.</p><p><strong>The 5-second timeout.</strong> Each failed query has a timeout. With the default timeout of 5 seconds and multiple search domains, a single external DNS lookup can take 15 to 20 seconds in the worst case. This is the &#8220;5-second timeout&#8221; that shows up in application latency and makes engineers think the network is slow.</p><p><strong>The fix:</strong></p><p>Option 1: Use fully qualified domain names (FQDNs) with a trailing dot. <code>api.stripe.com.</code> (note the dot at the end) tells the resolver &#8220;this is a complete name, do not append search domains.&#8221; The resolver sends one query instead of four.</p><p>Option 2: Lower ndots in your pod spec for pods that make many external DNS calls:</p><pre><code><code>spec:
  dnsConfig:
    options:
    - name: ndots
      value: "2"
</code></code></pre><p>With <code>ndots:2</code>, names with 2 or more dots (like <code>api.stripe.com</code>) are tried as-is first. Internal service names (like <code>my-service</code>) still get the search domain treatment because they have 0 dots.</p><p>Option 3: Use a node-level DNS cache (NodeLocal DNSCache). This caches responses locally on each node, eliminating the network hop to CoreDNS for repeated queries. It also handles negative caching, so the failed search domain lookups resolve instantly from cache.</p><pre><code><code># Check if NodeLocal DNSCache is running
kubectl get pods -n kube-system -l k8s-app=node-local-dns
</code></code></pre><div><hr></div><h2>Cause 3: Service Has No Endpoints</h2><p>DNS resolves the Service name correctly. But the Service has no healthy backends. The connection still fails.</p><p>This looks like a DNS problem because <code>curl my-service:8080</code> times out. But DNS is working fine. The Service just has nothing to route to.</p><pre><code><code># Check if the Service has endpoints
kubectl get endpoints my-service

# Expected: at least one IP:port listed
# If empty: no pods match the Service selector
</code></code></pre><p>If the endpoints list is empty:</p><p>The Service selector does not match any pod labels. This is the most common cause. A typo in the selector or the pod labels.</p><pre><code><code># Compare Service selector with pod labels
kubectl get svc my-service -o jsonpath='{.spec.selector}'
kubectl get pods -l app=my-service
</code></code></pre><p>The pods exist but are not Ready. If the readiness probe is failing, Kubernetes removes the pod from the endpoints list. The pod is running but not receiving traffic.</p><pre><code><code># Check pod readiness
kubectl get pods -l app=my-service -o wide
# Look for 0/1 in the READY column
</code></code></pre><div><hr></div><h2>Cause 4: DNS Policy Misconfiguration</h2><p>Every pod has a <code>dnsPolicy</code> setting. The default is <code>ClusterFirst</code>, which means &#8220;use CoreDNS for everything.&#8221; But if someone sets it to the wrong value, DNS breaks.</p><p>The four DNS policies:</p><p><strong>ClusterFirst</strong> (default): Uses CoreDNS. Internal names resolve to cluster services. External names get forwarded to upstream DNS. This is what you want 99% of the time.</p><p><strong>Default</strong>: Uses the node&#8217;s DNS configuration, not CoreDNS. Internal service names do not resolve. This is almost never what you want in a cluster.</p><p><strong>None</strong>: No DNS configuration at all. You must provide everything in <code>dnsConfig</code>. Used for very specific edge cases.</p><p><strong>ClusterFirstWithHostNet</strong>: For pods running with <code>hostNetwork: true</code>. Uses CoreDNS but falls back to the node&#8217;s DNS if CoreDNS does not respond.</p><p>The most common mistake: setting <code>dnsPolicy: Default,</code> thinking it means &#8220;use the default Kubernetes DNS.&#8221; It does not. It means &#8220;use the node&#8217;s DNS, skip CoreDNS entirely.&#8221; Internal service names stop resolving.</p><pre><code><code># Check a pod's DNS policy
kubectl get pod my-pod -o jsonpath='{.spec.dnsPolicy}'
</code></code></pre><p>If a pod can resolve external names (<code>google.com</code>) but not internal names (<code>my-service.default.svc.cluster.local</code>), check the DNS policy first. It is probably set to <code>Default</code> instead of <code>ClusterFirst</code>.</p><div><hr></div><h2>Cause 5: Network Policy Blocking DNS</h2><p>If you have NetworkPolicies in your cluster, they might block DNS traffic. CoreDNS runs on port 53 (UDP and TCP). If your network policy does not explicitly allow egress to port 53, DNS queries are silently dropped.</p><pre><code><code># Check for network policies in the pod's namespace
kubectl get networkpolicies -n &lt;namespace&gt;
</code></code></pre><p>If network policies exist, verify they allow DNS egress:</p><pre><code><code># NetworkPolicy that allows DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
</code></code></pre><p>The tricky part: DNS failures from network policies are silent. The query is dropped. The application waits for a timeout. There is no error message saying &#8220;blocked by policy.&#8221; It just looks like DNS is slow or unresponsive.</p><p><strong>How to test:</strong> Exec into the pod and run a manual DNS query:</p><pre><code><code>kubectl exec -it my-pod -- nslookup kubernetes.default
</code></code></pre><p>If this times out but CoreDNS pods are healthy, a network policy is likely blocking the traffic.</p><div><hr></div><h2>The 3-Minute Debug Script</h2><p>Run this script when DNS is broken. It checks all 5 causes in order.</p><pre><code><code>#!/bin/bash
echo "=============================="
echo "Kubernetes DNS Debug"
echo "=============================="

echo ""
echo "=== 1. CoreDNS Pod Status ==="
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide

echo ""
echo "=== 2. CoreDNS Service ==="
kubectl get svc -n kube-system kube-dns

echo ""
echo "=== 3. CoreDNS Endpoints ==="
kubectl get endpoints -n kube-system kube-dns

echo ""
echo "=== 4. CoreDNS ConfigMap ==="
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' 2&gt;/dev/null
echo ""

echo ""
echo "=== 5. DNS Resolution Test ==="
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- \
  sh -c "nslookup kubernetes.default &amp;&amp; echo 'Internal DNS: OK' || echo 'Internal DNS: FAILED'"

echo ""
echo "=== 6. External DNS Test ==="
kubectl run dns-test-ext --image=busybox:1.36 --rm -it --restart=Never -- \
  sh -c "nslookup google.com &amp;&amp; echo 'External DNS: OK' || echo 'External DNS: FAILED'"

echo ""
echo "=== 7. Network Policies ==="
kubectl get networkpolicies --all-namespaces --no-headers 2&gt;/dev/null | wc -l
echo "network policies found in the cluster"

echo ""
echo "=============================="
echo "Debug complete."
echo "=============================="
</code></code></pre><p><strong>Reading the results:</strong></p><p>Internal DNS fails + External DNS fails = CoreDNS is down or unreachable. Check cause 1 and cause 5.</p><p>Internal DNS works + External DNS fails = CoreDNS upstream forwarding is broken. Check the Corefile <code>forward</code> directive.</p><p>Both work but application is slow = The ndots problem. Check cause 2.</p><p>DNS works from debug pod but not from application pod = DNS policy or network policy issue specific to that pod. Check causes 4 and 5.</p><div><hr></div><h2>The resolv.conf Cheat Sheet</h2><p>Every DNS issue starts with what is in <code>/etc/resolv.conf</code> inside the pod:</p><pre><code><code>kubectl exec my-pod -- cat /etc/resolv.conf
</code></code></pre><p><strong>What to look for:</strong></p><p>The <code>nameserver</code> should be the CoreDNS ClusterIP (usually <code>10.96.0.10</code>). If it is a different IP, check the pod&#8217;s DNS policy.</p><p>The <code>search</code> domains should include <code>&lt;namespace&gt;.svc.cluster.local</code>. If they are missing, the pod cannot resolve short service names.</p><p>The <code>ndots</code> value controls how many dots trigger the search domain behavior. Default is 5. Lower it if external DNS is slow.</p><div><hr></div><h2>The Bottom Line</h2><p>Five causes. Five debug steps. The script checks all of them in 3 minutes.</p><p>When DNS breaks: check CoreDNS pods first. If they are healthy, check endpoints. If endpoints exist, check ndots. If ndots is fine, check DNS policy. If the policy is correct, check network policies.</p><p>Do not start with tcpdump. Do not start with Wireshark. Start with the 5 causes in order. The answer is almost always in the first three.</p><div><hr></div><p><em>Next week: LLMOps on Kubernetes: Patterns for Running LLMs in Production.</em></p><p><em>If you are running production Kubernetes clusters, I cover control plane internals, GPU infrastructure, and debugging every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[The Course Platform I Wish Existed When I Was Interviewing for DevOps Roles]]></title><description><![CDATA[GPU infrastructure, Kubernetes security, LLM operations, performance tuning, and identity systems, taught through real interview scenarios]]></description><link>https://www.kubenatives.com/p/introducing-devopsbeast-devops-interview-prep</link><guid isPermaLink="false">https://www.kubenatives.com/p/introducing-devopsbeast-devops-interview-prep</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Sat, 09 May 2026 16:42:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VFu-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>The Course Platform I Wish Existed When I Was Interviewing for DevOps Roles</h1><p>Today I&#8217;m launching DevOpsBeast, a course platform for senior DevOps engineers preparing for interviews at FAANG and other top tier tech companies. It teaches the design first reasoning that real interviews test: Kubernetes architecture, GPU infrastructure, LLM operations, security, performance tuning, and identity systems.</p><p>This has been quietly building in the background for the past few months, and I want to walk you through what&#8217;s inside, why I built it, and what&#8217;s coming next.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vPJA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vPJA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 424w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 848w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 1272w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vPJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png" width="1329" height="1242" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1242,&quot;width&quot;:1329,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382838,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/197017465?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vPJA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 424w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 848w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 1272w, https://substackcdn.com/image/fetch/$s_!vPJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55abe7ef-ab34-47a7-a932-11e6441629d7_1329x1242.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Why I Built This</h2><p>Most DevOps interview prep is broken.</p><p>You either get tutorial videos that teach you tools without context, or you get LeetCode-style problem sets that have nothing to do with what interviewers actually ask. Neither prepares you for the moment when a senior engineer says, &#8220;Your cluster is slow. What do you do?&#8221;</p><p>That question doesn&#8217;t have the right answer. It has the right approach. And the difference between candidates who get hired at FAANG-level companies and candidates who don&#8217;t is whether they can demonstrate that approach in 45 minutes of high-pressure design conversation.</p><p>So I built DevOpsBeast.</p><p>It&#8217;s a course platform focused on one specific thing: teaching the design-first reasoning that senior DevOps interviews actually test.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VFu-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VFu-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 424w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 848w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 1272w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VFu-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png" width="1112" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1112,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138239,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/197017465?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VFu-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 424w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 848w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 1272w, https://substackcdn.com/image/fetch/$s_!VFu-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb385afaf-1f4c-4367-afd3-a046c249761b_1112x464.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>What&#8217;s Inside</h2><p>Every course follows the same format. Realistic scenarios, architecture design, technical reasoning, trade-off analysis, and the actual interview questions that come from these scenarios at companies like Atlassian, Netflix, Stripe, and the FAANG names you&#8217;d expect.</p><p>Here&#8217;s what&#8217;s live or in active development:</p><p><strong><a href="https://devopsbeast.com/courses/production-gpu-infrastructure">Production GPU Infrastructure on Kubernetes</a>.</strong> Running GPU workloads at scale. Driver management, MIG, time slicing, multi tenancy, cost optimization, and the architecture decisions that separate a working setup from a production one. This is the course that&#8217;s most relevant if you&#8217;re moving into AI/ML platform work.</p><p><strong><a href="https://devopsbeast.com/courses/llm-operations">LLM Operations for MLOps Engineers</a>.</strong> 30 essential LLM concepts taught through the lens of operating them at scale. Inference serving, RAG architectures, agent infrastructure, hallucination detection, cost engineering. Not how to train models. How to deploy, monitor, and defend them in production.</p><p><strong><a href="https://devopsbeast.com/courses/kubernetes-security">Kubernetes Security</a>.</strong> 40 lessons covering the full attack surface and defense layers. The Kubernetes API, RBAC, STRIDE threat modeling, Pod Security Admission, network policies, runtime detection, supply chain security, incident response. Every lesson starts with a breach scenario and walks through both the attack and the defense.</p><p><strong><a href="https://devopsbeast.com/courses/kubernetes-performance-optimization">Kubernetes Performance Optimization</a>.</strong> The course I wish I&#8217;d had when I first hit &#8220;the cluster is slow&#8221; and didn&#8217;t know where to look. Control plane tuning, etcd performance, scheduler throughput, resource right sizing, CPU throttling, network and storage performance, autoscaling deep dives, and dedicated optimization for EKS, GKE, and AKS.</p><p><strong>Free Courses (Linux Fundamentals plus Networking Fundamentals).</strong> These are the prerequisites for everything else. Free, no email required, available right now.</p><h2>Why This Matters Now</h2><p>DevOps interviews have shifted in the last two years.</p><p>Companies aren&#8217;t asking &#8220;do you know how to write a Dockerfile&#8221; anymore. They&#8217;re asking &#8220;design a multi tenant Kubernetes platform that supports 200 teams with proper isolation, cost attribution, and security boundaries.&#8221; They&#8217;re asking &#8220;your inference latency is 3 seconds and the team needs it under 500ms, diagnose and fix.&#8221; They&#8217;re asking &#8220;we just had a security breach in our CI/CD pipeline, walk me through your incident response.&#8221;</p><p>These are senior staff and principal level questions. And the engineers who answer them well aren&#8217;t the ones who memorized more tools. They&#8217;re the ones who can reason through novel scenarios using frameworks they&#8217;ve internalized.</p><p>That&#8217;s what I&#8217;m trying to teach.</p><h2>The Companion Resources</h2><p>I&#8217;ve also been writing a blog at devopsbeast.com/blog covering debugging scenarios that don&#8217;t fit neatly into a course. The latest one is about Kubernetes certificate expiry, the silent killer that takes down production at 2 AM with no warning. If you&#8217;ve never been hit by it, bookmark that post before you do.</p><p>And I&#8217;m posting interview-related content on LinkedIn most days. Real questions, model answers, common misconceptions. If that&#8217;s useful, follow me there.</p><h2>What&#8217;s Next</h2><p>Over the next few months, I&#8217;ll be filling in lessons, adding new courses (security engineering deep dives, observability for distributed systems, platform engineering interview prep), and publishing more of these debugging blog posts.</p><p>If you&#8217;re preparing for a senior DevOps role or just want to deepen your design-first thinking, head over to devopsbeast.com and explore.</p><p>Thanks for reading. As always, hit reply if you have questions, feedback, or specific topics you want me to cover.</p><p>Sharon</p><p>P.S. <a href="https://devopsbeast.com/">The free courses</a> (Linux Fundamentals, Networking Fundamentals) are genuinely free and don&#8217;t require an email signup. They&#8217;re meant to be useful on their own, even if you never look at the paid stuff. Start there if you want to see how I teach.</p><p>Read devopsbeast blog <a href="https://devopsbeast.com/blog">here</a></p>]]></content:encoded></item><item><title><![CDATA[Why Your GPU Pods Are Pending: Debugging Kubernetes GPU Scheduling]]></title><description><![CDATA[Every reason a GPU pod gets stuck in Pending. Every debug command. Root cause in under 5 minutes.]]></description><link>https://www.kubenatives.com/p/gpu-pod-pending-debugging-kubernetes</link><guid isPermaLink="false">https://www.kubenatives.com/p/gpu-pod-pending-debugging-kubernetes</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 08 May 2026 13:01:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!utF1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your GPU pod has been Pending for 10 minutes. kubectl describe shows:</p><pre><code><code>0/12 nodes are available: 12 Insufficient nvidia.com/gpu.
</code></code></pre><p>You have 12 GPU nodes. nvidia-smi works on all of them. The GPUs are physically there.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!utF1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!utF1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 424w, https://substackcdn.com/image/fetch/$s_!utF1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 848w, https://substackcdn.com/image/fetch/$s_!utF1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 1272w, https://substackcdn.com/image/fetch/$s_!utF1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!utF1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png" width="1344" height="852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:852,&quot;width&quot;:1344,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:202367,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190942565?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!utF1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 424w, https://substackcdn.com/image/fetch/$s_!utF1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 848w, https://substackcdn.com/image/fetch/$s_!utF1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 1272w, https://substackcdn.com/image/fetch/$s_!utF1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38fa623d-38d0-4514-a408-6470d8cf2c99_1344x852.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>So why does Kubernetes think there are no GPUs available?</p><p>There are exactly 7 reasons this happens. This article covers all of them in order of likelihood. Work through them top to bottom. You will find the root cause in under 5 minutes</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s2cF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s2cF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 424w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 848w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 1272w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s2cF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png" width="1338" height="1432" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1432,&quot;width&quot;:1338,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:324326,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190942565?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s2cF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 424w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 848w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 1272w, https://substackcdn.com/image/fetch/$s_!s2cF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7723784c-d048-4aac-a537-c62ce00cc2c6_1338x1432.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Reason 1: All GPUs Are Already Allocated</h2><p>This is the most common cause. And the most misunderstood.</p><p>Kubernetes treats GPUs as integers. When a pod requests <code>nvidia.com/gpu: 1</code>, it gets an entire physical GPU. There is no fractional allocation. A pod using 8GB of an 80GB A100 still consumes 1 full GPU from the allocatable pool.</p><p>Check the actual allocation:</p><pre><code><code># Show allocatable vs allocated GPUs on each node
kubectl describe nodes | grep -A5 "Allocated resources" | grep -B1 "nvidia.com/gpu"
</code></code></pre><p>If allocated equals allocatable on every node, you do not have a scheduling bug. You have a capacity problem.</p><p><strong>The fix:</strong></p><p>Add more GPU nodes. Or enable GPU sharing (MIG, Time-Slicing, or MPS) to run multiple workloads per physical GPU. We covered all three sharing strategies in detail in the MIG vs Time-Slicing vs MPS article.</p><p>Quick capacity check:</p><pre><code><code># Total GPUs in the cluster
kubectl get nodes -l nvidia.com/gpu.present=true \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.nvidia\.com/gpu}{"\n"}{end}'
</code></code></pre><div><hr></div><h2>Reason 2: The GPU Operator Is Not Healthy</h2><p>This is the second most common cause. The NVIDIA GPU Operator has 8 components that form a dependency chain. If any component fails, GPUs do not show up as allocatable resources.</p><pre><code><code># Check GPU Operator pod status
kubectl get pods -n gpu-operator
</code></code></pre><p>Every pod should be Running or Completed. If any pod is in CrashLoopBackOff, Init, or Error, that is your problem.</p><p><strong>The dependency chain:</strong> NFD &#8594; Driver &#8594; Container Toolkit &#8594; Device Plugin &#8594; GFD &#8594; DCGM &#8594; MIG Manager &#8594; Validator.</p><p>The first unhealthy pod in this chain is your root cause. Everything below it is a symptom.</p><p>Common failures:</p><p><strong>Driver pod crashing.</strong> The <code>nouveau</code> kernel module conflicts with the NVIDIA driver. Or the driver container cannot compile kernel modules for your host kernel version. On managed Kubernetes (EKS, GKE, AKS), the platform pre-installs drivers. Set <code>driver.enabled=false</code> in the GPU Operator ClusterPolicy.</p><p><strong>Device plugin not running.</strong> It depends on the container toolkit. If the toolkit did not configure the runtime correctly, the device plugin cannot register GPUs. Fix the toolkit first.</p><p><strong>Validator stuck in Init:0/4.</strong> Do not debug the validator. It is reporting that something upstream failed. Look up the chain.</p><p>We covered all 8 components in detail in the NVIDIA GPU Operator article.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div><hr></div><h2>Reason 3: Node Labels Are Missing</h2><p>The GPU Operator uses node labels to decide where to deploy its components. If the labels are missing, the operator has no targets.</p><pre><code><code># Check for NVIDIA PCI device labels (set by NFD)
kubectl get nodes -l feature.node.kubernetes.io/pci-10de.present=true
</code></code></pre><p>If this returns nothing, Node Feature Discovery is not running. Without it, the GPU Operator does not know which nodes have GPUs.</p><pre><code><code># Check NFD pods
kubectl get pods -n gpu-operator -l app.kubernetes.io/component=worker
</code></code></pre><p>Also check for GPU Feature Discovery labels:</p><pre><code><code># Check GPU-specific labels
kubectl get node &lt;gpu-node&gt; -o json | \
  jq '.metadata.labels | with_entries(select(.key | startswith("nvidia.com")))'
</code></code></pre><p>If you see no <code>nvidia.com/gpu.product</code> or <code>nvidia.com/gpu.count</code> labels, GFD is not running or not healthy.</p><div><hr></div><h2>Reason 4: Taints and Tolerations Mismatch</h2><p>GPU nodes often have taints to prevent non-GPU workloads from being scheduled on them. If your GPU pod does not have the matching toleration, the scheduler rejects it.</p><pre><code><code># Check taints on GPU nodes
kubectl get nodes -l nvidia.com/gpu.present=true \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'
</code></code></pre><p>Common GPU node taints:</p><pre><code><code>nvidia.com/gpu=present:NoSchedule
</code></code></pre><p>Your pod spec needs the matching toleration:</p><pre><code><code>tolerations:
- key: nvidia.com/gpu
  operator: Exists
  effect: NoSchedule
</code></code></pre><p>The describe pod output will tell you if taints are the issue:</p><pre><code><code>kubectl describe pod &lt;pending-pod&gt;
# Look for: "0/12 nodes are available: 12 node(s) had untolerated taint"
</code></code></pre><p>If you see &#8220;untolerated taint&#8221; in the Events section, add the toleration to your pod spec.</p><div><hr></div><h2>Reason 5: Node Affinity Mismatch</h2><p>If your pod requests a specific GPU type using node affinity, and no nodes match, the pod stays Pending.</p><pre><code><code># Example: pod requires H100 but cluster only has A100s
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: nvidia.com/gpu.product
          operator: In
          values:
          - NVIDIA-H100-80GB-HBM3
</code></code></pre><p>Check what GPU types actually exist in your cluster:</p><pre><code><code># List all GPU types across nodes
kubectl get nodes -l nvidia.com/gpu.present=true \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.nvidia\.com/gpu\.product}{"\n"}{end}'
</code></code></pre><p>The describe pod output will show:</p><pre><code><code>0/12 nodes are available: 12 node(s) didn't match Pod's node affinity/selector
</code></code></pre><p><strong>The fix:</strong> Either change the node affinity to match your actual GPU types, or add nodes with the requested GPU type.</p><div><hr></div><h2>Reason 6: Resource Requests Exceed Node Capacity</h2><p>Your pod requests more GPUs than any single node has. Or the pod requests a combination of GPU, CPU, and memory that no node can satisfy.</p><pre><code><code># Check your pod's resource requests
kubectl get pod &lt;pending-pod&gt; -o jsonpath='{.spec.containers[*].resources}'
</code></code></pre><pre><code><code># Check what's available on each GPU node
kubectl describe nodes | grep -A15 "Allocated resources"
</code></code></pre><p>Common scenarios:</p><p>A pod requests <code>nvidia.com/gpu: 4</code> but your nodes only have 2 GPUs each. No single node can satisfy the request.</p><p>A pod requests <code>nvidia.com/gpu: 1</code> and <code>memory: 256Gi</code> but GPU nodes only have 128Gi of RAM. The GPU is available but the memory is not.</p><p>A pod with tensor parallelism requests 8 GPUs. You have 8 GPUs across 4 nodes (2 each). Tensor parallelism requires all GPUs on the same node. No node has 8.</p><p><strong>The fix:</strong> Reduce the resource requests, add larger nodes, or use a different parallelism strategy.</p><div><hr></div><h2>Reason 7: MIG Configuration Mismatch</h2><p>If MIG is enabled on your GPUs, the resource names change. Instead of <code>nvidia.com/gpu</code>, MIG instances are advertised as specific profile resources:</p><pre><code><code>nvidia.com/mig-1g.10gb
nvidia.com/mig-2g.20gb
nvidia.com/mig-3g.40gb
nvidia.com/mig-7g.80gb
</code></code></pre><p>A pod requesting <code>nvidia.com/gpu: 1</code> will not match a MIG-enabled node. The node no longer advertises <code>nvidia.com/gpu</code>. It advertises the MIG profile resources instead.</p><pre><code><code># Check what GPU resources the node advertises
kubectl describe node &lt;gpu-node&gt; | grep nvidia
</code></code></pre><p>If you see <code>nvidia.com/mig-*</code> resources instead of <code>nvidia.com/gpu</code>, your pod needs to request the specific MIG profile:</p><pre><code><code>resources:
  limits:
    nvidia.com/mig-3g.40gb: 1
</code></code></pre><p>The describe pod output is not always clear about this. It will say &#8220;Insufficient nvidia.com/gpu&#8221; even though the real issue is that the resource name has changed because MIG is enabled.</p><div><hr></div><h2>The 5-Minute Debug Script</h2><p>Save this script. Run it first every time a GPU pod is Pending.</p><pre><code><code>#!/bin/bash
echo "=============================="
echo "GPU Pod Pending Debug Script"
echo "=============================="

echo ""
echo "=== 1. Pending GPU Pods ==="
kubectl get pods --all-namespaces --field-selector=status.phase=Pending \
  -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,NODE:.spec.nodeName | head -20

echo ""
echo "=== 2. GPU Operator Health ==="
kubectl get pods -n gpu-operator --no-headers | awk '{print $1, $3}' | grep -v Running | grep -v Completed
NOT_RUNNING=$(kubectl get pods -n gpu-operator --no-headers | awk '{print $3}' | grep -v Running | grep -v Completed | wc -l)
if [ "$NOT_RUNNING" -eq 0 ]; then
  echo "All GPU Operator pods healthy."
else
  echo "WARNING: $NOT_RUNNING GPU Operator pods are NOT healthy."
fi

echo ""
echo "=== 3. GPU Node Labels ==="
kubectl get nodes -l feature.node.kubernetes.io/pci-10de.present=true \
  -o custom-columns=NAME:.metadata.name,GPU:.metadata.labels.nvidia\\.com/gpu\\.product,COUNT:.metadata.labels.nvidia\\.com/gpu\\.count 2&gt;/dev/null
NODE_COUNT=$(kubectl get nodes -l feature.node.kubernetes.io/pci-10de.present=true --no-headers 2&gt;/dev/null | wc -l)
if [ "$NODE_COUNT" -eq 0 ]; then
  echo "WARNING: No nodes with GPU labels found. NFD may not be running."
fi

echo ""
echo "=== 4. GPU Allocation ==="
for node in $(kubectl get nodes -l feature.node.kubernetes.io/pci-10de.present=true -o name 2&gt;/dev/null); do
  echo "--- $node ---"
  kubectl describe $node | grep -A3 "nvidia.com"
done

echo ""
echo "=== 5. GPU Node Taints ==="
kubectl get nodes -l feature.node.kubernetes.io/pci-10de.present=true \
  -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints 2&gt;/dev/null

echo ""
echo "=== 6. Pending Pod Events ==="
PENDING_POD=$(kubectl get pods --all-namespaces --field-selector=status.phase=Pending -o jsonpath='{.items[0].metadata.name}' 2&gt;/dev/null)
PENDING_NS=$(kubectl get pods --all-namespaces --field-selector=status.phase=Pending -o jsonpath='{.items[0].metadata.namespace}' 2&gt;/dev/null)
if [ -n "$PENDING_POD" ]; then
  echo "Events for $PENDING_NS/$PENDING_POD:"
  kubectl describe pod $PENDING_POD -n $PENDING_NS | tail -20
else
  echo "No pending pods found."
fi

echo ""
echo "=============================="
echo "Debug complete."
echo "=============================="
</code></code></pre><p>This script checks all 7 reasons in order. In 30 seconds you know whether the problem is capacity, the GPU Operator, labels, taints, affinity, resources, or MIG configuration.</p><div><hr></div><h2>The Decision Tree</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0FXw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0FXw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 424w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 848w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 1272w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0FXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png" width="1326" height="1132" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1132,&quot;width&quot;:1326,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:236979,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190942565?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0FXw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 424w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 848w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 1272w, https://substackcdn.com/image/fetch/$s_!0FXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e64c37b-4b05-4c62-9290-29ad0501e804_1326x1132.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>When a GPU pod is Pending, follow this exact order:</p><p><strong>Step 1.</strong> Read the Events section in kubectl describe pod. It tells you the scheduler&#8217;s exact reason for rejection.</p><p><strong>Step 2.</strong> If &#8220;Insufficient nvidia.com/gpu&#8221;: check allocation. Are all GPUs already in use?</p><p><strong>Step 3.</strong> If GPUs show 0 allocatable: check the GPU Operator. kubectl get pods -n gpu-operator.</p><p><strong>Step 4.</strong> If GPU Operator pods are healthy but GPUs are not allocatable: check node labels. Is NFD running?</p><p><strong>Step 5.</strong> If &#8220;untolerated taint&#8221;: add the toleration to your pod spec.</p><p><strong>Step 6.</strong> If &#8220;node affinity/selector&#8221;: check what GPU types actually exist vs what the pod requests.</p><p><strong>Step 7.</strong> If MIG is enabled: check that the pod requests the MIG profile resource, not nvidia.com/gpu.</p><p>Start at Step 1. The Events section narrows the search immediately. Do not skip it.</p><div><hr></div><h2>The Bottom Line</h2><p>GPU pods get stuck in Pending for 7 reasons. 6 of them are configuration issues, not hardware problems.</p><p>Read the Events section first. Run the debug script second. The root cause is almost always visible within 30 seconds.</p><p>The hardest part is not finding the problem. It is resisting the urge to blame the scheduler when the answer is sitting in kubectl describe pod.</p><div><hr></div><p><em>Next week: Kubernetes DNS Troubleshooting: CoreDNS, ndots, and the 5-Second Timeout.</em></p><p><em>If you are building GPU infrastructure on Kubernetes, I cover this intersection every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[3-Node HA Setup: Quorum, Split-Brain, and Why the Math Matters]]></title><description><![CDATA[The number 3 is not arbitrary. It is the minimum that makes distributed consensus work.]]></description><link>https://www.kubenatives.com/p/kubernetes-ha-quorum-split-brain</link><guid isPermaLink="false">https://www.kubenatives.com/p/kubernetes-ha-quorum-split-brain</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 01 May 2026 13:02:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xOO0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every production Kubernetes guide tells you to run 3 control plane nodes. Most never explain why.</p><p>It is not about redundancy. You could have redundancy with 2 nodes. It is about quorum. And quorum is the reason your cluster stays consistent when things fail.</p><p>This article explains why 3, what happens with 2, 4, and 5, and the exact failure scenarios you need to plan for.</p><div><hr></div><h2>The Quorum Formula</h2><p>etcd uses the Raft consensus algorithm. Every write must be acknowledged by a majority of members before it is committed.</p><p>The formula:</p><pre><code><code>Quorum = (N / 2) + 1

3 nodes &#8594; quorum = 2 &#8594; tolerates 1 failure
5 nodes &#8594; quorum = 3 &#8594; tolerates 2 failures
7 nodes &#8594; quorum = 4 &#8594; tolerates 3 failures
</code></code></pre><p>The general rule: a cluster of N nodes can tolerate (N - 1) / 2 failures.</p><p>This is why 3 is the minimum for HA. With 3 nodes and a quorum of 2, you can lose 1 node and the cluster keeps accepting writes. With 2 nodes, the quorum is also 2. Lose 1 and you lose quorum. The cluster goes read only.</p><p><strong>2 nodes is worse than 1 node for write availability.</strong> With 1 node, there is no consensus requirement. Writes always succeed (until that node dies). With 2 nodes, both must be healthy for writes to succeed. You added hardware but reduced availability.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div><hr></div><h2>What Happens When You Lose Quorum</h2><p>When etcd loses quorum, it enters a read only state. The API server can still read existing state (pods, services, configurations). But it cannot write.</p><p>This means:</p><p>No new pods can be created. No existing pods can be modified. No Deployments can be scaled. No ConfigMaps can be updated. No new nodes can join. No Secrets can be created or rotated.</p><p>Existing workloads continue running. The kubelet on each node keeps running its containers. Health checks continue. But nothing can change.</p><p>If a running pod crashes during a quorum loss, it will not be restarted by a controller because the controller cannot write the new pod spec to etcd. The kubelet will try to restart the container locally based on the restartPolicy, but the Deployment controller cannot create a replacement.</p><p>This is why quorum loss is a critical incident. The cluster looks alive but is frozen.</p><div><hr></div><h2>Why 3, Not 4</h2><p>4 nodes seems like an improvement over 3. More hardware, more redundancy. But the math tells a different story.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uOzT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uOzT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 424w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 848w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 1272w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uOzT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png" width="839" height="584" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:584,&quot;width&quot;:839,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72869,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190924815?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uOzT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 424w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 848w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 1272w, https://substackcdn.com/image/fetch/$s_!uOzT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ff0599-72ca-4e55-a236-58ec46dbd18c_839x584.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>4 nodes tolerates the same number of failures as 3. You added a node but gained zero additional fault tolerance. You did add more write latency though, because every write now needs 3 acknowledgments instead of 2.</p><p>This is why production clusters use odd numbers: 3, 5, or 7. Even numbers add cost and latency without improving fault tolerance.</p><p><strong>When 5 makes sense.</strong> If losing a single node keeps you up at night because maintenance windows overlap with failures, go to 5. A 5 node cluster tolerates 2 simultaneous failures. This means you can take 1 node down for maintenance and still survive an unexpected failure.</p><p>For most production clusters under 500 nodes, 3 is the right answer. The cost and operational complexity of 5 etcd nodes is only justified when the blast radius of quorum loss is extremely high.</p><div><hr></div><h2>Split-Brain: The Scenario Everyone Fears</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZQ3A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 424w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 848w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 1272w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png" width="839" height="671" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:671,&quot;width&quot;:839,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:94593,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190924815?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 424w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 848w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 1272w, https://substackcdn.com/image/fetch/$s_!ZQ3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff89dbcb4-91bf-485a-b545-b6883bba1d4d_839x671.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Split-brain happens when a network partition divides the cluster into two groups, and both groups think they are the active cluster.</p><p>In traditional systems without consensus, this is catastrophic. Both sides accept writes. When the partition heals, you have conflicting state and no way to automatically reconcile.</p><p>Raft prevents this by design. Here is what actually happens with 3 nodes:</p><p><strong>Scenario: Network partition isolates Node A from Nodes B and C.</strong></p><p>Node A is alone. It has 1 out of 3 members. It cannot form a quorum (needs 2). It stops accepting writes. It becomes read-only.</p><p>Nodes B and C have 2 out of 3 members. They form a quorum. They elect a new leader (if A was the leader). Writing continues normally.</p><p>When the partition heals, Node A rejoins and catches up on all the writes it missed. No conflicting state. No data loss.</p><p><strong>The key insight:</strong> Raft makes split-brain impossible as long as you have an odd number of nodes. The minority side always fails to reach a quorum. The majority side always succeeds. There is never ambiguity about which side is authoritative.</p><p>With an even number (4 nodes), a network partition could create a 2-2 split. Neither side has a quorum. Both sides go read-only. This is another reason odd numbers are better.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hwh4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hwh4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 424w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 848w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 1272w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hwh4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png" width="833" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:833,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87198,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190924815?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hwh4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 424w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 848w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 1272w, https://substackcdn.com/image/fetch/$s_!hwh4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426e57cc-2e1f-42b8-9652-6f7d6076f14e_833x582.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>The 3 Node Architecture</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uXcD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uXcD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 424w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 848w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 1272w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uXcD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png" width="1456" height="1080" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1080,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:229181,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190924815?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uXcD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 424w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 848w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 1272w, https://substackcdn.com/image/fetch/$s_!uXcD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63321ecb-d761-4d1a-900e-55b01a500a94_1688x1252.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The load balancer distributes API requests across all 3 API servers. The API servers are stateless. Anyone can handle any request.</p><p>etcd runs on all 3 nodes (stacked topology). One etcd member is the leader. Writes go to the leader and are replicated to followers.</p><p>The scheduler and controller manager use leader election. Only one instance is active at a time. If the active instance dies, another takes over within seconds.</p><p><strong>The load balancer is critical.</strong> Without it, kubectl and the kubelets point at a single API server IP. If that node goes down, nothing can reach the control plane even though 2 healthy nodes are still running.</p><p>Use a Layer 4 (TCP) load balancer. Do not use Layer 7 (HTTP). The API server handles its own TLS. Health check endpoint: <code>/healthz</code> on port 6443.</p><div><hr></div><h2>Stacked vs External in HA Context</h2><p>In the architecture above, etcd runs on the same nodes as the API server. This is stacked topology.</p><p>External topology separates etcd onto its own dedicated nodes:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xOO0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xOO0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 424w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 848w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 1272w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xOO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png" width="1456" height="1369" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1369,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:301787,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190924815?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xOO0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 424w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 848w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 1272w, https://substackcdn.com/image/fetch/$s_!xOO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7f759af-0fe5-4977-8552-c0ad42484dce_1664x1564.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>External topology means 6 nodes instead of 3. More cost. More complexity. But etcd gets a dedicated disk and CPU. No resource contention with the API server.</p><p><strong>Which one for HA?</strong> Both provide quorum protection. The difference is performance under load, not availability.</p><p>Stacked is fine for clusters with fewer than 200 nodes. Simple to set up. Kubeadm supports it natively.</p><p>External becomes necessary when etcd disk latency degrades because the API server is consuming the same I/O bandwidth. We covered this in detail in the Stacked vs External etcd article.</p><div><hr></div><h2>Failure Scenarios: What Actually Happens</h2><h3>Scenario 1: One node goes down (expected)</h3><p>Quorum is maintained (2 of 3). A new etcd leader is elected if the failed node was the leader. The scheduler and controller manager fail over if they were active on the failed node. API requests continue through the load balancer to the remaining 2 nodes.</p><p><strong>Impact:</strong> Brief spike in API latency during leader election (typically under 5 seconds). No service disruption.</p><h3>Scenario 2: Two nodes go down simultaneously</h3><p>Quorum is lost (1 of 3). etcd becomes read only. The API server can read state but cannot write. No new pods, deployments, or changes.</p><p>Existing workloads keep running. The kubelet on worker nodes continues managing its containers. But nothing can be updated or replaced.</p><p><strong>Recovery:</strong> Bring at least 1 node back online to restore quorum. etcd will automatically re-form consensus.</p><h3>Scenario 3: etcd data corruption on one node</h3><p>The corrupted member falls behind. etcd detects the inconsistency through Raft log verification. The member stops participating in consensus.</p><p><strong>Recovery:</strong> Remove the corrupted member from the cluster. Provision a new node. Add it as a new etcd member. It will automatically replicate data from the healthy members.</p><pre><code><code># Remove the bad member
etcdctl member remove MEMBER_ID

# Add a new member
etcdctl member add new-node --peer-urls=https://NEW_IP:2380

# Start etcd on the new node with the --initial-cluster-state=existing flag
</code></code></pre><h3>Scenario 4: Disk fills up on one node</h3><p>etcd performance degrades as the disk fills. Write latency increases. If the etcd database hits its storage quota, that member triggers a NOSPACE alarm.</p><p>If only one member hits NOSPACE, the cluster continues (2 of 3 are healthy). But you should act immediately because the remaining members are likely on the same trajectory.</p><p><strong>Recovery:</strong> Follow the NOSPACE runbook (compact, defrag, disarm alarm). Then investigate why disk usage grew and fix the root cause.</p><h3>Scenario 5: Control plane node scheduled for maintenance</h3><p>Drain the node&#8217;s workloads (if it also runs worker pods). The other 2 nodes maintain quorum. Perform maintenance. Bring the node back.</p><p><strong>Important:</strong> Never take 2 nodes down for maintenance simultaneously. With 3 nodes, losing 2 means quorum loss. Always verify the first node is healthy and has rejoined the etcd cluster before starting maintenance on the second.</p><pre><code><code># Verify cluster health before maintenance
etcdctl endpoint health --cluster
etcdctl endpoint status --write-out=table
</code></code></pre><div><hr></div><h2>The Health Checks You Need</h2><h3>Daily automated check</h3><pre><code><code>#!/bin/bash
# etcd-health-check.sh

CERTS="--cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key"

echo "=== Cluster Health ==="
ETCDCTL_API=3 etcdctl endpoint health --cluster $CERTS

echo ""
echo "=== Member Status ==="
ETCDCTL_API=3 etcdctl endpoint status --cluster --write-out=table $CERTS

echo ""
echo "=== Active Alarms ==="
ETCDCTL_API=3 etcdctl alarm list $CERTS
</code></code></pre><h3>Prometheus alerts for quorum</h3><pre><code><code>groups:
- name: etcd-quorum
  rules:
  - alert: EtcdMemberDown
    expr: up{job="etcd"} == 0
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "etcd member {{ $labels.instance }} is down"
      description: "With 3 members, losing 1 means you are one failure from quorum loss."

  - alert: EtcdInsufficientMembers
    expr: count(up{job="etcd"} == 1) &lt; 2
    for: 1m
    labels:
      severity: page
    annotations:
      summary: "etcd cluster has lost quorum"
      description: "Fewer than 2 etcd members are healthy. Cluster is read-only."
</code></code></pre><div><hr></div><h2>The Bottom Line</h2><p>The number 3 is not arbitrary. It is the minimum required for distributed consensus to work with fault tolerance.</p><p>3 nodes, quorum of 2, tolerates 1 failure. Add a load balancer in front. Use odd numbers. Never take 2 nodes down at the same time.</p><p>If you understand quorum, you understand why your cluster survives node failures. If you do not, you will learn the hard way at 3 AM.</p><div><hr></div><p><em>Next week: Why Your GPU Pods Are Pending: Debugging Kubernetes GPU Scheduling.</em></p><p><em>If you are running production Kubernetes clusters, I cover control plane internals, GPU infrastructure, and model serving every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Production Case Study: The vLLM Pod That Only OOMed at 3 AM]]></title><description><![CDATA[A 5-week investigation into a memory failure that ignored every rule we knew about LLM inference. The root cause changed how we think about KV cache management.]]></description><link>https://www.kubenatives.com/p/vllm-production-case-study-3am-oom-investigation</link><guid isPermaLink="false">https://www.kubenatives.com/p/vllm-production-case-study-3am-oom-investigation</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Wed, 29 Apr 2026 13:03:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xmmo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb927a134-6575-4105-b12e-de3c547209a6_1674x990.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>The Symptom</strong></h2><p>A multi-tenant LLM inference platform serving roughly a dozen production workloads on H100 nodes. Standard setup: vLLM behind KServe, KServe behind Istio, autoscaling driven by request rate and queue depth.</p><p>The platform had been stable for months. Then it started OOMing.</p><p>Not constantly. Not predictably. Just sometimes, in the early morning hours UTC, one of the vLLM pods would go OOMKilled. Exit code 137. Always between 2 AM and 4 AM. Never the same pod twice in a row. Service recovered within 60 seconds because the deployment had multiple replicas, but the on-call alert woke up an engineer every time.</p><p>For the first week we did what every team does. We assumed it was traffic. We pulled the request rate metrics. The 3 AM window was the lowest-traffic period of the day. Less than 5% of peak QPS.</p><p>That was the first signal that this was not a normal OOM.</p><div><hr></div><h2>The Easy Explanations That Were Wrong</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p9Ld!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p9Ld!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 424w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 848w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 1272w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p9Ld!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png" width="1456" height="1204" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1204,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:306162,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/195438486?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p9Ld!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 424w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 848w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 1272w, https://substackcdn.com/image/fetch/$s_!p9Ld!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cae0d60-1544-4742-a219-01feb68c10e4_1664x1376.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A week of investigation eliminated the obvious causes one by one.</p><p><strong>Hypothesis 1: A specific request was blowing up memory.</strong></p><p>We grabbed the request logs from the 5 minutes before each OOM event. No common pattern. Different models, different prompt sizes, different output lengths. Some events had no requests in flight at all when the OOM fired. Ruled out.</p><p><strong>Hypothesis 2: Memory leak accumulating until breakpoint.</strong></p><p>We graphed <code>container_memory_working_set_bytes</code> for each pod over 24 hours. Memory was stable. Pods that had been running for 30 hours had the same memory footprint as pods that had been running for 2. There was no slow growth. The OOM happened from a stable baseline.</p><p><strong>Hypothesis 3: Bad model image with a regression.</strong></p><p>Rolled back to the previous vLLM version. OOMs continued at the same rate. Rolled forward. Same. The vLLM version was not the variable.</p><p><strong>Hypothesis 4: Noisy neighbor on the host.</strong></p><p>Checked DCGM metrics for all GPUs on the same physical node during OOM events. No correlated GPU memory pressure or compute contention. The OOMing pod was on a node where every other GPU workload was idle or low utilization.</p><p><strong>Hypothesis 5: Resource limit set too low.</strong></p><p>This is where most teams stop. They raise the memory limit by 25% and call it solved. We tried it. The OOMs moved later in the night, then resumed at the new threshold a few days later. Higher limit, same problem, slower recurrence.</p><p>That last one was the breakthrough, even though we did not know it yet. The fact that raising the limit only delayed the OOM rather than preventing it meant something was actively growing memory. We just could not see what.</p><p></p>
      <p>
          <a href="https://www.kubenatives.com/p/vllm-production-case-study-3am-oom-investigation">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Production Kubernetes Debugging: A Systematic Framework]]></title><description><![CDATA[A systematic framework for debugging Kubernetes in production. Five layers from application to hardware, with the exact commands for each layer.]]></description><link>https://www.kubenatives.com/p/production-kubernetes-debugging-framework</link><guid isPermaLink="false">https://www.kubenatives.com/p/production-kubernetes-debugging-framework</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Fri, 24 Apr 2026 13:02:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rTUq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Something is wrong with your cluster.</p><p>Pods are stuck. Deployments are failing. API requests are slow. Users are complaining.</p><p>You open a terminal and start running commands. kubectl get pods. kubectl describe pod. kubectl logs. You scroll through the output looking for something that stands out.</p><p>Twenty minutes later, you&#8217;re deep in a rabbit hole, debugging a network policy that has nothing to do with the actual problem.</p><p>This is how most engineers debug Kubernetes. Randomly. They start with whatever command comes to mind first and hope to stumble on the root cause.</p><p>There is a better way. A systematic framework that works for every Kubernetes problem. It starts at the top of the stack and works down through five layers. Each layer has specific symptoms, specific commands, and a clear signal indicating whether to stay at that layer or move to the next.</p><div><hr></div><h2>The Five Layer Model</h2><p>Every Kubernetes problem lives at one of five layers. The layers are ordered from most common to least common. Start at Layer 1 and work down. Most problems resolve in the first two layers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y9Vj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y9Vj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 424w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 848w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 1272w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y9Vj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png" width="825" height="894" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80596553-4c00-473f-bf95-9effd7159b64_825x894.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:894,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:175720,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190276390?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!y9Vj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 424w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 848w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 1272w, https://substackcdn.com/image/fetch/$s_!y9Vj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80596553-4c00-473f-bf95-9effd7159b64_825x894.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Layer 1: Application.</strong> The container itself is broken. Bad config, missing env vars, crashed process, OOM.</p><p><strong>Layer 2: Pod Scheduling.</strong> The pod can&#8217;t get placed on a node. Resource limits, taints, affinity rules, node capacity.</p><p><strong>Layer 3: Networking.</strong> The pod is running, but can&#8217;t communicate. DNS failures, service misconfig, network policies, and ingress issues.</p><p><strong>Layer 4: Cluster Infrastructure.</strong> The control plane is degraded. etcd performance, API server latency, scheduler delays, and certificate expiry.</p><p><strong>Layer 5: Node and Hardware.</strong> The underlying node is unhealthy. Disk pressure, memory pressure, kubelet issues, and GPU driver failures.</p><p>The framework works because Kubernetes problems almost always manifest at the application layer first. A pod crashes. A deployment doesn&#8217;t roll out. A request times out. The root cause might be at any layer, but the symptoms always show up at the top.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rTUq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rTUq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 424w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 848w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 1272w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rTUq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png" width="831" height="739" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:739,&quot;width&quot;:831,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:117975,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190276390?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rTUq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 424w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 848w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 1272w, https://substackcdn.com/image/fetch/$s_!rTUq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fadee4e-a009-44fc-ae0f-1027fc79ddbd_831x739.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Layer 1: Application Debugging</h2><p>This is where 60% of production issues live. The container is doing something wrong. Before blaming Kubernetes, check the application.</p><h3>The first three commands</h3><p>Run these in order for any pod that isn&#8217;t healthy:</p><pre><code><code># 1. What is the pod doing right now?
kubectl get pod &lt;pod-name&gt; -o wide

# 2. What happened to it?
kubectl describe pod &lt;pod-name&gt;

# 3. What is the application saying?
kubectl logs &lt;pod-name&gt; --tail=100
</code></code></pre><p>The <code>get pod</code> output tells you the current state. Is it Running, Pending, CrashLoopBackOff, Error, or ImagePullBackOff? Each state points to a different problem.</p><p>The <code>describe pod</code> output tells you the history. Look at the Events section at the bottom. Read it from bottom to top. The first event is usually the trigger.</p><p>The <code>logs</code> output tells you what the application thinks is happening. If the container crashed, use <code>--previous</code> to see the last run&#8217;s logs before the crash.</p><pre><code><code>kubectl logs &lt;pod-name&gt; --previous --tail=100
</code></code></pre><h3>CrashLoopBackOff</h3><p>This is the most common pod failure. The container starts, crashes, restarts, crashes again. Kubernetes backs off the restart interval exponentially.</p><p>The root cause is almost always in the application logs. Check:</p><pre><code><code># See the exit code
kubectl get pod &lt;pod-name&gt; -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'
</code></code></pre><p>Exit code 1 means the application crashed on its own. Check logs for the error.</p><p>Exit code 137 means Kubernetes killed the container. It ran out of memory (OOMKilled). Check:</p><pre><code><code>kubectl describe pod &lt;pod-name&gt; | grep -i oom
</code></code></pre><p>If it was OOMKilled, the fix is either increasing the memory limit or fixing the memory leak in the application.</p><p>Exit code 143 means the container received SIGTERM. Kubernetes asked it to stop gracefully. This happens during rollouts, scaling, or node drains.</p><h3>ImagePullBackOff</h3><p>The container image can&#8217;t be downloaded. Check:</p><pre><code><code>kubectl describe pod &lt;pod-name&gt; | grep -A5 "Events"
</code></code></pre><p>Common causes: wrong image name, wrong tag, private registry without image pull secrets, or the registry is down.</p><pre><code><code># Check if image pull secrets are configured
kubectl get pod &lt;pod-name&gt; -o jsonpath='{.spec.imagePullSecrets}'
</code></code></pre><h3>Readiness and Liveness Probes</h3><p>A pod is Running but not receiving traffic. The readiness probe is failing.</p><pre><code><code># Check probe configuration and recent failures
kubectl describe pod &lt;pod-name&gt; | grep -A10 "Readiness\|Liveness"
</code></code></pre><p>Common mistake: the readiness probe checks an endpoint that takes 30 seconds to respond, but the timeout is set to 1 second. The pod is healthy but Kubernetes thinks it isn&#8217;t.</p><h3>The signal to move to Layer 2</h3><p>If <code>kubectl describe pod</code> shows the pod is Pending (not Running, not CrashLoopBackOff), the problem isn&#8217;t the application. The pod hasn&#8217;t been scheduled yet. Move to Layer 2.</p><div><hr></div><h2>Layer 2: Pod Scheduling</h2><p>The pod exists but it&#8217;s stuck in Pending. Kubernetes can&#8217;t find a node to run it on.</p><h3>The diagnostic command</h3><pre><code><code>kubectl describe pod &lt;pod-name&gt; | grep -A20 "Events"
</code></code></pre><p>The Events section tells you exactly why the scheduler rejected the pod. The message will say something like:</p><p><code>0/12 nodes are available: 6 Insufficient cpu, 4 node(s) had taint, 2 node(s) didn't match pod affinity.</code></p><p>Read this carefully. It tells you how many nodes exist, how many were filtered, and why each one was rejected.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/production-kubernetes-debugging-framework?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Kubenatives! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/p/production-kubernetes-debugging-framework?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.kubenatives.com/p/production-kubernetes-debugging-framework?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p><h3>Insufficient resources</h3><pre><code><code># Check available resources across all nodes
kubectl top nodes

# Check a specific node's allocation
kubectl describe node &lt;node-name&gt; | grep -A15 "Allocated resources"
</code></code></pre><p>Compare the pod&#8217;s resource requests against what&#8217;s available. If the pod requests 4 CPU and 16Gi memory, but no node has that much free, the pod stays Pending.</p><p>The fix is either reducing the pod&#8217;s resource requests, adding more nodes, or cleaning up unused workloads to free resources.</p><h3>Taints and tolerations</h3><p>Nodes can have taints that repel pods. The pod needs a matching toleration to land on a tainted node. GPU nodes almost always have taints.</p><pre><code><code># Check node taints
kubectl describe node &lt;node-name&gt; | grep -A3 "Taints"

# Check pod tolerations
kubectl get pod &lt;pod-name&gt; -o jsonpath='{.spec.tolerations}' | jq .
</code></code></pre><p>If the node has a taint and the pod doesn&#8217;t have a matching toleration, the scheduler will skip that node.</p><h3>Node selectors and affinity</h3><pre><code><code># Check what the pod requires
kubectl get pod &lt;pod-name&gt; -o jsonpath='{.spec.nodeSelector}' | jq .
kubectl get pod &lt;pod-name&gt; -o jsonpath='{.spec.affinity}' | jq .

# Check what nodes have
kubectl get nodes --show-labels | grep &lt;expected-label&gt;
</code></code></pre><p>If the pod requires <code>gpu-type=a100</code> but no node has that label, the pod stays Pending forever.</p><h3>PersistentVolumeClaim binding</h3><pre><code><code>kubectl get pvc -n &lt;namespace&gt;
</code></code></pre><p>If the PVC status is Pending, the pod can&#8217;t start because its storage isn&#8217;t ready. Check the PVC events:</p><pre><code><code>kubectl describe pvc &lt;pvc-name&gt; -n &lt;namespace&gt; | grep -A10 "Events"
</code></code></pre><h3>The signal to move to Layer 3</h3><p>If the pod is Running but the service isn&#8217;t working (requests fail, connections time out, DNS doesn&#8217;t resolve), the problem is networking. Move to Layer 3.</p><div><hr></div><h2>Layer 3: Networking</h2><p>The pod is running. The application is healthy. But traffic isn&#8217;t reaching it. Or it can&#8217;t reach other services.</p><h3>Service connectivity</h3><p>First, verify the service exists and has endpoints:</p><pre><code><code># Check the service
kubectl get svc &lt;service-name&gt; -n &lt;namespace&gt;

# Check if the service has endpoints (pods backing it)
kubectl get endpoints &lt;service-name&gt; -n &lt;namespace&gt;
</code></code></pre><p>If endpoints shows zero addresses, the service selector doesn&#8217;t match any running pods. Compare the service selector with the pod labels:</p><pre><code><code># Service selector
kubectl get svc &lt;service-name&gt; -o jsonpath='{.spec.selector}'

# Pod labels
kubectl get pods -n &lt;namespace&gt; --show-labels
</code></code></pre><h3>DNS resolution</h3><p>The most common networking issue in Kubernetes. The pod can&#8217;t resolve service names.</p><pre><code><code># Test DNS from inside a pod
kubectl exec -it &lt;pod-name&gt; -- nslookup &lt;service-name&gt;
kubectl exec -it &lt;pod-name&gt; -- nslookup &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local
</code></code></pre><p>If DNS fails, check CoreDNS:</p><pre><code><code># Is CoreDNS running?
kubectl get pods -n kube-system -l k8s-app=kube-dns

# CoreDNS logs
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
</code></code></pre><p>A common cause of slow DNS is the <code>ndots</code> setting. By default, Kubernetes adds <code>ndots:5</code> to resolv.conf, which means any name with fewer than 5 dots gets appended with search domains before the actual lookup. A simple lookup for <code>api.example.com</code> generates 4 failed queries before the real one succeeds.</p><p>The fix:</p><pre><code><code>spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"
</code></code></pre><h3>Network policies</h3><p>If you have network policies in your cluster, they might be blocking traffic between pods.</p><pre><code><code># List network policies in the namespace
kubectl get networkpolicies -n &lt;namespace&gt;

# Describe a specific policy
kubectl describe networkpolicy &lt;policy-name&gt; -n &lt;namespace&gt;
</code></code></pre><p>A missing egress rule means the pod can&#8217;t make outbound connections. A missing ingress rule means nothing can connect to the pod. An empty pod selector <code>{}</code> applies to all pods in the namespace.</p><h3>Testing connectivity</h3><pre><code><code># Test pod to pod connectivity
kubectl exec -it &lt;pod-a&gt; -- curl -v http://&lt;pod-b-ip&gt;:&lt;port&gt;

# Test pod to service connectivity
kubectl exec -it &lt;pod-a&gt; -- curl -v http://&lt;service-name&gt;:&lt;port&gt;

# Test pod to external connectivity
kubectl exec -it &lt;pod-a&gt; -- curl -v https://httpbin.org/get
</code></code></pre><h3>The signal to move to Layer 4</h3><p>If all pods are slow (not just one service), if kubectl itself is slow, or if you see <code>etcdserver: request timed out</code> in logs, the problem is the control plane. Move to Layer 4.</p><div><hr></div><h2>Layer 4: Cluster Infrastructure</h2><p>The control plane is degraded. This affects everything in the cluster, not just one application.</p><h3>Symptoms</h3><p>kubectl commands take 5+ seconds. Deployments don&#8217;t roll out. Pod creation is delayed. Controller reconciliation falls behind. Events show <code>etcdserver: request timed out</code>.</p><h3>API server health</h3><pre><code><code># Check API server response time
time kubectl get nodes

# Check API server metrics (if accessible)
kubectl get --raw /metrics | grep apiserver_request_duration_seconds

# Check API server logs
kubectl logs -n kube-system kube-apiserver-&lt;node&gt; --tail=50
</code></code></pre><p>If the API server is slow, the cause is almost always etcd. The API server is stateless. etcd is not.</p><h3>etcd health</h3><pre><code><code># Quick health check
etcdctl endpoint health --cluster \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Detailed status
etcdctl endpoint status --write-out=table --cluster \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key
</code></code></pre><p>Check the metrics that predict etcd failures:</p><p><code>etcd_disk_wal_fsync_duration_seconds</code> p99 above 10ms means disk latency. <code>etcd_mvcc_db_total_size_in_bytes</code> approaching the quota means NOSPACE is coming. <code>etcd_server_leader_changes_seen_total</code> above 1 per hour means instability.</p><p>We covered all five etcd failure modes in detail in our etcd debugging guide.</p><h3>Certificate expiry</h3><pre><code><code>kubeadm certs check-expiration
</code></code></pre><p>If certificates expire, everything breaks at once. Existing pods keep running from kubelet cache. But nothing new can be created, updated, or deleted.</p><h3>Scheduler health</h3><pre><code><code># Check scheduler logs
kubectl logs -n kube-system kube-scheduler-&lt;node&gt; --tail=30

# Check if scheduler is falling behind
kubectl get --raw /metrics | grep scheduler_scheduling_attempt_duration_seconds
</code></code></pre><h3>The signal to move to Layer 5</h3><p>If specific nodes show problems (NotReady status, high resource usage, kubelet errors) but the control plane is healthy, the issue is at the node level. Move to Layer 5.</p><div><hr></div><h2>Layer 5: Node and Hardware</h2><p>Individual nodes are unhealthy. This only affects pods running on those specific nodes.</p><h3>Node status</h3><pre><code><code># Check all node statuses
kubectl get nodes

# Look for conditions on a specific node
kubectl describe node &lt;node-name&gt; | grep -A10 "Conditions"
</code></code></pre><p>The Conditions section shows:</p><p>MemoryPressure: the node is running out of RAM. DiskPressure: the node is running out of disk. PIDPressure: the node has too many processes. Ready: False means the kubelet can&#8217;t communicate with the API server.</p><h3>Kubelet health</h3><pre><code><code># Check kubelet status on the node
systemctl status kubelet

# Kubelet logs
journalctl -u kubelet --tail=50
</code></code></pre><p>Common kubelet issues: certificate expired, container runtime not responding, disk full on the node.</p><h3>GPU specific issues</h3><p>For GPU nodes, check the GPU Operator components:</p><pre><code><code># Are all GPU Operator pods running?
kubectl get pods -n gpu-operator -o wide

# Can the node see GPUs?
kubectl describe node &lt;gpu-node&gt; | grep nvidia.com/gpu

# Check nvidia-smi on the node
kubectl debug node/&lt;gpu-node&gt; -it --image=nvidia/cuda:12.0-base -- nvidia-smi
</code></code></pre><p>If <code>nvidia-smi</code> fails, the GPU driver isn&#8217;t loaded. Check the driver container in the GPU Operator.</p><p>We covered the full GPU Operator debugging path in our GPU Operator article.</p><h3>Disk pressure</h3><pre><code><code># Check disk usage on the node
kubectl debug node/&lt;node&gt; -it --image=ubuntu -- df -h

# Check container image storage
kubectl debug node/&lt;node&gt; -it --image=ubuntu -- du -sh /var/lib/containerd
</code></code></pre><p>Old container images and unused layers accumulate over time. Kubernetes garbage collection should handle this, but sometimes it falls behind.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n0S_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n0S_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 424w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 848w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 1272w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n0S_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png" width="822" height="849" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:849,&quot;width&quot;:822,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:128272,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/190276390?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n0S_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 424w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 848w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 1272w, https://substackcdn.com/image/fetch/$s_!n0S_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb7bae90-d4dd-4803-9566-ecd7f9b5ad71_822x849.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The Quick Reference Checklist</h2><p>When something breaks in production, run through this sequence:</p><pre><code><code>1. kubectl get pods -n &lt;namespace&gt;
   &#8594; What state are the affected pods in?

2. If CrashLoopBackOff or Error:
   &#8594; kubectl logs &lt;pod&gt; --previous --tail=100
   &#8594; Layer 1: Application issue

3. If Pending:
   &#8594; kubectl describe pod &lt;pod&gt; (read Events)
   &#8594; Layer 2: Scheduling issue

4. If Running but not working:
   &#8594; kubectl exec &lt;pod&gt; -- curl &lt;service&gt;
   &#8594; kubectl exec &lt;pod&gt; -- nslookup &lt;service&gt;
   &#8594; Layer 3: Networking issue

5. If everything is slow:
   &#8594; time kubectl get nodes
   &#8594; etcdctl endpoint health --cluster
   &#8594; Layer 4: Control plane issue

6. If specific node problems:
   &#8594; kubectl describe node &lt;node&gt; (check Conditions)
   &#8594; systemctl status kubelet
   &#8594; Layer 5: Node/hardware issue
</code></code></pre><p>This sequence takes 2 minutes. It eliminates 80% of possible causes and points you at the right layer immediately. No more guessing.</p><div><hr></div><h2>The Debugging Mindset</h2><p>Three rules that make debugging faster:</p><p><strong>Rule 1: Read the Events.</strong> Every kubectl describe output has an Events section. Read it. From bottom to top. The events tell you what Kubernetes already knows about the problem. Most engineers skip this and start guessing.</p><p><strong>Rule 2: Check one layer at a time.</strong> Don&#8217;t jump between application logs, network policies, and etcd metrics in the same debugging session. Start at Layer 1. If the evidence points to a different layer, move there deliberately. Randomized debugging wastes time.</p><p><strong>Rule 3: Reproduce before you fix.</strong> If you can&#8217;t reproduce the problem on demand, you don&#8217;t understand it yet. A fix applied without understanding the root cause is just a workaround that will break again later.</p><div><hr></div><h2>What This Framework Connects To</h2><p>This article is the anchor for production debugging at KubeNatives. Every specific debugging guide links back here:</p><p>Our etcd debugging guide covers Layer 4 in depth: the 5 ways etcd breaks and the metrics that predict each failure.</p><p>Our GPU Operator article covers Layer 5 for GPU nodes: the 8 components and the initialization dependency chain.</p><p>Our DNS troubleshooting guide (coming soon) will cover Layer 3 in depth: CoreDNS, ndots, and the 5 second timeout problem.</p><p>Each supporting article gives you the deep dive for a specific problem. This framework tells you which article to reach for.</p><div><hr></div><p><em>Next week: Deploying vLLM on Kubernetes: From Single Pod to Production.</em></p><p><em>If you&#8217;re running production Kubernetes, I cover control plane operations, GPU infrastructure, and model serving every week. Subscribe at kubenatives.com.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Production Runbook: vLLM OOMKilled Recovery]]></title><description><![CDATA[When your inference pod dies mid-request with exit code 137. What to check, what to fix, and how to stop it from happening again.]]></description><link>https://www.kubenatives.com/p/vllm-oomkilled-recovery-kubernetes-runbook</link><guid isPermaLink="false">https://www.kubenatives.com/p/vllm-oomkilled-recovery-kubernetes-runbook</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Wed, 22 Apr 2026 16:43:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!GknI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Severity:</strong> High (production inference down) <strong>Audience:</strong> On call engineer <strong>Prerequisites:</strong> kubectl access, namespace admin, GPU node SSH if needed <strong>Time to resolve:</strong> 15 to 45 minutes</p><div><hr></div><h2>Symptom</h2><p>Your vLLM pod restarted during normal traffic. Users saw 503 errors for the duration of the restart. The pod eventually came back but might OOM again on the next large request.</p><p><strong>Signals you are in this runbook:</strong></p><pre><code><code>$ kubectl get pod vllm-0
NAME      READY   STATUS      RESTARTS   AGE
vllm-0    1/1     Running     3          2h

$ kubectl describe pod vllm-0 | grep -A3 "Last State"
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
</code></code></pre><p>Exit code 137 means the container received SIGKILL from the kernel OOM killer. Not from a crash. Not from vLLM code. The kernel decided the container used too much memory and killed it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GknI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GknI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 424w, https://substackcdn.com/image/fetch/$s_!GknI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 848w, https://substackcdn.com/image/fetch/$s_!GknI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 1272w, https://substackcdn.com/image/fetch/$s_!GknI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GknI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png" width="1456" height="1386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1386,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:336038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/195050864?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GknI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 424w, https://substackcdn.com/image/fetch/$s_!GknI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 848w, https://substackcdn.com/image/fetch/$s_!GknI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 1272w, https://substackcdn.com/image/fetch/$s_!GknI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa55c52c-4ef9-4a0a-ae85-28b71a0931c4_1672x1592.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Quick Triage: Is This GPU Memory or Host Memory?</h2><p>This is the first branch. vLLM has two memory failure modes and they need different fixes.</p><p><strong>Check pod events:</strong></p><pre><code><code>kubectl describe pod vllm-0 | grep -A2 -i "oom\|killed"
</code></code></pre><p><strong>If you see &#8220;Memory cgroup out of memory&#8221; in kubelet events:</strong> This is <strong>host memory</strong> OOM. The container exceeded its <code>resources.limits.memory</code>. Jump to Procedure A.</p><p><strong>If you see &#8220;CUDA out of memory&#8221; or &#8220;torch.cuda.OutOfMemoryError&#8221; in vLLM logs:</strong> This is <strong>GPU memory</strong> OOM. The model tried to allocate more VRAM than available on the device. Jump to Procedure B.</p><p><strong>If you see both or cannot tell:</strong> Pull the last 200 lines of logs from the previous container:</p><pre><code><code>kubectl logs vllm-0 --previous --tail=200 | grep -iE "oom|memory|cuda|killed"
</code></code></pre><p>Look for the first memory related error. That is the trigger. Everything after is cascade.</p><div><hr></div><h2>Procedure A: Host Memory OOM (exit 137, kernel killed the container)</h2><p><strong>What happened:</strong> the container exceeded <code>resources.limits.memory</code>. Kubernetes killed it.</p><p><strong>Root causes, ranked by frequency:</strong></p><ol><li><p>Memory limit set too low for the model size (most common)</p></li><li><p>Prefix caching or KV cache overflow into host memory via swap or CPU offload</p></li><li><p>Memory leak in vLLM (rare, usually requires version upgrade)</p></li></ol><h3>Step 1: Confirm the limit violation</h3><pre><code><code># What was the memory limit?
kubectl get pod vllm-0 -o jsonpath='{.spec.containers[0].resources.limits.memory}'
# Example output: 32Gi

# What did it actually use before death?
kubectl top pod vllm-0 --containers 2&gt;/dev/null || echo "metrics-server needed"
</code></code></pre><p>If limits are 32Gi and a 70B model needs host memory to mirror the weights during load, you will hit the limit on startup.</p><p></p>
      <p>
          <a href="https://www.kubenatives.com/p/vllm-oomkilled-recovery-kubernetes-runbook">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Ajay on why most IDPs fail (workshop this Saturday)]]></title><description><![CDATA[A short Q&A with Ajay Chankramath on when teams are ready for an IDP, how AI workloads break the standard patterns, and a workshop worth your Saturday.]]></description><link>https://www.kubenatives.com/p/ajay-on-why-most-idps-fail-workshop</link><guid isPermaLink="false">https://www.kubenatives.com/p/ajay-on-why-most-idps-fail-workshop</guid><dc:creator><![CDATA[Sharon Sahadevan]]></dc:creator><pubDate>Tue, 21 Apr 2026 13:02:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sAF8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>A short Q&amp;A with Ajay Chankramath on when teams are ready for an IDP, how AI workloads break the standard patterns, and a workshop worth your Saturday.</em></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sAF8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sAF8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 424w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 848w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 1272w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sAF8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:215194,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.kubenatives.com/i/194780644?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sAF8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 424w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 848w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 1272w, https://substackcdn.com/image/fetch/$s_!sAF8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72ab3377-9e7e-49d2-bb8f-fbd702204bd2_1280x640.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Most weeks you get a technical deep dive from me on Fridays. Today is different.</p><p>I want to put a workshop on your radar that I think is worth your Saturday.</p><p>Internal Developer Platforms have been the dominant platform engineering conversation for two years now. Most teams I talk to are either building one badly, buying one they do not fully understand, or avoiding the topic because they have seen too many failed platform projects.</p><p>The pattern is consistent. Teams start with a portal (usually Backstage) and work backwards into the underlying platform. That order is wrong. It is why so many IDPs end up as another bottleneck instead of a force multiplier.</p><p>Ajay Chankramath runs Platformetrics and previously led Platform Engineering at Thoughtworks. He is running a two day workshop on April 25 and 26 on building an AI powered IDP from scratch. I asked him a few questions on the stuff most teams get wrong.</p><p><strong>When is a team actually ready to build an IDP?</strong></p><p>Ajay: When you can name your top three developer friction points based on data, not gut feeling. If you have not watched a developer go through onboarding end to end, you are not ready to build the platform. Do not start building a platform just because you learned about a solution. Start when you truly understand the problems.</p><p><strong>How do IDP patterns need to evolve for AI and ML workloads?</strong></p><p>Ajay: AI workloads break three assumptions baked into the standard IDP: resource primitives, lifecycle, and failure modes.</p><p>IDPs need to treat GPU pools as first class resources with their own abstractions. They need to build golden paths for ML workflows, not just microservices. They need to integrate model registries and experiment trackers into the service catalog. And they need observability for inference latency, confidence scores, and data drift.</p><p>The standard Backstage style IDP was not designed for workloads that can fail by giving confident wrong answers for weeks.</p><p><strong>What will engineers walk away understanding?</strong></p><p>Ajay: How the layers connect to each other.</p><p>You can learn about each tool from its documentation. This workshop teaches what happens when a developer submits a service request in the portal, which triggers a golden path scaffolder, which provisions a namespace with RBAC and quotas, which applies policies via OPA, which is monitored by an SLO driven alerting stack, which feeds into an AI powered alert correlator.</p><p>That end to end chain, from portal click to production insight, is the platform.</p><p><strong>Workshop details</strong></p><p>Building an AI Powered Internal Developer Platform from Scratch</p><p>Saturday April 25 and Sunday April 26, 2026 11 AM to 3 PM ET each day 4 PM to 8 PM UK / 8:30 PM to 12:30 AM IST / 7 PM to 11 PM Gulf</p><p>Hosted by Deep Engineering by Packt.</p><p><strong>What&#8217;s included:</strong></p><p>Live hands on sessions with Ajay across two days. Working code for AI platform features that runs locally without API keys. A 30 to 60 minute one on one Platform Journey consultation with Ajay. Certificate of Completion plus a Credly digital badge you can add to LinkedIn.</p><p>Refunds available up to 3 days before the event. Seats are limited.</p><p><strong><a href="https://www.eventbrite.co.uk/e/building-an-ai-powered-internal-developer-platform-from-scratch-tickets-1978960034736?aff=kubernatives">Register here</a></strong></p><p><strong>Why I am sharing this</strong></p><p>I am selective about what I put in front of this list.</p><p>Ajay&#8217;s answer to the AI workloads question landed for me because it names a real gap in how most teams are thinking about ML platforms today. GPU pools as first class resources. Model registries in the service catalog. Observability that covers data drift, not just p99 latency. Most IDPs I have seen do none of this.</p><p>If you are on a platform team, a DevOps team going through an AI transformation, or an SRE figuring out how to support ML workloads, this workshop will save you months of trial and error.</p><p><strong>Disclosure:</strong> This is a paid partnership with Deep Engineering by Packt. I only promote things I would send to a friend.</p><p>Regular Friday content this week covers the production Kubernetes debugging framework I use on our clusters. More on that in a few days.</p><p>Sharon</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.kubenatives.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Kubenatives is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>