Route the Codex CLI and desktop app through a LiteLLM gateway with one setup script. LiteLLM logs requests, tracks spend, and issues a separate virtual key for each person. Codex clients never receive the OpenAI API key.
Bring an OpenAI API key, the Codex CLI (npm i -g @openai/codex), and Docker
Compose.
Paste this into Codex from an empty directory:
Clone
https://github.com/BerriAI/codex-litellm-stack, cd into it, and set up my Codex CLI and desktop app to use its LiteLLM gateway. ReadAGENTS.mdand the skills in.agents/skills/, then followcodex-litellm-setup. Ask for my OpenAI API key when the script needs it. Never invent or print a key. Finish by running./scripts/doctor.shand a real Codex turn, confirm the request in the gateway logs, and list every file changed outside the repo.
The repo includes three skills:
| Skill | Job |
|---|---|
codex-litellm-setup |
preflight, setup, and end-to-end verification |
codex-litellm-troubleshoot |
diagnose gateway, key, model, and streaming failures |
codex-litellm-fleet |
prepare secret-free MDM files and per-user keys |
git clone https://github.com/BerriAI/codex-litellm-stack
cd codex-litellm-stack
./scripts/setup.shThe script:
- creates the ignored
docker-compose/litellm.envat mode 600; - starts LiteLLM and Postgres;
- mints a virtual key for Codex;
- backs up and writes
~/.codex/config.toml; - installs
fast,deep, andastraprofile files; and - on macOS, installs a LaunchAgent for the desktop app.
Use another port if 4000 is occupied:
./scripts/setup.sh --port 4010Start Codex and verify the request path:
./dist/start-codex.sh
./scripts/doctor.sh
codex exec "reply with the word CONNECTED and nothing else"
docker compose -f docker-compose/docker-compose.yml logs litellm --tail 20The log should contain a POST to /v1/responses.
| Key | Stored in | Used by |
|---|---|---|
OPENAI_API_KEY |
docker-compose/litellm.env |
LiteLLM, to authenticate upstream OpenAI requests |
LITELLM_MASTER_KEY |
the same file | gateway administration and virtual-key issuance |
| virtual key | dist/start-codex.sh and the LaunchAgent |
Codex requests; budget and revoke it independently |
The OpenAI key is stored only on the gateway host. LiteLLM sends it to
api.openai.com over TLS; it is never distributed to Codex clients.
Setup writes this base configuration:
model = "gpt-5.6-terra"
model_provider = "litellm"
[model_providers.litellm]
name = "LiteLLM Gateway"
base_url = "http://localhost:4000/v1"
env_key = "LITELLM_API_KEY"
wire_api = "responses"
stream_idle_timeout_ms = 7200000Current Codex versions load named profiles from separate files:
| Command | File | Model |
|---|---|---|
codex --profile fast |
~/.codex/fast.config.toml |
gpt-5.6-luna, low effort |
codex --profile deep |
~/.codex/deep.config.toml |
gpt-5.6-sol, high effort |
codex --profile astra |
~/.codex/astra.config.toml |
gpt-6-astra, high effort |
Model IDs pass through unchanged. The gateway publishes the explicit model
list plus a "*" catch-all. Remove the catch-all to restrict access.
The desktop app reads ~/.codex/config.toml, but apps opened from Finder, the
Dock, or Spotlight do not inherit shell variables. On macOS, setup installs
~/Library/LaunchAgents/com.codex-litellm-stack.env.plist so the app receives
its virtual key at login. Quit and reopen Codex after setup.
The plist is mode 600, but every process in the login session can read the virtual key. Keep the desktop integration only if that tradeoff is acceptable:
./scripts/setup.sh --no-desktop
./scripts/uninstall-desktop-env.sh./scripts/setup.sh --public-url https://llm-gw.corp.example --enterpriseThis writes four secret-free files to dist/: config.toml and the three
profile files. Distribute them to ~/.codex/ with MDM and deliver one
LITELLM_API_KEY per person through your secret manager.
Before a fleet rollout, provide TLS, durable Postgres, unbuffered SSE, and a rollback path. The included Compose stack is a local target, not a production deployment.
AGENTS.md instructions for coding agents
.agents/skills/ setup, troubleshooting, and fleet procedures
docker-compose/ LiteLLM, Postgres, config, and profile templates
scripts/setup.sh configure and verify the stack
scripts/doctor.sh test the complete request path
docs/index.html GitHub Pages site
dist/ generated files containing a live virtual key
./scripts/setup.sh
--port <n> gateway port (default 4000)
--public-url <url> URL clients use
-y, --yes fail instead of prompting
--enterprise generate MDM files
--no-desktop skip the macOS LaunchAgent
./scripts/doctor.sh
--base <url> gateway URL
--model <name> model to test (default gpt-5.6-terra)
docker compose down -v deletes every issued virtual key and all spend
history. Use restart litellm unless you intend to erase the Postgres volume.
MIT. See LICENSE.