Article

LLM GPU Self-Healing in Production: Reducing Inference Interruptions with Health Probes, Node Isolation, and Request Draining

Learn how to build a production-grade GPU fault self-healing system for LLM inference. Covers DCGM health checks, Kubernetes four-layer probes, node isolation, request draining, CUDA OOM classification, and canary recovery to reduce inference interruptions and retry storms.

Background: The “Zombie” GPU Is the Worst Nightmare for LLM Inference

In typical web services, restarting a crashed process usually fixes the issue. But LLM inference is fundamentally different—a single request can consume significant GPU memory, and the inference process holds model weights, KV cache, CUDA context, NCCL communication state, and a runtime scheduling queue.

When a GPU fails, the service doesn’t always crash immediately. The symptoms are often more subtle:

  • Requests suddenly slow down, with high time-to-first-token (TTFT)
  • Streaming output is interrupted
  • GPU memory on a specific card is not released
  • The container is alive, but CUDA calls fail
  • Monitoring metrics are intermittently missing

If you rely solely on “process alive” checks, you’ll likely misdiagnose these issues—the Pod is still Running, /healthz returns 200, but new requests can’t execute reliably. Worse, after a simple restart triggered by a CUDA OOM, the node repeatedly joins the service and fails again, amplifying queueing and retry storms.

The goal of production-grade GPU self-healing isn’t “auto-restart everything,” but to establish a clear closed loop:

Detect anomaly → Stop accepting new requests → Drain in-flight requests → Isolate suspicious node → Rebuild inference process → Verify recovery → Preserve failure evidence

If any link in this chain is missing, a single-card problem can escalate into a cluster-wide disruption.


Core Principles: Decompose Health into Four Layers

Layer 1: Application Health

Application health answers: “Can the inference service process still accept and handle requests normally?” Typical signals include:

  • Is the HTTP health endpoint reachable?
  • Is the model fully loaded?
  • Are the tokenizer, scheduler, and workers initialized?
  • Is the queue in overload protection?
  • Are request error rates, timeout rates, and cancellation rates abnormal over a recent window?

This layer is well-suited for Kubernetes readiness probes. The readiness probe’s purpose isn’t to restart the container, but to decide if the Pod should continue receiving Service traffic. For LLM Serving, the readiness semantics should be stricter: return not-ready if the model isn’t loaded, GPU workers are unavailable, the queue is at a protective watermark, or the node is draining.

Layer 2: GPU Device Health

GPU device health answers: “Can this GPU card be trusted to perform computations reliably?” Don’t just look at utilization and memory usage. NVIDIA DCGM health checks cover these dimensions:

DimensionCheck Content
PCIeBandwidth, replay count, link status
MemoryECC errors, pending page retirement, faulty memory
ThermalTemperature, throttling events
PowerPower anomalies, insufficient power supply
NVLinkLink errors, bandwidth degradation
DriverDriver version, device visibility

DCGM documentation lists the following as failure conditions: GPU fallen off bus, severe XID, requires reset, requires reboot, pending page retirement, faulty memory. The application layer should handle these signals with different severity levels:

  • warning → Reduce weight, observe, trigger migration
  • error → Drain, isolate
  • reset / reboot signals → Not just restart the container, but remove the node from the scheduling pool

Layer 3: Kubernetes Scheduling Health

Scheduling health answers: “Should this node still receive new GPU Pods?” The NVIDIA Kubernetes device plugin can expose GPU count and track health status, but the official documentation clearly states that the device plugin itself should not be considered a complete GPU fault self-healing system.

This means production environments typically need an additional GPU node controller. It reads DCGM, device plugin, Prometheus, Kubernetes events, and inference service metrics, and performs the following actions on nodes when necessary:

  • cordon — Mark the node as unschedulable
  • taint — Add an eviction taint
  • Remove endpoints
  • Trigger Pod rebuild or notify for manual intervention

Layer 4: Request Lifecycle Health

Request lifecycle health answers: “How do we handle in-flight requests when a failure occurs?” LLM requests differ from standard REST calls; they might be streaming output, have consumed many tokens, or be stuck in a tool call or long-context decode phase.

The self-healing logic must distinguish three types of requests:

  1. Completed requests: Return normally, just record the node as suspicious.
  2. In-flight requests: Prioritize completion or clean interruption; don’t leave connections hanging indefinitely.
  3. Queued requests (not yet started): Migrate as soon as possible or return a retryable error.

Therefore, the self-healing process must include a drain mode. Once an instance enters drain mode, it stops accepting new requests and only handles existing ones. After exceeding the maximum drain time, it is forcefully terminated.


Engineering Implementation: An Executable GPU Self-Healing Pipeline

1. Define a Health State Machine

Don’t just use healthy=true/false. Break the instance state into these stages:

STARTING → WARMING → READY → DEGRADED → DRAINING → QUARANTINED → RECOVERING → READY
                                                                    ↘ FAILED
StateMeaningAction
STARTINGContainer starting, model not loadedWait for startup probe
WARMINGModel loaded, warming upProbe inference, no production traffic
READYCan accept production trafficreadiness=true, normal scheduling
DEGRADEDWarning signals detectedReduce weight, limit new long requests, alert and observe
DRAININGStop accepting new requests, drainingreadiness=false, load balancer removes traffic
QUARANTINEDNode or instance isolatedcordon/taint, excluded from scheduling
RECOVERINGRestarting worker or rebuilding Pod, verifyingProbe inference + canary recovery
FAILEDAuto-recovery failedManual intervention, preserve failure evidence

This state machine should drive the readiness probe, load balancing weights, alert levels, and operational actions.

2. Separate Readiness and Liveness

Many teams point both readiness and liveness probes to the same /healthz endpoint, which is a common mistake. LLM inference services should provide at least three endpoints:

EndpointSemanticsFailure Effect
/livezIs the process alive?Restart container
/readyzCan it accept new requests?Remove traffic
/gpuzAre GPU workers, CUDA context, DCGM signals, and memory state trustworthy?Trigger DRAINING

Example Kubernetes configuration:

readinessProbe:
  httpGet:
    path: /readyz
    port: 8080
  periodSeconds: 5
  failureThreshold: 2

livenessProbe:
  httpGet:
    path: /livez
    port: 8080
  initialDelaySeconds: 120
  periodSeconds: 10
  failureThreshold: 3

startupProbe:
  httpGet:
    path: /readyz
    port: 8080
  periodSeconds: 10
  failureThreshold: 60

Key point: Model loading and warmup can be slow. Use a startupProbe to prevent kubelet from killing the container before the model is loaded. Use readiness for traffic removal; don’t wait for the process to crash.

3. Trigger Node Isolation with DCGM Signals

Handle DCGM health results in three categories:

groups:
  - name: llm-gpu-health
    rules:
      - alert: LlmGpuMetricsMissing
        expr: absent_over_time(DCGM_FI_DEV_GPU_UTIL[3m])
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "GPU metrics disappeared for a serving node"

      - alert: LlmGpuXidError
        expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "GPU reported XID errors; drain and quarantine the node"

The key isn’t to copy these rules verbatim, but to grasp two concepts: monitor explicit hardware errors (XID, ECC, NVLink) and monitor structural anomalies—such as metrics suddenly disappearing, scrape delays, or changes in the number of devices.

4. Make Request Draining a First-Class Capability

The most common mistake in GPU fault self-healing is restarting directly, which causes three types of side effects:

  • Streaming requests that have started outputting are abruptly disconnected.
  • Client retries send traffic to other instances, creating a secondary peak.
  • The failure scene is cleaned up, making root cause analysis impossible.

A safer draining process:

Controller marks instance as DRAINING
    → readiness probe fails, load balancer removes new traffic
    → Gateway stops assigning new requests to the instance
    → Instance continues processing requests already in decode
    → Queued but unexecuted requests are migrated or return a retryable error
    → Force terminate after exceeding max_drain_seconds
    → Save last error, GPU metric window, request samples, and container events
    → Rebuild worker or isolate node

Pod configuration direction:

terminationGracePeriodSeconds: 180
lifecycle:
  preStop:
    exec:
      command:
        - /bin/sh
        - -c
        - "curl -X POST localhost:8080/admin/drain && sleep 30"

terminationGracePeriodSeconds must be greater than the maximum acceptable drain time; otherwise, kubelet will forcefully kill the process before requests are finished. In production, the Gateway and Controller must also be aware that the instance has entered drain mode, rather than just relying on a sleep inside the container.

5. Distinguish the Root Cause of CUDA OOM

CUDA OOM doesn’t mean the GPU is broken. At a minimum, distinguish four scenarios:

OOM TypeTypical TriggerRecommended Action
Single request exceeds limitsPrompt or max_tokens too largeReject request or reduce max_tokens
Batch overloadScheduler puts too many tokens in at onceReduce concurrency, reduce batch token limit
Memory fragmentationNon-contiguous available memory after long runtimeDrain and rebuild worker
Driver/device anomalyOOM accompanied by XID, CUDA context errorsIsolate node and perform GPU health check

If you let the liveness probe restart everything for every OOM, you’ll mask the real problem. The correct approach is to record the context of each OOM—input tokens, output tokens, batch size, KV cache usage, GPU ID, model version, adapter version, and recent DCGM signals—and then classify and handle it.

6. Design Node Isolation and Recovery Strategies

Node isolation shouldn’t rely solely on deleting Pods. Consider three layers:

LayerScopeUse Case
Instance isolationSingle inference process enters DRAININGSingle request anomaly, queue overload
Pod isolationPod is rebuilt, node can still schedule other GPU PodsMemory fragmentation, process leak, worker deadlock
Node isolationcordon + taint, no new GPU workloads allowedFallen off bus, severe XID, NVLink/driver-level failure

Don’t immediately restore full traffic after recovery. Use canary recovery: first, run probe inference → accept a small number of short requests → observe error rate, TTFT, TPOT, memory release, and DCGM signals → restore full weight after a stable window.


Applicable Scenarios

This approach is suitable for:

  • Self-hosted vLLM, TensorRT-LLM, TGI, SGLang, or Ray Serve LLM inference clusters
  • Multi-replica GPU inference services running on Kubernetes
  • Scenarios with long outputs, streaming output, multi-tenant requests, or high-concurrency queues
  • Clusters with a small number of GPU nodes, where a single node failure significantly impacts availability
  • Engineering governance for inference interruptions, timeouts, retry storms, and node cascading failures

For development/test environments or single-node offline inference, you can simplify to: health checks, OOM logs, manual restarts, and basic alerts. But if your service faces users, you should at least implement readiness, drain, and failure evidence preservation.


Common Misconceptions

Misconception 1: Only Look at GPU Utilization

Normal GPU utilization doesn’t mean the node is healthy. Driver errors, device loss, metric gaps, PCIe/NVLink errors, and non-releasable memory can all occur before or independently of utilization changes. Production systems need to monitor numeric metrics, structural metrics, and request outcomes simultaneously.

Misconception 2: Auto-Restart Everything

Auto-restart is suitable for process-level failures, not all GPU failures. For GPU problems that require a reset or reboot, a container restart is usually insufficient; if you continue scheduling new Pods to that node, they will just fail repeatedly.

Misconception 3: Draining Only via Kubernetes Pod Deletion

Kubernetes’ graceful termination provides a basic termination flow, but it doesn’t know if an LLM request is still decoding, streaming output, or can be migrated. Draining must be coordinated by the Gateway, Serving Runtime, and Controller.

Misconception 4: Full Traffic Immediately After Recovery

After GPU fault recovery, you should first run a canary. Especially after a driver reset, node restart, or model reload, you should first verify with probe inference, short requests, long requests, and concurrent requests, rather than restoring all traffic immediately.


Go-Live Checklist

Health Probes

  • Are /livez, /readyz, and /gpuz semantically separated?
  • Does startupProbe cover model loading and warmup time?
  • Does readinessProbe fail during drain, overload, or GPU worker anomalies?
  • Does livenessProbe avoid killing instances that are in a long warmup phase?

GPU Monitoring

  • Are DCGM or equivalent GPU health metrics integrated?
  • Are XID, fallen off bus, reset required, and pending page retirement monitored?
  • Are metric gaps, scrape delays, and changes in GPU device count monitored?
  • Are warning, error, and fatal signals mapped to different actions?

Request Draining

  • Does the Gateway support instance-level traffic removal?
  • Can queued requests be migrated or fast-failed?
  • Can streaming requests be cleanly ended or return an explainable error?
  • Is terminationGracePeriodSeconds greater than the maximum drain window?

Node Isolation

  • Is cordon/taint supported for suspicious GPU nodes?
  • Is there a distinction between Pod rebuild, GPU reset, node reboot, and manual repair?
  • Is there a probe inference before recovery and a stable window after recovery?
  • Are the last segment of metrics, logs, Kubernetes events, and request samples preserved?

References

FAQ

Is configuring a liveness probe enough for GPU self-healing?
No. A liveness probe can only trigger container restarts; it cannot determine whether a GPU needs draining, isolation, reset, or manual repair. A production system must chain application probes, GPU health signals, node scheduling status, and request draining into a complete loop.
Should a CUDA OOM always trigger a restart of the inference service?
No. The root cause of an OOM—such as an excessively long context in a single request, batch scheduling overload, memory fragmentation, or a driver/device error—requires different handling. You must classify the OOM first, then decide whether to reject the request, reduce concurrency, rebuild the worker, or isolate the node.
Can a GPU node still be unavailable even if utilization metrics look normal?
Yes. GPU failures don't always manifest as a single metric exceeding a threshold. Device disappearance, metric gaps, scrape delays, driver anomalies, or request timeouts can all occur while utilization appears normal. Therefore, you must monitor both numeric metrics and structural signals.