Set up Codex against a LiteLLM gateway
You are configuring a machine so the Codex CLI sends its requests to a local LiteLLM gateway instead of straight to OpenAI. The gateway holds the real OpenAI key; Codex only ever sees a virtual key you can revoke.
scripts/setup.sh does the deterministic work. Your job is to get its inputs
right, read its output, and fix what it reports. Do not reimplement it with raw
docker commands; it handles port interpolation, key minting, and config backup
in ways that are easy to get subtly wrong.
Getting the repo
You may be handed this task with nothing on disk yet.
If you are not already inside the repo, clone it and change into it:
git clone https://github.com/BerriAI/codex-litellm-stack
cd codex-litellm-stack
If a codex-litellm-stack directory already exists, work in it rather than
cloning a second copy. Confirm you are at the repo root before going on:
ls scripts/setup.sh docker-compose/docker-compose.yml
Every path below is relative to that root.
Before you run anything
Check these and resolve them first, because each produces a confusing failure several steps later:
- Docker is running.
docker infomust succeed. If it doesn't, tell the user to start Docker Desktop; you can't start it for them on macOS. - An OpenAI API key is available. On a fresh clone, setup creates the
ignored
docker-compose/litellm.envfromlitellm.env.exampleand prompts for the key. For unattended setup, put the key in that file first. Never invent, guess, or reuse a key from elsewhere in the conversation, and don't echo the key back in your reply once you have it. - Port 4000 is free. Run
lsof -i :4000. If something holds it, pass--port <n>rather than killing the other process.
Running it
./scripts/setup.sh # prompts for the OpenAI key if needed
./scripts/setup.sh -y # unattended; the key must already be in litellm.env
./scripts/setup.sh --port 4123 # different host port
./scripts/setup.sh --enterprise # also emit the fleet config (see codex-litellm-fleet)
If you already wrote the key into litellm.env yourself, use -y so the
script doesn't block on a prompt you can't answer.
Setup ends by running scripts/doctor.sh. Read that output. The "Ready."
banner above it means the script finished, not that the connection works.
What it changed
Say this back to the user plainly, because two of these touch files outside the repo:
docker-compose/litellm.envgot a generated master key, plus the OpenAI key if one was entered. It is now chmod 600. Do not commit it.~/.codex/config.tomlwas overwritten. Any pre-existing config was copied toconfig.toml.bak.<timestamp>first. If the user had settings there they care about (MCP servers, sandbox policy, approval mode), offer to merge them back from the backup.~/.codex/fast.config.toml,deep.config.toml, andastra.config.tomlwere installed as current Codex profile files. Existing files with those names were backed up first.dist/start-codex.shexportsLITELLM_API_KEYand launches Codex. It contains a live credential and is gitignored. Keep it that way.- On macOS,
~/Library/LaunchAgents/com.codex-litellm-stack.env.plistwas installed and loaded. It putsLITELLM_API_KEYinto the GUI login session at every login, which is the only way the Codex desktop app can see it. It holds the key in plaintext at chmod 600.--no-desktopskips it;scripts/uninstall-desktop-env.shremoves it. - Two containers,
litellmandpostgres, under the compose projectcodex-litellm-stack.
Verifying it actually works
Don't declare success on exit code alone. Run a real turn:
export LITELLM_API_KEY="$(sed -n 's/^export LITELLM_API_KEY=//p' dist/start-codex.sh | tr -d '"')"
codex exec "reply with the word CONNECTED and nothing else"
Then confirm the request reached the gateway rather than OpenAI directly:
docker compose -f docker-compose/docker-compose.yml --project-directory docker-compose \
logs litellm --tail 20
You should see a POST to /v1/responses. If Codex answered but nothing shows
in the gateway log, ~/.codex/config.toml isn't being read. Check whether
CODEX_HOME is set to somewhere else.
The desktop app
Nothing extra to run. Setup already installed the LaunchAgent, so codex app
or the Dock icon works with the same config and key as the CLI.
Two things to tell the user, because both look like bugs:
- An app that was already open won't have the key. It kept the environment it launched with. Quit and reopen it.
- A session's model is fixed when the session starts, and a custom provider
has no in-app model picker.
codex app -c model=gpt-6-astrasets it at launch; otherwise editmodelin~/.codex/config.tomland start a new session.
Verify without opening anything:
launchctl getenv LITELLM_API_KEY # non-empty means the app will find it
If the user objects to the key sitting in the login session environment, that
is a fair objection. ./scripts/setup.sh --no-desktop keeps it CLI-only, and
scripts/uninstall-desktop-env.sh undoes an install.
Choosing models
There are no aliases to configure. The gateway publishes OpenAI's model ids
under their own names plus a "*" catch-all, so any model id that works
against OpenAI works here. Model choice lives entirely on the Codex side:
codex # the default in config.toml
codex -m gpt-6-astra # any model, ad hoc
codex --profile deep # loads ~/.codex/deep.config.toml
Edit docker-compose/litellm-config.yaml only to restrict what the gateway
will serve, or to attach budgets and rate limits to specific models. If you
remove the "*" entry, every model must then be listed explicitly.
If something fails
Use the codex-litellm-troubleshoot skill. Don't guess at fixes; each
doctor.sh check failure names its own cause.