<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: riyankhamdani</title>
    <description>The latest articles on DEV Community by riyankhamdani (@riyankhamdani).</description>
    <link>https://dev.to/riyankhamdani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4232983%2F376878a6-4fbf-458a-86fc-85b6bf690984.png</url>
      <title>DEV Community: riyankhamdani</title>
      <link>https://dev.to/riyankhamdani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/riyankhamdani"/>
    <language>en</language>
    <item>
      <title>Zero-Downtime LLM Inference on Kubernetes Spot Instances (~70% GPU Cost Savings)</title>
      <dc:creator>riyankhamdani</dc:creator>
      <pubDate>Sun, 08 Nov 2026 01:39:06 +0000</pubDate>
      <link>https://dev.to/riyankhamdani/zero-downtime-llm-inference-on-kubernetes-spot-instances-70-gpu-cost-savings-6en</link>
      <guid>https://dev.to/riyankhamdani/zero-downtime-llm-inference-on-kubernetes-spot-instances-70-gpu-cost-savings-6en</guid>
      <description>&lt;p&gt;Self-hosting open-source Large Language Models (LLMs) like Llama 3, DeepSeek, or Mistral on dedicated On-Demand cloud GPU instances gets expensive rapidly. A typical 4x GPU setup easily exceeds 2,000 USD/month on AWS or GCP.&lt;/p&gt;

&lt;p&gt;Spot/Preemptible instances offer steep 65% to 75% discounts, but cloud providers can reclaim them with only a 30 to 120-second warning. Without an automated eviction mechanism, node preemptions cause dropped TCP connections, broken streaming completions, and severe HTTP 502/504 spikes.&lt;/p&gt;

&lt;p&gt;In this guide, we break down a production-grade Kubernetes architecture that achieves a 0.0% request failure rate during spot node evictions while automatically falling back to On-Demand nodes when spot capacity evaporates.&lt;/p&gt;

&lt;p&gt;💰 FinOps &amp;amp; Cost Breakdown&lt;/p&gt;

&lt;p&gt;Strategy&lt;/p&gt;

&lt;p&gt;Compute Nodes (4x GPUs)&lt;/p&gt;

&lt;p&gt;Monthly Cost (est.)&lt;/p&gt;

&lt;p&gt;Preemption Failure Rate&lt;/p&gt;

&lt;p&gt;Reliability Risk&lt;/p&gt;

&lt;p&gt;Standard On-Demand&lt;/p&gt;

&lt;p&gt;g4dn.xlarge (AWS) / g2-standard-4 (GCP)&lt;/p&gt;

&lt;p&gt;~$2,050 / mo&lt;/p&gt;

&lt;p&gt;0%&lt;/p&gt;

&lt;p&gt;None (High Cost)&lt;/p&gt;

&lt;p&gt;Naive Spot Instances&lt;/p&gt;

&lt;p&gt;Spot / Preemptible equivalents&lt;/p&gt;

&lt;p&gt;~$615 / mo&lt;/p&gt;

&lt;p&gt;12% - 25% dropped reqs&lt;/p&gt;

&lt;p&gt;High (HTTP 502/504 spikes)&lt;/p&gt;

&lt;p&gt;Spot + Resilient Architecture&lt;/p&gt;

&lt;p&gt;Spot with On-Demand Fallback&lt;/p&gt;

&lt;p&gt;~$635 / mo&lt;/p&gt;

&lt;p&gt;0.0% (Zero dropped reqs)&lt;/p&gt;

&lt;p&gt;Zero-Downtime&lt;/p&gt;

&lt;p&gt;🏗️ Architecture &amp;amp; Failover Mechanics&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              [ Ingress / In-Cluster Service Proxy ]
                                |
        +-----------------------+-----------------------+
        |                                               |
        v                                               v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;[ Node Pool: Spot (Worker 2) ]              [ Node Pool: On-Demand (Worker 1) ]&lt;br&gt;
  +----------------------------+              +---------------------------------+&lt;br&gt;
  | [Spot Termination Daemon]  |              |                                 |&lt;br&gt;
  |  - Watches metadata API    |              |                                 |&lt;br&gt;
  |  - Triggers cordon &amp;amp; drain |              |                                 |&lt;br&gt;
  |                            |              |                                 |&lt;br&gt;
  | &lt;a href="https://dev.toActive"&gt;vLLM Pod 1&lt;/a&gt;      |  --Evict--&amp;gt;  | &lt;a href="https://dev.toScheduled%20here"&gt;vLLM Pod 1&lt;/a&gt;   |&lt;br&gt;
  | &lt;a href="https://dev.toPDB%20Locked"&gt;vLLM Pod 2&lt;/a&gt;  |  --Evict--&amp;gt;  | &lt;a href="https://dev.toScheduled%20after%201"&gt;vLLM Pod 2&lt;/a&gt;|&lt;br&gt;
  +----------------------------+              +---------------------------------+&lt;/p&gt;

&lt;p&gt;The 4 Pillars of Resilient Failover&lt;/p&gt;

&lt;p&gt;Autonomous Preemption Handler (DaemonSet):&lt;br&gt;
Runs strictly on nodes labeled node-type: spot. It accesses the host node name via the Kubernetes Downward API (spec.nodeName) and monitors cloud preemption metadata. Upon receiving a warning, it cordons the node and initiates an asynchronous kubectl drain.&lt;/p&gt;

&lt;p&gt;Pod Disruption Budget (PDB):&lt;br&gt;
Configured with minAvailable: 1. When the node drain fires, Kubernetes rejects simultaneous evictions. It forces sequential evacuation, keeping active inference pods alive until fallback pods pass readiness probes.&lt;/p&gt;

&lt;p&gt;Graceful Traffic Handover (preStop Hook):&lt;br&gt;
Inference pods delay shutdown using a lifecycle.preStop sleep hook (5–15 seconds). This grace period allows kube-proxy and ingress controllers to deregister the pod endpoint before the container process receives SIGTERM.&lt;/p&gt;

&lt;p&gt;Soft Node Affinity:&lt;br&gt;
Inference workloads use preferredDuringSchedulingIgnoredDuringExecution to prioritize Spot instances during standard scheduling cycles, but fall back seamlessly to On-Demand nodes when Spot capacity is exhausted.&lt;/p&gt;

&lt;p&gt;🧪 Validating the Failover Locally with Kind&lt;/p&gt;

&lt;p&gt;You can validate this entire failover flow locally without renting cloud GPU instances by using kind (Kubernetes in Docker).&lt;/p&gt;

&lt;p&gt;Step 1: Provision the Multi-Node Cluster&lt;/p&gt;

&lt;p&gt;Create a cluster with heterogeneous node labels representing Spot and On-Demand pools:&lt;/p&gt;

&lt;h1&gt;
  
  
  kind-config.yaml
&lt;/h1&gt;

&lt;p&gt;kind: Cluster&lt;br&gt;
apiVersion: kind.x-k8s.io/v1alpha4&lt;br&gt;
nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;role: control-plane&lt;/li&gt;
&lt;li&gt;role: worker
labels:
node-type: on-demand&lt;/li&gt;
&lt;li&gt;role: worker
labels:
node-type: spot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apply the configuration:&lt;/p&gt;

&lt;p&gt;kind create cluster --name spot-lab --config kind-config.yaml&lt;/p&gt;

&lt;p&gt;Step 2: Deploy Workloads &amp;amp; Preemption DaemonSet&lt;/p&gt;

&lt;p&gt;Deploy the inference service, PodDisruptionBudget, and the preemption listener:&lt;/p&gt;

&lt;p&gt;kubectl apply -f &lt;a href="https://raw.githubusercontent.com/riyankhamdani/spot-llm-k8s/main/k8s/vllm-resilient.yaml" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/riyankhamdani/spot-llm-k8s/main/k8s/vllm-resilient.yaml&lt;/a&gt;&lt;br&gt;
kubectl apply -f &lt;a href="https://raw.githubusercontent.com/riyankhamdani/spot-llm-k8s/main/k8s/spot-handler.yaml" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/riyankhamdani/spot-llm-k8s/main/k8s/spot-handler.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Run the Chaos Test&lt;/p&gt;

&lt;p&gt;In your first terminal, simulate continuous live inference requests from an in-cluster client locked to the on-demand worker:&lt;/p&gt;

&lt;p&gt;kubectl run traffic-tester --rm -it --image=curlimages/curl --restart=Never \&lt;br&gt;
  --overrides='{"spec":{"nodeSelector":{"node-type":"on-demand"}}}' -- sh -c '&lt;br&gt;
  SUCCESS=0; FAILED=0;&lt;br&gt;
  while true; do&lt;br&gt;
    STATUS=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 2 -X POST &lt;a href="http://vllm-service:8000/v1/completions" rel="noopener noreferrer"&gt;http://vllm-service:8000/v1/completions&lt;/a&gt; -H "Content-Type: application/json" -d "{\"prompt\": \"Test\"}")&lt;br&gt;
    [ "$STATUS" -eq 200 ] &amp;amp;&amp;amp; SUCCESS=$((SUCCESS+1)) || FAILED=$((FAILED+1))&lt;br&gt;
    echo "[$(date +%T)] Status: $STATUS | Success: $SUCCESS | Failed: $FAILED"&lt;br&gt;
    sleep 0.3&lt;br&gt;
  done'&lt;/p&gt;

&lt;p&gt;In a second terminal, trigger a simulated spot termination signal:&lt;/p&gt;

&lt;p&gt;POD_NAME=$(kubectl get pods -l app=spot-termination-handler -o jsonpath='{.items[0].metadata.name}')&lt;br&gt;
kubectl exec -it $POD_NAME -- touch /tmp/preempt_signal&lt;/p&gt;

&lt;p&gt;Observation:&lt;/p&gt;

&lt;p&gt;The DaemonSet detects the signal, immediately marks the spot worker as SchedulingDisabled, and initiates sequential pod eviction.&lt;/p&gt;

&lt;p&gt;The PodDisruptionBudget maintains an active replica while the displaced replica spins up on the fallback on-demand node.&lt;/p&gt;

&lt;p&gt;The traffic generator maintains continuous 200 OK responses with a 0.0% failure rate.&lt;/p&gt;

&lt;p&gt;☁️ Production Cloud Migration&lt;/p&gt;

&lt;p&gt;To run this in live production environments (AWS EKS or GCP GKE):&lt;/p&gt;

&lt;p&gt;Metadata Endpoint Polling:&lt;br&gt;
Replace the local file trigger in the DaemonSet with HTTP calls pointing to cloud provider metadata:&lt;/p&gt;

&lt;p&gt;AWS IMDSv2:&lt;/p&gt;

&lt;p&gt;TOKEN=$(curl -s -S -X PUT "&lt;a href="http://169.254.169.254/latest/api/token" rel="noopener noreferrer"&gt;http://169.254.169.254/latest/api/token&lt;/a&gt;" -H "X-aws-ec2-metadata-token-ttl-seconds: 60")&lt;br&gt;
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" &lt;a href="http://169.254.169.254/latest/meta-data/spot/instance-action" rel="noopener noreferrer"&gt;http://169.254.169.254/latest/meta-data/spot/instance-action&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GCP Metadata:&lt;/p&gt;

&lt;p&gt;curl -s -H "Metadata-Flavor: Google" &lt;a href="http://metadata.google.internal/computeMetadata/v1/instance/preempted" rel="noopener noreferrer"&gt;http://metadata.google.internal/computeMetadata/v1/instance/preempted&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Production Image &amp;amp; GPU Limits:&lt;br&gt;
Switch the mock inference container image to official vllm/vllm-openai:latest and declare explicit GPU limits (nvidia.com/gpu: 1).&lt;/p&gt;

&lt;p&gt;📦 Source Code&lt;/p&gt;

&lt;p&gt;All Kubernetes manifests, kind configuration files, and chaos testing scripts are available in the GitHub repository:&lt;/p&gt;

&lt;p&gt;👉 github.com/riyankhamdani/spot-llm-k8s&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>sre</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
