Capacity auto-tune
Measure how many requests a self-hosted model serves at once with a short load probe, then apply the result as its pool size.
3 min readUpdated for v2.4.0Edit on GitHub
Run a probe
- 1
Open the dialog
On the model's page, under Capacity, choose Auto-tune capacity. Run it in a quiet window: the probe sends real concurrent load to the model's backend.
- 2
Describe the backend
Set Replicas running, the copies of the model serving now; Latency tolerance, how much slower than one idle request you accept at peak (Tight 2×, Balanced 4×, Relaxed 8×); and the Workload: short chat turns, or coding with a large context and longer replies.
- 3
Run and apply
Choose Run probe. The dialog shows the recommended slots, the throughput there, the latency budget, why the ramp stopped, and a row per step. Apply sets the model's pool to the recommendation and switches it to the
tunedcapacity mode.
Keep a model on a metered cloud API on a fixed pool instead: the probe would spend real money, and a fixed number bounds your spend. If the backend's replica count changes on its own, use the discovered mode, with the tuned value divided by the replicas as the per-replica concurrency.
How the probe works
The probe runs in the Management API and sends requests straight to the model's API base, bypassing obleth's own admission, so it measures the backend rather than the current pool. It carries the model's upstream headers.
- It steps concurrency up 1, 2, 4, 8 and so on to a ceiling, holding each step for 2.5 seconds and recording throughput and p50 and p99 latency.
- It sets a latency budget from the concurrency-1 baseline: the baseline p99 × the tolerance, never below 100 ms.
- It always runs the whole ladder, so the result is reproducible; only the safety limits below, or a step where every request failed, cut it short.
- It recommends the highest concurrency that stayed within the budget and still added throughput.
| Why it stopped | Meaning |
|---|---|
latency_degraded | A step's p99 went past the budget |
plateau | Throughput gained less than about 7% over the step before |
max_concurrency | It reached the ceiling with no knee; the real one may be higher |
no_data | No step produced usable samples: the upstream is unreachable |
The ceiling is max_concurrency if you give it (at most 512), otherwise 32 × Replicas running, otherwise 64.
| Workload | Prompt | Reply |
|---|---|---|
chat (default) | about 512 tokens | 128 tokens |
coding | about 6,000 tokens | 512 tokens |
Capacity modes
| Mode | Pool size |
|---|---|
static (default) | The Max slots you set |
tuned | The value last applied from auto-tune; capacity_tuned_at records when |
discovered | Follows the backend's ready replicas; see Pool sizes |
static and tuned both use max_in_flight, so between them the mode is a record for you and the audit log. You can switch back to static without losing the value.
Management API
# Run a probe (changes nothing)
curl -X POST "http://localhost:9180/api/v1/models/$MODEL_ID/autotune" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"workload": "chat", "latency_headroom": 4.0, "replicas": 2}'
# Apply a recommendation
curl -X POST "http://localhost:9180/api/v1/models/$MODEL_ID/autotune/apply" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"max_in_flight": 32}'
The probe answers with recommended_max_in_flight, knee_reason, baseline_p99_ms, latency_ceiling_ms, recommended_throughput_rps and a steps array. Applying sets max_in_flight, switches the mode to tuned, stamps capacity_tuned_at and updates every gateway replica. Probes and applies are recorded in the audit log. See Management API.
Safety limits
| Limit | Value |
|---|---|
| Wall clock | 60 seconds for all steps together |
| Total requests | 20,000 |
| Concurrency | 512 |
latency_headroom | Clamped to 1.5–20; default 4.0 |
| Model types | Chat and embeddings |
