teachyou.ai academy
← All posts
Codexconfig.tomlOpenAI Codex CLIdeveloper toolscoding agents

The Codex config.toml, Explained

Pramod Dutta · Jul 6, 2026 · 12 min read

Your codex config lives in a single TOML file, and once you understand its shape you stop guessing at flags on every command line. This guide walks through every section of config.toml for the Codex CLI: where the file lives, what each key controls, and how to build profiles you can switch between with one flag. If you have ever wondered why Codex asked for approval on a command you expected to run automatically, or how to point it at a different model provider, the answer is almost always in this file.

Where Codex Looks for config.toml

Codex CLI reads its configuration from a single file on startup:

~/.codex/config.toml

That directory, ~/.codex, is also where Codex stores session logs, auth tokens, and history, so treat it as the CLI's home directory. If the file does not exist yet, Codex runs on built-in defaults: an approval policy that asks before anything risky, a sandboxed shell, and whatever model was set during login or the last codex login flow.

You do not have to hand-edit the file from scratch. Running codex once creates the directory, and many settings can be set with command-line flags that Codex will happily let you promote into the file once you know you want them permanently. A common workflow looks like this:

mkdir -p ~/.codex
touch ~/.codex/config.toml

Then open it in your editor of choice and start adding keys. TOML is forgiving about ordering, so you can group settings however makes sense to you, top-level keys first, then tables like [mcp_servers.something] further down.

One detail that trips people up: Codex also accepts a -c (or --config) flag on the command line for one-off overrides, in key=value form. Those overrides win over the file for that single invocation, but they never get written back to disk. Use the file for anything you want to persist, and the flag for anything you are just testing.

Core codex config Settings You Will Touch First

A handful of top-level keys cover almost everything a new user needs. Here is a minimal but realistic starting point:

model = "gpt-5.1-codex"
model_provider = "openai"
approval_policy = "on-request"
sandbox_mode = "workspace-write"

Walking through each:

  • model: the model Codex will use for the coding agent loop. You can override this per-session with codex --model <name>, but setting a default here means you do not have to type it every time.
  • model_provider: which entry in your model_providers table (or the built-in openai provider) Codex should talk to. This matters once you start routing through a proxy, a self-hosted gateway, or an alternate provider that speaks an OpenAI-compatible API.
  • approval_policy: controls how often Codex stops to ask before running a command. This is the single setting that most changes how the tool "feels" day to day, so it gets its own section below.
  • sandbox_mode: controls what the agent's shell commands are allowed to touch on disk and over the network. Also covered in detail below.

Beyond these four, a few other top-level keys are worth knowing:

  • disable_response_storage: set to true if you do not want conversation data retained server-side by the model provider. Useful in regulated environments.
  • notify: a path to a script Codex will invoke on certain events (like a turn completing), handy for wiring desktop notifications or a sound when a long task finishes.
  • file_opener: which editor scheme Codex uses when it prints "click to open" style links in its output, for example vscode versus a plain file path.
  • hide_agent_reasoning: when true, suppresses the intermediate reasoning summaries Codex prints while it works, which some people find noisy and others find essential for trust.

None of these require restarting anything beyond your next codex invocation. TOML changes are read fresh each time the CLI starts.

approval_policy and sandbox_mode: The Two Settings That Matter Most

If you only remember two keys from this whole codex config, make them these two, because together they define how much Codex can do without stopping to ask you first.

approval_policy accepts a small set of values:

  • untrusted: Codex asks before almost every action, including read commands in some setups. This is the most conservative option, good for your first few sessions in a new repo.
  • on-failure: Codex runs commands without asking, but if a command fails, it stops and asks how you want to proceed. A good middle ground once you trust the agent with routine work.
  • on-request: Codex decides for itself, per action, whether the action is risky enough to warrant asking. This tends to be the most natural default for day-to-day coding.
  • never: Codex never asks. Combine this only with a sandbox mode you are fully comfortable with, since there is no human checkpoint left.

sandbox_mode controls the blast radius of anything Codex actually executes:

  • read-only: the agent can read files and run read-only commands, but cannot write to disk or hit the network. Good for exploration, code review, and planning tasks where you do not want any side effects.
  • workspace-write: the agent can write inside the current project directory (and a few explicitly allowed paths) but is still fenced off from the rest of your filesystem and, depending on platform, from arbitrary network access.
  • danger-full-access: no sandbox. The agent's shell commands run exactly like commands you typed yourself. Reserve this for containers, throwaway VMs, or situations where you are actively supervising every step.

A sane default for local development on a real project looks like this:

approval_policy = "on-request"
sandbox_mode = "workspace-write"

That combination lets Codex read and edit files, run tests, and install dependencies inside your project without interrupting you for every step, while still asking before anything that reaches outside the workspace or looks destructive. If you are running Codex inside an already-isolated container (say, a disposable Docker image spun up just for this task), it is common to relax both settings:

approval_policy = "never"
sandbox_mode = "danger-full-access"

Just be deliberate about it. The sandbox is the actual safety boundary; the approval policy is a human-in-the-loop convenience on top of it. Turning off both at once on your main machine means Codex behaves exactly like a shell script you wrote and never reviewed.

model_providers: Pointing codex config at a Different Backend

By default Codex talks to OpenAI's API using whatever credentials you set up with codex login. If you want to route through a different endpoint, whether that is an OpenAI-compatible proxy, a self-hosted gateway, or a different vendor entirely, you define it under model_providers:

[model_providers.my_proxy]
name = "My Internal Proxy"
base_url = "https://llm-gateway.internal.example.com/v1"
env_key = "MY_PROXY_API_KEY"

Then point the top-level model_provider key at it:

model_provider = "my_proxy"

The env_key field tells Codex which environment variable to read the API key from, so the key itself never has to live in the TOML file. Export it in your shell profile:

export MY_PROXY_API_KEY="your-key-here"

This pattern is the same one you would use to point Codex at a local model server that exposes an OpenAI-compatible /v1/chat/completions or /v1/responses endpoint. As long as the base URL and auth header shape line up, Codex does not care whether the bytes on the other end came from a hosted API or a process running on localhost.

You can define as many providers as you like under model_providers and switch between them by changing one line, or by overriding model_provider on the command line for a single run:

codex -c model_provider=my_proxy

The mcp_servers Block: Giving Codex Tools

One of the most useful sections in any real-world codex config is mcp_servers. This is where you register Model Context Protocol servers that Codex can call out to during a session, things like a database client, a browser automation tool, or an internal API wrapper.

Each server gets its own table:

[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/projects"]

[mcp_servers.postgres]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-postgres"]
env = { DATABASE_URL = "postgres://localhost/mydb" }

Every entry needs a command (the executable Codex will spawn) and typically an args list. The optional env table lets you pass environment variables to that specific server process without polluting your whole shell environment. This is the right place for connection strings and scoped tokens that only that one tool needs.

Once a server is registered, Codex discovers its tools automatically at session start and can call them the same way it calls its built-in shell and file-editing tools. There is no separate registration step inside a conversation, the moment the server is in config.toml, it is available.

A couple of practical notes from running this in real projects:

  • Keep the server list scoped to what a given project actually needs. Every registered MCP server adds tool descriptions to the model's context, and a long list of unused tools is pure overhead.
  • If a server needs a long-lived credential, prefer reading it from an environment variable you set in your shell profile rather than hardcoding it directly in env, especially if config.toml might ever end up in a dotfiles repo.
  • Test a new server outside Codex first (run the command and args by hand) before wiring it in, so you know failures are about Codex integration and not about the server itself.

Profiles: Multiple Setups in One codex config

If you bounce between contexts, a strict read-only review setup for one repo, a fully autonomous container setup for another, profiles let you define named bundles of settings and pick one at launch time instead of hand-editing the file each time.

[profiles.review]
approval_policy = "untrusted"
sandbox_mode = "read-only"

[profiles.autonomous]
approval_policy = "never"
sandbox_mode = "danger-full-access"
model = "gpt-5.1-codex"

[profiles.default]
approval_policy = "on-request"
sandbox_mode = "workspace-write"

Select one with the --profile flag:

codex --profile review
codex --profile autonomous

Any key you do not set inside a profile falls back to the top-level default in the same file. This means you can keep your everyday settings at the top level and only override the two or three keys that actually change between profiles, rather than duplicating the whole file per use case.

Profiles are also a clean way to keep secrets and MCP server lists scoped. If your autonomous profile is meant to run inside a disposable container, you might register a different (more permissive) set of MCP servers for it than for your review profile running against a production database.

A Complete Example config.toml Walkthrough

Putting the pieces together, here is a fuller example that reflects a typical setup for someone working across a couple of repos with a local Postgres instance and an internal proxy:

model = "gpt-5.1-codex"
model_provider = "openai"
approval_policy = "on-request"
sandbox_mode = "workspace-write"
file_opener = "vscode"

[model_providers.internal_proxy]
name = "Internal LLM Gateway"
base_url = "https://llm-gateway.internal.example.com/v1"
env_key = "INTERNAL_PROXY_KEY"

[mcp_servers.postgres]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-postgres"]
env = { DATABASE_URL = "postgres://localhost/devdb" }

[profiles.review]
approval_policy = "untrusted"
sandbox_mode = "read-only"

[profiles.ci]
approval_policy = "never"
sandbox_mode = "danger-full-access"
model_provider = "internal_proxy"

Read top to bottom: the defaults at the top apply unless a profile overrides them, the model_providers table defines an alternate backend without touching the default openai provider, the mcp_servers table wires in a database tool scoped with its own environment variable, and the two profiles at the bottom give you a locked-down review mode and a fully autonomous CI mode, both selectable with --profile without ever editing the file again.

Save that structure as a starting template and prune what you do not need. Most projects end up with the top-level defaults, one or two MCP servers, and maybe one extra profile for CI or containerized runs.

Common Mistakes When Editing config.toml

A short list of things that cause silent, confusing failures:

  • Wrong table nesting. [mcp_servers.postgres] and [mcp_servers.postgres.env] are different tables. If you are not sure, use the inline table syntax shown above (env = { KEY = "value" }) instead of a separate [mcp_servers.postgres.env] block, it is less error-prone.
  • Quoting values that look like other types. TOML is picky: sandbox_mode = workspace-write (no quotes) will fail to parse, while sandbox_mode = "workspace-write" works. Always quote string values.
  • Forgetting the key changes per Codex version. Key names have shifted as Codex has evolved (older setups used different names for what is now sandbox_mode, for example). If a key silently has no effect, check that it still matches the version of the CLI you have installed rather than assuming your file is broken.
  • Putting secrets directly in the file. Prefer env_key and shell-exported variables over literal API keys in config.toml, particularly if the file lives anywhere near a dotfiles repository that gets synced or shared.
  • Expecting `-c` overrides to persist. Command-line -c key=value overrides apply only to that invocation. If you like the setting, copy it into the file.

FAQ

Where exactly does Codex look for config.toml? At ~/.codex/config.toml by default. That same ~/.codex directory also holds auth state and session history, so back it up as a unit if you want to preserve your setup across machines.

What is the safest starting sandbox_mode for a new project? read-only if you just want Codex to explore and suggest changes without touching disk, or workspace-write paired with approval_policy = "on-request" if you want it to actually edit files and run tests while still checking in before anything risky.

Can I use environment variables inside config.toml values? Not directly as string interpolation inside arbitrary keys, but the env_key field on model_providers and the env table on mcp_servers both let you reference environment variables by name so the actual secret never has to be written into the file.

How do I switch between two different setups quickly? Define them as separate entries under [profiles.<name>] and launch with codex --profile <name>. This is cleaner than maintaining two separate config files or hand-editing before every session.

Does editing config.toml require restarting anything? No daemon to restart. Codex reads the file fresh on every codex invocation, so save your changes and start a new session to pick them up.

What happens if config.toml has a syntax error? Codex will fail to start and report a parse error pointing at the offending line. Since TOML errors are usually about missing quotes or mismatched table nesting, checking those two things first resolves most of them quickly.

Is it safe to commit config.toml to a shared repo? Only if you have kept secrets out of it (using env_key references instead of literal keys) and you are comfortable with teammates inheriting your approval_policy and sandbox_mode defaults. Many teams keep a project-level template in the repo and let each developer copy it into their own ~/.codex/config.toml rather than syncing the live file directly.