Overview
LiteLLM is an OpenAI-compatible LLM gateway and proxy: a single endpoint in front of 100+ model providers, with virtual API keys, per-key and per-team spend tracking and budgets, rate limiting, and routing with fallbacks. This template deploys the stateless LiteLLM proxy tier (API and Admin UI on port4000) backed by a bundled PostgreSQL for virtual keys, spend, teams, and budgets, and a bundled Sentinel-managed Redis for rate-limit counters and the response cache shared across proxy replicas. Clients call it exactly as they would call OpenAI — point any OpenAI SDK at the endpoint and swap in a LiteLLM key.
What Gets Created
Prerequisites
LiteLLM needs three kinds of credential that never pass through Helm values: the master key that gates the Admin API and UI and mints virtual keys, the salt key that encrypts provider keys stored in PostgreSQL, and one provider API key per entry inlitellm.providerEnv. All of them live in a single dictionary secret you create before installing; the chart references it by name and grants the proxy identity reveal on exactly that secret. The bundled PostgreSQL’s credentials live in a second dictionary secret you also create before installing (a third holds the Redis password when Redis auth is on).
Create the dictionary secret
LITELLM_MASTER_KEY and LITELLM_SALT_KEY are required and must start with sk-. Add one more entry for every environment-variable name listed in litellm.providerEnv (the default list is just OPENAI_API_KEY), holding that provider’s real API key:secrets.name to this name. Secrets are org-level, so no GVC flag is involved.Back the salt key up outside Control Plane
LITELLM_SALT_KEY is write-once: it encrypts every provider key stored in PostgreSQL, and changing it corrupts all of them. The master key can be rotated later; the salt key cannot once models or keys are stored. Keep a copy somewhere safe.Create the database credentials secret
postgres.config.credentialsSecretName to its name (default my-litellm-db-credentials); secret names are org-wide, so give each LiteLLM release its own:_): the proxy’s DATABASE_URL is assembled from them at container start. The snippet’s hex password is URL-safe. PostgreSQL reads the secret only when its data directory is first initialized — see Important Notes for how to change the password later.password (see Bundled Redis), and scheduled database backups, which need a bucket and cloud account, are described under Backing Up.
Installation
Once both dictionary secrets exist, install from the marketplace registry, pointingsecrets.name and postgres.config.credentialsSecretName at them:
UI
CLI
Terraform
Pulumi
Configuration
LiteLLM Proxy
api_key in modelList points at an environment variable (os.environ/NAME), and every such name must appear in both providerEnv and your prerequisite secret. Adding a provider is therefore three edits: a new modelList entry, its variable name in providerEnv, and the key itself in the secret.
Prerequisite Secret
Access
Bundled PostgreSQL
The database is the postgres template as a subchart, so every knob of that template is available underpostgres.*. Its credentials come from the prerequisite database credentials secret you created (see Prerequisites).
Bundled Redis
Redis is the redis template as a subchart: a Sentinel-managed master-replica set that the proxy reaches through Sentinel (master namemymaster), so a failover is transparent to it. It holds the rate-limit and budget counters and the response cache that all proxy replicas share.
AUTH, create a dictionary secret holding the key password before you install, then set redis.redis.auth.fromSecret.enabled: true and redis.redis.auth.fromSecret.name to its name. Redis, Sentinel and the proxy all read it:
redis.sentinel.auth setting, including a plaintext redis.sentinel.auth.password. Redis keeps no password on disk, so changing it later follows Rotating the Redis Password.
Connecting
modelList:
publicAccess.enabled: false there is no public endpoint; reach the proxy from your machine through a tunnel instead, then use http://localhost:4000 (UI at http://localhost:4000/ui) in place of the canonical endpoint:
First Run
The Admin UI is gated by the master key from the first request, so there is no setup wizard to race — but the master key should not be what your applications use.Wait for the proxy to report ready
P1001 error, and restart once; it recovers on its own.Sign in to the Admin UI
/ui on the canonical endpoint (or the port-forward URL). The username is admin and the password is your LITELLM_MASTER_KEY.Mint a virtual key for each application
Point an OpenAI SDK at the proxy
model_name values from modelList, plus any models added through the UI while litellm.storeModelInDb is true.Operations
Managing Virtual Keys
Rather than handing out the master key, mint scoped virtual keys — each with its own budget, rate limits, and allowed models — and give those to your applications and teams. Spend is tracked per key in PostgreSQL.sk-… key usable on /v1/chat/completions and /v1/models. Keys, teams, and budgets can also be managed visually in the Admin UI at /ui. Rate limits are enforced across all proxy replicas only while Redis is enabled; see Scaling and Availability.
Backing Up
Scheduled backups of the bundled PostgreSQL are off by default. Enablingpostgres.backup.enabled: true adds one cron workload, RELEASE_NAME-postgres-backup, that runs pg_dumpall on the schedule and uploads a gzipped dump to your bucket under the configured prefix. Pick a postgres.backup.provider and complete the matching setup before installing or upgrading with it on.
- AWS S3
- Google Cloud Storage
- MinIO / S3-compatible
Create a bucket
postgres.backup.aws.bucket and postgres.backup.aws.region to match.Set up a cloud account
postgres.backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET_NAME) and set postgres.backup.aws.policyName to its name:Restoring a Backup
Each backup is a gzipped plain-SQLpg_dumpall archive. Restoring means replaying that SQL into the bundled PostgreSQL. The one transport that carries a dump into the container intact is cpln workload exec with --stdin and a base64 wrapper on both ends — raw binary piped through exec is corrupted:
CREATE ROLE and CREATE DATABASE statements, so a straight replay collides with the database the proxy already created and migrated on first boot; and no proxy replica may be connected while you drop and recreate that database before the replay. The restored data is only readable with the same LITELLM_SALT_KEY the dump was taken under.
Upgrading from 1.0.0
Version 1.1.0 adopted thepostgres 3.4.1 template, which stopped taking database credentials as values. Three keys were removed:
postgres.config.credentialsSecretName to its name, and remove the three keys. A values file that still carries an old key fails the render with the postgres template’s message (config.username was REMOVED in postgres 3.4.0).
helm upgrade after an install can restart the bundled PostgreSQL and Redis even when their values are unchanged, so expect a brief interruption.Upgrading from 1.1.0
Version 1.2.0 removedpostgres.credentials: the database credentials are now a prerequisite secret, the same one the postgres template requires on its own. Carrying the old key forward fails the render.
1.1.0 created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with — not new ones, or the proxy loses its database connection. Read your current values from the old secret (named by postgres.config.credentialsSecretName in the installed release, my-litellm-db-credentials by default) before the upgrade deletes it:
litellm / litellm were the 1.1.0 defaults; use your own values if you overrode them.) If you never overrode the password, it is literally change-me-litellm-pg — put that in the new secret, and rotate it afterwards as described under Important Notes.
Then upgrade with postgres.config.credentialsSecretName set to the new name and postgres.credentials removed. The old RELEASE_NAME-litellm-db secret is deleted too; nothing replaces it.
The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so LiteLLM briefly loses its database; it reconnects by itself. The new version of a workload can also wait for several minutes, with get-deployments reporting that it is not allowed to reveal the secret, while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
If you ran 1.1.0 with Redis auth on, redis.redis.auth.password was removed too (carrying it forward fails the render), and the chart-created RELEASE_NAME-redis-auth-password secret is deleted by the upgrade. Before upgrading, copy your current Redis password into a secret under a new name, keeping the same value so the replicas keep replicating while they restart:
redis.redis.auth.fromSecret.enabled: true, redis.redis.auth.fromSecret.name set to the new name, and redis.redis.auth.password removed. With Redis auth off in 1.1.0 (the default), just remove redis.redis.auth.password from your values. Version 1.2.1 also refuses any redis.sentinel.auth setting, so remove that block too if your values carry one.
Rotating the Redis Password
Only with Redis auth on. Redis keeps no password on disk, but a running Sentinel holds the one it started with, so a new password is applied live in three places before the proxy restarts. Each Redis node first accepts both passwords, which keeps replication and every connected client working throughout.rx and sx run a command on one Redis node or Sentinel (LOCATION is your GVC’s location; nodes and Sentinels are numbered from 0 up to redis.redis.replicas and redis.sentinel.replicas minus one):
litellm.replicas of 2 or more it rolls one replica at a time). Redis and Sentinel need no restart: when they next start they read the new password from the secret.
Scaling and Availability
litellm.replicas sets how many proxy replicas run. The proxy is stateless — all state lives in PostgreSQL and Redis — so replicas need no peer discovery and a rolling restart cycles one at a time. Set it to 2 or more for an always-on tier:
Troubleshooting
Deployment never becomes ready and the logs are empty
Deployment never becomes ready and the logs are empty
cpln helm install reported success, cpln logs returns nothing, and cpln workload get-deployments RELEASE_NAME-litellm --gvc GVC_NAME -o yaml shows The secret LITELLM_SECRET_NAME no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed. under status.versions[].message.Cause: The prerequisite dictionary secret named by secrets.name does not exist, or it is missing one of the keys the workload references (LITELLM_MASTER_KEY, LITELLM_SALT_KEY, or a name listed in litellm.providerEnv). The database credentials secret named by postgres.config.credentialsSecretName, or with Redis auth on the secret named by redis.redis.auth.fromSecret.name, may also be missing.Fix: Create the secret with every required key (see Prerequisites). The deployment recovers on its own once the secret exists; cpln workload force-redeployment RELEASE_NAME-litellm --gvc GVC_NAME skips the wait.Readiness never passes after the secret exists
Readiness never passes after the secret exists
/health/readiness keeps failing and the deployment does not reach ready.Cause: The readiness check is database-aware. The proxy cannot reach the bundled PostgreSQL, or the migration it runs on boot has not completed.Fix: Check that RELEASE_NAME-postgres is ready, then read the proxy’s logs:P1001 (cannot reach database server) followed by one restart on a cold install is normal and recovers on its own.Render fails because the Redis password secret name is required
Render fails because the Redis password secret name is required
litellm: redis.redis.auth.fromSecret.name is required when redis.redis.auth.fromSecret.enabled is true.Cause: redis.redis.auth.fromSecret.enabled is true with an empty redis.redis.auth.fromSecret.name.Fix: Create the Redis password secret and set redis.redis.auth.fromSecret.name to its name (see Bundled Redis), or set enabled back to false.Render fails because Sentinel auth must stay disabled
Render fails because Sentinel auth must stay disabled
litellm: redis.sentinel.auth must stay disabled — LiteLLM sends only the Redis master password, not a Sentinel password.Cause: Your values carry a redis.sentinel.auth setting — redis.sentinel.auth.password.enabled, a plaintext redis.sentinel.auth.password.value, or redis.sentinel.auth.fromSecret.enabled. The proxy authenticates to the Redis master only, so a Sentinel password would lock it out.Fix: Remove redis.sentinel.auth from your values. Use redis.redis.auth.fromSecret to protect the master; the same-GVC firewall is the boundary for Sentinel.Upgrade from 1.0.0 fails asking you to create a dictionary secret
Upgrade from 1.0.0 fails asking you to create a dictionary secret
config.username was REMOVED in postgres 3.4.0 and the postgres template’s advice to create a prerequisite dictionary secret.Cause: Your values file still carries postgres.config.username, postgres.config.password, or postgres.config.database.Fix: Put their values into the database credentials secret and remove the keys, as described in Upgrading from 1.0.0.Upgrade from 1.1.0 fails because postgres.credentials or redis.redis.auth.password was removed
Upgrade from 1.1.0 fails because postgres.credentials or redis.redis.auth.password was removed
postgres.credentials was removed in litellm 1.2.0 or redis.redis.auth.password was removed in litellm 1.2.0.Cause: Your values file still carries the 1.1.0 plaintext database credentials or Redis password.Fix: Create the prerequisite secrets holding your current values and remove the old keys, as described in Upgrading from 1.1.0.Requests are rejected with 401 on the public endpoint
Requests are rejected with 401 on the public endpoint
401, including on the public endpoint.Cause: The endpoint is key-gated. The request carries no Authorization: Bearer header, or the key is not the master key or a valid virtual key for the requested model.Fix: Send the master key or a virtual key minted for that model (see Managing Virtual Keys). Compare the master key you are sending with cpln secret reveal LITELLM_SECRET_NAME -o yaml.Important Notes
- Create every prerequisite secret before installing.
secrets.namemust be a dictionary secret withLITELLM_MASTER_KEY,LITELLM_SALT_KEY, and everylitellm.providerEnvkey;postgres.config.credentialsSecretNameone withusername,passwordanddatabase; and, with Redis auth on,redis.redis.auth.fromSecret.nameone withpassword. A missing one wedges the deployment silently. - Never rotate
LITELLM_SALT_KEY. It encrypts the provider keys stored in PostgreSQL; changing it corrupts every stored key. The master key may be rotated — force a redeployment ofRELEASE_NAME-litellmafterwards so the proxy picks up the new value. - Hand out virtual keys, not the master key. The public endpoint is key-gated, and virtual keys carry their own budgets, rate limits, and model lists.
- Keep Redis enabled for
litellm.replicasof 2 or more. Otherwise rate limits are counted per replica. - Redis is authless by default, bounded by the same-GVC firewall. To require a master password, create the Redis password secret and set
redis.redis.auth.fromSecret.enabled: truewith its name; changing it later follows Rotating the Redis Password. Sentinel auth must stay off. - Give each release its own database credentials secret. Secret names are org-wide.
- Rotating the database password needs
ALTER ROLEplus a forced redeployment of the proxy (not the database). PostgreSQL keeps the password it was initialized with, and a running workload keeps the values it started with. RunALTER ROLE DB_USERNAME PASSWORD 'NEW_PASSWORD';first, thencpln secret edit SECRET_NAME, then right awaycpln workload force-redeployment RELEASE_NAME-litellm --gvc GVC_NAME, and wait untilget-deploymentsshows a higher version number, alone and ready. Connections the proxy already has open keep working afterALTER ROLE; new ones fail until its redeploy finishes, so rotate in a quiet window. - PostgreSQL data survives proxy restarts and redeploys. To reset virtual keys and spend, uninstall the whole release — that deletes the volume set.