litellm-preflight
Reads your LiteLLM proxy config and flags settings that cost money or break once you run more than one process. It runs offline. It never connects to your proxy or sends anything anywhere.
Every check matches how LiteLLM v1.104.0 actually behaves, and was reproduced against real LiteLLM containers in a local harness before it shipped. Today there's 1 check, health-checks.
litellm-preflight is an independent project. It isn't affiliated with BerriAI or the LiteLLM project.
Install
pip install litellm-preflight
Or run it without installing, with uvx litellm-preflight or pipx run litellm-preflight. It needs Python 3.9 or newer, and its only dependency is PyYAML.
health-checks: what background health checks really cost
With background_health_checks: true, LiteLLM probes every deployment in your model_list once per health_check_interval. It does that in every process that runs the proxy: each replica, each uvicorn worker inside it, the collector sidecar if you run one, and on the split litellm-gateway/litellm-backend images, each backend pod too. 3 replicas with 2 workers make 6x the calls you'd expect, and each probe is a real request to your provider, billed like any other.
For a config with 20 deployments and background_health_checks: true, on 3 replicas with 2 workers each:
litellm-preflight health-checks config.yaml --replicas 3 --workers 2 --cost-per-probe 0.0003
Config: config.yaml
Background health checks: on, every 300s, 20 deployments probed
Processes running the loop: 6 (3 replicas x 2 workers). Each one probes on its own.
Probes per day: up to 34,560
1 loop would make 5,760. You're making 6x that.
Cost at $0.0003 per probe: $10.37 a day, $311.04 per 30 days
Fix: set general_settings.use_shared_health_check: true, with a Redis every process can reach (general_settings.coordination_redis, a Redis litellm_settings.cache, or REDIS_HOST). Then 1 process probes for all.
It also catches checks that are on but never run. LiteLLM only starts the loop when health_check_interval is a whole number of seconds above 0, so a quoted "300", a 30.5 or an os.environ/ reference silently turns health checks off.
What to pass it
- Your
config.yaml, the file the proxy loads with--config. Files pulled in withinclude:are merged the same way LiteLLM merges them. - A Helm values file for the
litellm-helmor splitlitellmchart. It reads the proxy config from it, and replicas, workers, autoscaling minimums, the backend and the collector sidecar too, so you can usually skip the flags. - A Kubernetes ConfigMap, from
kubectl get configmap <name> -n <namespace> -o yaml > cm.yaml.
Flags always win over what a values file says.
Finding the numbers on Kubernetes
--replicas: the pod count of the proxy (or gateway) Deployment, fromkubectl get deploy -n <namespace>. With an HPA it moves during the day, so pass your usual count (kubectl get hpa -n <namespace>).--workers: the container's--num_workersargument orNUM_WORKERSenv var. In the charts that'snumWorkersorgateway.numWorkers. Unset means 1.--backend-replicas: the pod count of the backend Deployment, on the split chart.
Working out --cost-per-probe
A chat probe sends a short fixed prompt ("Hey how's it going?" or "What's 1 + 1?") with max_tokens set to 16 by default. So 1 probe costs about 20 input tokens plus up to 16 output tokens. At $3 and $15 per million tokens, that's about $0.0003.
That's a floor for some deployments:
- Reasoning models can get a bigger
max_tokens(health_check_max_tokens_reasoning), and wildcard routes likeopenai/*get no cap. image_generation,video_generationandaudio_speechdeployments generate real media on every probe. The report calls them out.- A failing probe can be retried, so it can cost up to 3 requests.
Options
| Flag | Default | What it's for |
|---|---|---|
--replicas N |
1 | Proxy pods, or gateway pods with --split |
--workers N |
1 | Uvicorn workers per pod (--num_workers or NUM_WORKERS) |
--split |
off | You run the litellm-gateway and litellm-backend images |
--backend-replicas N |
1 | Backend pods, with --split |
--backend-workers N |
1 | Uvicorn workers per backend pod, with --split |
--collector |
off | Each proxy pod runs the collector sidecar, which runs its own loop |
--db-deployments N |
0 | Deployments added through the API or UI (store_model_in_db), added to the config's count |
--redis-env |
off | The proxy gets Redis from REDIS_HOST or REDIS_URL, which a config can't show |
--shared-ttl SECONDS |
300 | DEFAULT_SHARED_HEALTH_CHECK_TTL, if you set that env var on the proxy |
--restarts-per-day N |
0 | Process starts a day (deploys, autoscaling, MAX_REQUESTS_BEFORE_RESTART). Each start probes right away |
--cost-per-probe USD |
none | What 1 probe costs, to print a cost |
Shared health checks
use_shared_health_check: true lets 1 process probe while the others reuse its results. Two catches, both reported:
- It needs Redis. Without one, LiteLLM quietly falls back to every process probing on its own.
- Results are reused for 300s (
DEFAULT_SHARED_HEALTH_CHECK_TTL). An interval shorter than that becomes about 300s. With an interval longer than that, each process still wakes on its own schedule, so the count lands in a range that depends on how their start times line up.
Limits
- Counts are upper bounds for steady state. LiteLLM sleeps the full interval after each round finishes, so slow probes stretch the real period.
- The proxy's environment is invisible.
os.environ/references are resolved from your shell, and the report says when one isn't set there.DEFAULT_HEALTH_CHECK_INTERVALon the proxy changes the default interval of 300s, and the tool assumes 300. - Shared rounds over 60s. If a shared round takes longer than 60s (
DEFAULT_SHARED_HEALTH_CHECK_LOCK_TTL), for example because a deployment hangs until its timeout, the other processes stop waiting and probe too. - Some deployments the router changes aren't modeled. An allowlist that names an alias or routing group, deployments dropped by
supported_environments, and invalid deployments the proxy skips at startup. - Hypercorn runs 1 process whatever
--num_workerssays. Pass--workers 1if you use it. - 1 price for every probe.
How it was validated
The harness runs LiteLLM v1.104.0 in Docker, both the classic image and the split images, against a mock model server that counts every call by container. With 3 deployments, a 20s interval and a 120s window it measured:
| Setup | Calls in 120s |
|---|---|
| 3 replicas | 54 |
| 1 replica x 2 workers | 36 |
| 3 split gateways + 1 backend | 72 |
| Shared, with Redis | a round of 3 every ~300s |
| Shared, with the TTL set to 20s | 18 |
The tests check the calculator against those numbers. FINDINGS.md has the full write-up, including the other LiteLLM behaviors the harness reproduced.
Exit codes
0 means the report printed. 2 means bad input: a file it can't read, a config LiteLLM would reject, or a bad flag. It doesn't fail on findings, so it's safe to run in CI.
The command line is the supported interface. Python imports may change before 1.0.
Contributing
Seen LiteLLM break or cost money at scale? Open an issue with your LiteLLM version, how you deploy it and what happened. A check gets added once it reproduces in the harness.
To report a security problem in litellm-preflight, see SECURITY.md.
License
MIT
Metadata
Release files for litellm-preflight 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| litellm_preflight-0.1.0.tar.gz | 21.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| litellm_preflight-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.6 kB
Release files / litellm_preflight-0.1.0.tar.gz
| Download URL | litellm_preflight-0.1.0.tar.gz |
|---|---|
| Size | 21.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31380374161e55ad4393b808d4ec0e37bec30224d18961ab496741f590a0b507
|
|
BLAKE2b-256 checksum How to use checksums |
391ad1bd26de6a59dcf6913368c720a571e8e01469bdb1d606bcd2083555eeab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / litellm_preflight-0.1.0-py3-none-any.whl
| Download URL | litellm_preflight-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df4e52a83ca87d06bf889cbac0ace54be449ed2f4867b3efd6e5f2439378728a
|
|
BLAKE2b-256 checksum How to use checksums |
ad14469ab43d1d30f1058180ea89fd26ce0b8b4df7e0d896b66a8d16080554e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log