Health checks
How obleth checks that each model answers, what each state means, and what clears a failure.
5 min readUpdated for v2.4.0Edit on GitHub
Check a model
On a model's page, Check health now runs a check at once; on a model that is off, Check and turn on runs one and turns the model on if it passes. The page's Health section sets the schedule, alerts, the failure threshold and a maintenance window, and lists recent checks. On Models, select rows to check several models together.
# One model
curl -X POST http://localhost:9180/api/v1/models/$MODEL_ID/health/check \
-H "Authorization: Bearer $TOKEN"
# Every model that is on, has checks on and is not in maintenance
curl -X POST http://localhost:9180/api/v1/models/health \
-H "Authorization: Bearer $TOKEN"
What a check does
A check uses the cheapest signal it can trust:
- Recent traffic. A successful client request for the model within its check interval (at least 300 seconds) settles it as
healthywithout a probe. Recent errors are never trusted on their own; they fall through to a probe. - A real request to the endpoint the model's type serves.
- The upstream catalog (
GET {api_base}/models) for image and video models, where a real request would be a costly generation.
Probe tokens are recorded under an internal tenant with request_type = health_probe, so they never land on a client's bill and are hidden from the request log. Probes and catalog requests carry the model's upstream headers.
What the active probe sends
| Model type | Probe |
|---|---|
| Chat | POST {api_base}/chat/completions, one ping message, max_tokens: 1 |
| Embeddings | POST {api_base}/embeddings, input: "ping" |
| Speech | POST {api_base}/audio/speech, input: ".", voice: "alloy" |
| Transcription | POST {api_base}/audio/transcriptions with a 0.1-second silent WAV |
| Search | GET {api_base}/search?q=ping&format=json, one real search; no catalog check |
| Image, video, other | GET {api_base}/models, and the model must be listed |
A probe tries up to three times, backing off, on a 408, 429, 5xx or network error before it records a result.
Scheduled and bulk checks skip a retired model, whose backend is usually gone. A staged model is checked like an active one.
States
| State | Meaning |
|---|---|
healthy | The model served a real request, or recent client traffic succeeded |
degraded | Reachable but not confirmed: overloaded (408, 429, 5xx), or rejected by an endpoint while the catalog lists the model, which points at a wrong model type. Doesn't alert |
unhealthy | The model is missing upstream, the key was rejected, or the upstream is unreachable. Counts toward the failure threshold |
unknown | Nothing could be verified, such as a wildcard catalog. Never alerts |
disabled | The model is off; no request is made |
A model reads unhealthy only once it has failed its failure threshold of checks in a row (2 by default), the same point at which the down alert goes out. A single failed check doesn't flip it.
Catalog checks for image and video models
An image or video model is healthy when the upstream catalog lists it. Because some servers mount their catalog only at /v1/models/, a 404 is retried once with the trailing slash. When the upstream answers the catalog with another 4xx, for example a gateway that doesn't serve /models or guards it differently from inference, the model is unknown rather than down: it stays routable and doesn't alert. No answer at all is unhealthy, a 5xx is degraded, and a catalog that answers without the model is unhealthy. Catalogs are cached per API base for 60 seconds.
What clears a failure
- A passing check. The next scheduled or manual check that comes back
healthyclears the failure and sends a recovery alert. - Fixing the configuration. Changing a model's API base, upstream model, type or upstream headers resets its health: the state goes back to
unknown, the failure count to zero, the alert state clears, and the next check runs within one worker pass. Check history is kept. - A maintenance window. Until it ends, failed checks are still recorded but don't alert, and the model shows as in maintenance.
Alerts
Alerts go through the channels in Settings › Alerts; see Alerting. Each model sends one alert when it reaches its failure threshold and one when it recovers. Nothing is sent while alerts are off for the model, it is in maintenance, or it is off.
Models with several endpoints, and Slurm models
Each enabled endpoint is probed on its own and gets its own state. Requests skip endpoints that are unhealthy or off; degraded and unknown endpoints stay in rotation. A degraded result lifts a stale unhealthy, but never lowers a confirmed healthy.
A model with no API base of its own, such as one launched on Slurm, takes its state from its endpoints, measured against its minimum replicas:
| Endpoints serving (answered the probe) | Model state |
|---|---|
| At least the minimum | healthy |
| Some, but fewer than the minimum | degraded |
Every live endpoint unknown | unknown |
| None registered, or none reachable | unhealthy |
Recent traffic can settle such a model only while an endpoint is still serving. These probes run from the gateway, so a replica the provisioner promoted can still read unhealthy if the gateway can't reach its node.
Settings and internals
| Environment variable | Default | Meaning |
|---|---|---|
OBLETH_MODEL_HEALTH_ENABLED | true | Run scheduled checks |
OBLETH_MODEL_HEALTH_INTERVAL_SECS | 900 | Default check interval |
OBLETH_MODEL_HEALTH_TIMEOUT_SECS | 30 | Timeout per check |
OBLETH_MODEL_HEALTH_RETENTION_DAYS | 30 | How long check history is kept |
Checks are claimed with Postgres row locks, so several Management API replicas can run them safely, and per-model jitter spreads them out. Health state is stored in Postgres and survives a restart. If the data plane can't read health, it keeps the last known state, so a failed read never re-enables an unhealthy model.
POST /api/v1/models/validate answers, without writing anything, whether an upstream is reachable, whether its catalog is a wildcard, and whether it lists a given upstream model. The dashboard calls it when you save a model's connection and shows its warnings; it never blocks a save.
