Skip to content

[Feature]: Add /health/ready endpoint for GPU health verification #36960

Description

@anencore94

🚀 The feature, motivation and pitch

Problem: vLLM's current /health endpoint only checks whether the engine process is alive (engine_dead boolean). When a GPU encounters a page fault or illegal memory access, the process remains alive but inference becomes impossible. The /health endpoint continues returning 200, causing Kubernetes to route traffic to a broken pod.

I hit this exact scenario in production — GPU page fault / illegal memory access made inference fail, but the vLLM process itself was still running. K8s liveness probe passed, so traffic kept flowing to a pod that couldn't actually serve requests.

Solution: Add a /health/ready endpoint that runs an actual GPU forward pass (1-token dummy batch) to verify the GPU can execute inference end-to-end.

This aligns with K8s liveness/readiness probe separation:

  • /health → liveness (existing, process-level boolean check)
  • /health/ready → readiness (new, GPU forward pass verification)

Key design decisions:

  • Reuses existing execute_dummy_batch_async() which calls _dummy_run(1, uniform_decode=True) — no new GPU code needed
  • Uses the call_utility_async() path (not the scheduler's request queue), so it doesn't compete with normal inference requests for scheduling
  • Configurable timeout via VLLM_HEALTH_CHECK_GPU_TIMEOUT env var (default 20s)
  • No breaking changes — existing /health is untouched

Prior art: SGLang has a similar /health_generate endpoint (issues sgl-project/sglang#853, PRs sgl-project/sglang#1154, sgl-project/sglang#8444, sgl-project/sglang#13320). Their implementation goes through the scheduler's generate pipeline which causes contention under load. vLLM's approach via call_utility_async() avoids this issue by using a separate utility call path.

Estimated scope: ~6 files, ~96 lines added, no breaking changes.

Alternatives

  1. Periodic background health checks with cached results — Rejected because cached results represent stale health state, and K8s already controls check intervals via probe configuration.
  2. Full generate request (like SGLang's initial approach) — Goes through the scheduler queue, competes with real requests, and can cause false timeouts under load. SGLang had to add multiple workarounds for this (PRs [Improvements] Merge health check route sgl-project/sglang#8444, [RL] fix skip_server_warmup and rl health_generate logic sgl-project/sglang#8757, [RL] Allow bypassing /health check sgl-project/sglang#13320).
  3. CUDA error detection via signal handlers — More invasive, doesn't catch all failure modes (e.g., driver-level hangs).

Additional context

Before submitting a new issue...

  • Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions