LiteLLM on macOS (Docker) — setup, config, maintenance, and usage supervision
This guide walks through running the LiteLLM Proxy on a MacBook Pro with Docker Desktop. It covers first-time setup, config.yaml and environment configuration, day-to-day maintenance, and how to monitor spend and usage.
What you get: an OpenAI-compatible gateway on http://localhost:4000, optional Admin UI, PostgreSQL for keys/spend/logs, and optional Prometheus metrics.
Table of contents
- Prerequisites
- Recommended layout on your Mac
- First-time setup
- Configuration
- Start, stop, and verify
- Using the proxy and Admin UI
- Maintaining the stack
- Supervising usage and cost
- Troubleshooting
- Security checklist (local dev)
- Official references
1. Prerequisites
| Requirement | Notes |
|---|---|
| macOS | Tested pattern matches MacBook Pro on Apple Silicon (M2 Pro reference) or Intel. |
| Docker Desktop | Install from docker.com. On a 32 GB Mac, Docker Desktop → Settings → Resources defaults are plenty; allocate ~6–8 GB memory to comfortably run Postgres + Prometheus + proxy with headroom for other apps. |
| curl | Preinstalled on macOS; used to download upstream files. |
| Provider API keys | Azure OpenAI, OpenAI, Anthropic, Bedrock, etc., depending on models you route. |
| Optional: 1Password CLI | Useful for generating strong LITELLM_SALT_KEY / LITELLM_MASTER_KEY and storing secrets (same pattern as OP_CLAUDE_TOKEN_REF in this repo’s Makefile). |
Apple Silicon (M2 Pro): Official LiteLLM and postgres:16 / prom/prometheus images are multi-arch and pull native arm64 builds, so no special configuration is needed. (Only if a future image lacks an arm64 build would you enable Docker Desktop’s “Use Rosetta for x86/amd64 emulation” as a fallback.)
Do not commit .env, real config.yaml with secrets, or database dumps to git.
2. Recommended layout on your Mac
Keep LiteLLM outside application repos (optional but cleaner). Example:
~/litellm-local/
├── docker-compose.yml # from upstream LiteLLM repo
├── prometheus.yml # optional; bundled in official compose
├── .env # secrets — chmod 600, never commit
├── config.yaml # model routing (mount into container)
└── backups/ # pg_dump outputYou can copy the compose file from the LiteLLM GitHub docker-compose.yml. The upstream stack defines three services:
- litellm — proxy on port
4000 - db — PostgreSQL 16 with a named volume
litellm_postgres_data - prometheus — metrics UI on port
9090(optional but included in the default compose)
3. First-time setup
3.1 Create the project directory
mkdir -p ~/litellm-local/backups
cd ~/litellm-local3.2 Download Docker Compose and Prometheus config
curl -fsSLO https://raw.githubusercontent.com/BerriAI/litellm/main/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/BerriAI/litellm/main/prometheus.yml3.3 Create .env (master key, salt key, UI login)
The proxy requires a master key (admin API bearer token). If you use the database (default in compose), you also need a salt key to encrypt stored provider credentials.
# Master key — used as Bearer token for admin API and UI (do not use the example value)
printf 'LITELLM_MASTER_KEY="sk-%s"\n' "$(openssl rand -hex 16)" > .env
# Salt key — REQUIRED for DB-backed credential storage.
# Treat like a vault key: if you lose it, encrypted provider keys cannot be recovered.
# If you change it after models/keys were stored, those credentials break.
printf 'LITELLM_SALT_KEY="sk-%s"\n' "$(openssl rand -hex 32)" >> .env
# Admin UI (optional but recommended)
echo 'UI_USERNAME=admin' >> .env
echo 'UI_PASSWORD=change-me-on-first-login' >> .env
chmod 600 .envReplace the openssl substitutions with real values (or use a password manager generator). Official docs: UI credentials.
3.4 Add provider credentials to .env
Examples (add only what you use):
# OpenAI
echo 'OPENAI_API_KEY="sk-..."' >> .env
# Azure OpenAI
echo 'AZURE_API_BASE="https://your-resource.openai.azure.com/"' >> .env
echo 'AZURE_API_KEY="..."' >> .env
# Anthropic (direct)
echo 'ANTHROPIC_API_KEY="sk-ant-..."' >> .envReference env vars in config.yaml as os.environ/VAR_NAME so secrets stay out of the config file.
3.5 Enable config.yaml in Compose
In docker-compose.yml, under the litellm service, uncomment the volume and command blocks so the proxy starts with your config:
volumes:
- ./config.yaml:/app/config.yaml
command:
- "--config=/app/config.yaml"Also delete the build: block from the litellm service. Upstream ships both an image: and a build: context: .:
litellm:
build: # <-- remove these lines
context: . # <-- remove
args: # <-- remove
target: runtime # <-- remove
image: docker.litellm.ai/berriai/litellm:main-stableYou only downloaded docker-compose.yml (not the full repo), so there is no Dockerfile in ~/litellm-local/. As long as the image pulls, the build: block is ignored — but if a pull ever fails, Compose falls back to building from context: . and errors out. Removing the block makes the stack pull-only and avoids that trap.
Create a minimal config.yaml (see section 4).
3.6 Pull images and start
docker compose pull
docker compose up -d
docker compose psWait until the litellm service is healthy (compose defines a healthcheck that polls http://localhost:4000/health/liveliness inside the container).
3.7 Load secrets into your shell
The keys in .env are loaded into the container (via env_file), not into your terminal. Many curl examples below reference ${LITELLM_MASTER_KEY}, so export it into your current shell first:
# Load .env into the current shell (strips the surrounding quotes from values)
set -a; source ./.env; set +a
# Verify it's set (should print your sk-... master key)
echo "$LITELLM_MASTER_KEY"Re-run this in any new terminal before using the admin/spend APIs.
4. Configuration
LiteLLM configuration splits into environment (.env) and YAML (config.yaml). With STORE_MODEL_IN_DB=True (default in upstream compose), you can also add or adjust models via the Admin UI; YAML remains the best place for version-controlled defaults.
4.1 Minimal config.yaml
model_list:
- model_name: gpt-4o
litellm_params:
model: azure/my-deployment-name
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
api_version: "2025-01-01-preview"
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
litellm_settings:
drop_params: true
set_verbose: falseDATABASE_URL is already set in docker-compose.yml for the bundled Postgres service (postgresql://llmproxy:dbpassword9090@db:5432/litellm). For a custom DB, change both compose and general_settings.database_url.
Docs: proxy config, managed files / DB.
4.2 Multiple models and aliases
Add one model_list entry per public alias (model_name). Clients call model: <model_name>; LiteLLM maps to the provider model string.
model_list:
- model_name: claude-sonnet
litellm_params:
model: anthropic/claude-sonnet-4-20250514
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: gpt-4o-mini
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY4.3 Router, fallbacks, and budgets (optional)
router_settings:
routing_strategy: simple-shuffle
num_retries: 2
litellm_settings:
callbacks: ["prometheus"]For team/key budgets and rate limits, use the database + Admin UI or /key/generate API (see section 8).
4.4 Reloading config
After editing config.yaml:
docker compose restart litellmFor changes made only in the UI / DB (STORE_MODEL_IN_DB), a restart is often not required; YAML-only changes always need a restart unless you use hot-reload features documented for your LiteLLM version.
4.5 Environment variables cheat sheet
| Variable | Purpose |
|---|---|
LITELLM_MASTER_KEY | Admin bearer token; required for /key/*, spend APIs, UI. |
LITELLM_SALT_KEY | Encrypts provider keys at rest in Postgres. Immutable after data exists. |
DATABASE_URL | Postgres connection string for virtual keys, spend logs, UI data. |
STORE_MODEL_IN_DB | When True, models can be managed in UI. |
UI_USERNAME / UI_PASSWORD | Admin UI login. |
LITELLM_LOG | e.g. DEBUG for troubleshooting (verbose; do not leave on in shared environments). |
Provider *_API_KEY | Passed through to os.environ/... in YAML. |
5. Start, stop, and verify
5.1 Lifecycle commands
cd ~/litellm-local
# Start in background
docker compose up -d
# Follow logs
docker compose logs -f litellm
# Stop (keep data volumes)
docker compose stop
# Stop and remove containers (volumes persist)
docker compose down
# Destroy containers AND named volumes (deletes Postgres data)
docker compose down -v5.2 Health checks
# Liveness (used by Docker healthcheck)
curl -sS http://localhost:4000/health/liveliness
# Readiness (when configured)
curl -sS http://localhost:4000/health/readiness5.3 Smoke test (chat completion)
Use your master key or a generated virtual key:
export LITELLM_KEY="sk-your-master-or-virtual-key"
curl -sS http://localhost:4000/v1/chat/completions \
-H "Authorization: Bearer ${LITELLM_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Say hello in one word."}],
"max_tokens": 10
}'5.4 Point tools at local LiteLLM
| Client | Typical settings |
|---|---|
| OpenAI SDK | base_url=http://localhost:4000, api_key=<virtual-or-master-key> |
| Claude Code / Anthropic-compatible | ANTHROPIC_BASE_URL=http://localhost:4000, ANTHROPIC_AUTH_TOKEN=<key> (if using Anthropic-compatible routes on the proxy) |
| Cursor / IDE | Custom OpenAI-compatible endpoint → http://localhost:4000/v1 |
Match model names to your model_name aliases in config.yaml.
6. Using the proxy and Admin UI
6.1 Admin UI
Open http://localhost:4000/ui (path may vary slightly by version; check logs on first start).
Sign in with UI_USERNAME / UI_PASSWORD from .env. The UI supports:
- Models and credentials (when
STORE_MODEL_IN_DB=True) - Virtual keys, teams, users
- Spend dashboards and request logs (with DB enabled)
Docs: LiteLLM UI.
6.2 Virtual keys (recommended for apps)
Do not hand out the master key to every app. Generate a scoped key:
curl -sS -X POST 'http://localhost:4000/key/generate' \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"key_alias": "my-laptop-cursor",
"max_budget": 5.0,
"rpm_limit": 60
}'Store the returned key in 1Password or your shell profile; rotate by generating a new key and revoking the old one in the UI.
7. Maintaining the stack
7.1 Upgrading LiteLLM
- Back up Postgres (see below).
- Pin a version in
docker-compose.ymlinstead of floatingmain-stablewhen you need reproducibility, for example:image: docker.litellm.ai/berriai/litellm:1.86.2 - Pull and recreate:
docker compose pull litellm
docker compose up -d litellm
docker compose logs -f litellmRead release notes before major jumps; database migrations run on startup when the proxy connects to Postgres.
7.2 Backing up PostgreSQL
From the host (default compose credentials):
docker exec litellm_db pg_dump -U llmproxy -d litellm -F c \
-f /tmp/litellm_backup.dump
docker cp litellm_db:/tmp/litellm_backup.dump \
~/litellm-local/backups/litellm_$(date +%Y%m%d).dumpRestore only into a fresh volume or after docker compose down with a clear recovery plan; test restores on a copy first.
Official guidance: backup before upgrade.
7.3 Upgrading Postgres
Prefer upgrading Postgres by dumping from the old container and restoring into a new major version container, rather than in-place major upgrades on the named volume.
7.4 Disk and Docker Desktop
- Named volume
litellm_postgres_datagrows with spend logs and key metadata. - Prometheus retains 15 days by default. This is set in the
prometheusservicecommand:insidedocker-compose.yml(--storage.tsdb.retention.time=15d), not inprometheus.yml(which only holds the scrape config). - In Docker Desktop → Settings → Resources, monitor disk use; prune unused images periodically:
docker system df
docker image prune -f7.5 Rotating secrets
| Secret | Safe to rotate? | Action |
|---|---|---|
UI_PASSWORD | Yes | Change .env, docker compose up -d. |
LITELLM_MASTER_KEY | Yes, with care | Update .env and general_settings.master_key; regenerate virtual keys if needed. |
LITELLM_SALT_KEY | No (after data exists) | Requires re-entering all provider credentials. Plan salt at install time. |
| Virtual keys | Yes | Revoke in UI; issue new keys. |
7.6 Config and compose in git
Track sanitized config.yaml (no literal API keys) and a docker-compose.override.yml if you customize ports. Keep .env in .gitignore.
8. Supervising usage and cost
LiteLLM records spend in Postgres when DATABASE_URL is configured. Use the Admin UI for exploration and the HTTP API for automation.
8.1 Admin UI
- Dashboards: spend by key, team, model, time range.
- Logs: request-level metadata (hashed key, model, tokens, USD spend).
- Alerts: configure budget and rate-limit notifications per monitoring docs.
8.2 Spend API (master key)
# Info + spend for one virtual key
curl -sS "http://localhost:4000/key/info?key=<virtual-key>" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}"
# Spend log for a specific request id (from response headers / body)
curl -sS "http://localhost:4000/spend/logs?request_id=<id>" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}"Endpoint names vary by LiteLLM version. For aggregated spend across keys/teams/models, prefer the Admin UI dashboards or the
/global/spend/*family; confirm exact routes against your pinned version’s API docs (http://localhost:4000→/docs).
Log entry fields include spend, total_tokens, model_group, team_id, end_user, and request_tags. Details: cost tracking.
8.3 Prometheus (optional service in compose)
- Ensure
litellm_settings.callbacksincludes"prometheus"inconfig.yaml. - Open http://localhost:9090 and query metrics such as:
litellm_requests_metriclitellm_remaining_team_budget_metric- Latency metrics (
litellm_request_total_latency_metric, etc.)
Configure label cardinality in prometheus_metrics_config if you need fewer labels on high-traffic deployments. Docs: Prometheus.
8.4 Operational habits
- Set per-key
max_budgetandrpm_limitfor every non-human integration. - Use
key_aliasandrequest_tagsin client calls to attribute spend to projects. - Review weekly spend in the UI; export or scrape Prometheus for trends.
- Watch
docker compose logs litellmduring incidents; switchLITELLM_LOG=DEBUGonly temporarily.
8.5 Example budget-aware key for CI
curl -sS -X POST 'http://localhost:4000/key/generate' \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"key_alias": "ci-nightly",
"max_budget": 0.50,
"rpm_limit": 10,
"duration": "30d"
}'Adjust duration and limits per virtual keys documentation.
9. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Container unhealthy | DB not ready or bad DATABASE_URL | docker compose logs db litellm; wait for pg_isready. |
401 on API | Wrong bearer token | Use virtual key or master key; no extra quotes in curl. |
Provider 401 / 404 | Wrong model string or API base | Fix litellm_params.model and env vars; test provider outside LiteLLM. |
| Encrypted key errors after env change | LITELLM_SALT_KEY changed | Restore previous salt or re-enter credentials in UI. |
Port 4000 in use | Another service | Change host port in compose, e.g. "4001:4000". |
| Slow first request | Cold start / model auth | Expected; watch logs for timeouts. |
| High disk use | Spend logs / Prometheus | Prune old logs per docs; reduce retention. |
Enable debug logging:
# In .env
LITELLM_LOG=DEBUG
docker compose up -d litellm10. Security checklist (local dev)
- Bind to localhost only unless you explicitly need LAN access; avoid exposing port
4000on public networks. - Never commit
.envor database dumps. - Use virtual keys with budgets for IDE and script access.
- Rotate
UI_PASSWORDand virtual keys if a laptop is lost or keys leak. - Treat
LITELLM_MASTER_KEYlike a root password. - On shared Macs, use separate virtual keys per person and disable UI password reuse.
11. Official references
- Docker quick start
- Deploy / production notes
- Config file
- Admin UI
- Virtual keys
- Cost tracking / spend logs
- Prometheus
- Upstream
docker-compose.yml
Last updated: 2026-06-16 — align image tags and API paths with your pinned LiteLLM version when upgrading.