Codex Advanced Configuration
Codex CLIThe advanced configuration layer lets us precisely control model providers, sandbox permissions, hooks, telemetry data, and terminal behavior.
If you are new to Codex, first readConfig basics(Configuration Basics)to build an overall understanding; this article assumes you already know~/.codex/config.tomlits existence and purpose.
Configuration Hierarchy Overview
Codex's configuration is composed of multiple stacked layers. Understanding this is the cornerstone of all subsequent advanced usage.
From bottom to top, the layers are: system layer → user layer (~/.codex/config.toml) → Profile layer → Project layer (.codex/config.toml) → CLI command-line arguments.
Upper layers override lower layers with the same name; the project configuration closest to the current working directory has the highest priority.
The safest way to determine the currently active configuration iscodex --helpto use logs, or explicitly usecodex exec "show my current config"and let the model interpret it for you.
Profiles: Named Configuration Layer
ProfileIs Codex's recommended "scenario-based switching" approach: save a set of configurations as a TOML file, and load it on demand via--profileloading.
Profile files are located in~/.codex/<name>.config.toml, where<name>can only contain letters, digits, hyphens, and underscores.
Create and Use a Profile
Suppose you often perform deep code reviews and want stronger reasoning ability:
Example
model = "gpt-5.5" # Use a model with stronger reasoning ability
model_reasoning_effort = "xhigh" # Set reasoning effort to the highest
approval_policy = "on-request" # Prompt for approval on every tool call
model_catalog_json = "/Users/me/.codex/model-catalogs/deep-review.json" # Optional: custom model directory
Launch Codex with the Profile:
Example
codex --profile deep-review
# Or use in non-interactive exec mode
codex exec --profile deep-review "review this change"
Profile Merge Semantics
Profile files are not a complete configuration; they are an incremental override on top of the user-level~/.codex/config.tomllayer.
This means you only need to write fields "different from the default configuration" in the Profile; other fields automatically inherit user-level settings.
If both user-level and Profile set the same fieldmodel_catalog_json,the Profile value takes precedence.。
Starting from Codex 0.134.0,--profileit no longer readsconfig.tomlin[profiles.<name>]tables, and no longer supports top-levelprofile = "name"selectors. All historical Profile settings must be migrated to separate~/.codex/<name>.config.tomlfiles.
Migrating from Old Configuration to Profile
If your previousconfig.tomllooked like this:
Example
profile = "deep-review"
[profiles.deep-review]
model = "gpt-5.5"
model_reasoning_effort = "xhigh"
You need to split it into two files:
# 文件路径:~/.codex/config.toml(保留非 profile 部分) # 注意:删除了 profile = "deep-review" 选择器
# 文件路径:~/.codex/deep-review.config.toml model = "gpt-5.5" model_reasoning_effort = "xhigh"
After migration, the originalcodex --profile deep-reviewcommand-line usage remains unchanged.
Command-Line One-Time Override
Sometimes you just want to temporarily change one or two fields for a single task without polluting the configuration files. In that case, you can use CLI overrides:
Prefer Dedicated Flags
If official dedicated parameters are provided (such as--model),prefer the dedicated flag, for best readability:
Example
codex --model gpt-5.4
Using --config to Override Arbitrary Keys
For fields without dedicated flags, use-cor--configOverrides. The value is in TOML format, not JSON.
Example
codex --config model='"gpt-5.4"'
# Boolean values are written directly
codex --config sandbox_workspace_write.network_access=true
# Arrays use TOML array syntax
codex --config 'shell_environment_policy.include_only=["PATH","HOME"]'
A few pitfall-prone rules:
- Use dot syntax to locate nested fields, e.g.,mcp_servers.context7.enabled=false
- --configThe value is parsed as TOML; strings containing spaces must be quoted.
- If parsing fails, Codex treats the entire value as a string.
The fastest way to tell whether an override took effect is to follow the command with a simple question: "what model are you using?", letting Codex report the current configuration.
Configuration File and State Locations
Codex stores all local state inCODEX_HOMEthe directory, by default~/.codex。
Common files and their meanings:
| File | Purpose | Sensitive? |
|---|---|---|
| config.toml | Local main configuration; all customizations go here. | no |
| auth.json | File-based credentials (if the system keychain is not used) | Yes, recommended 600 permissions |
| history.jsonl | Conversation history (can be disabled) | May contain sensitive content |
| logs/、caches/ | Runtime logs and cache | May contain request body fragments |
Just Want to Change the OpenAI Base URL?
Many people use LLM proxies, routers, or data-residency projects, and only need to modifyopenaithe base URL of the built-in provider.
In this case, directly setopenai_base_url, do not create a new one[model_providers.openai], because built-in IDs cannot be overridden:
Example
# Point the base URL of the built-in openai provider to the proxy
openai_base_url = "https://us.api.openai.com/v1"
Project-Level Configuration .codex/config.toml
Project-level configuration is placed in the repository root's.codex/config.toml, used for team-shared defaults.
Codex searches upward from the working directory,The file closest to the current working directory has the highest priority.。
Boundaries of Project-Level Configuration
For security reasons,only when the project is marked as "trusted"will the project-level .codex/ layer be loaded (including config.toml, local hooks, and local rules).
In untrusted projects, the sensitive keys in the table below are ignored and a startup warning is printed:
| Ignored key | Reason |
|---|---|
| openai_base_url、chatgpt_base_url | Prevents redirecting credentials / changing host metadata |
| apps_mcp_product_sku | Affects Codex Apps product identification |
| model_provider、model_providers | Prevents replacing credentials / provider identity |
| notify | Prevents executing arbitrary local machine commands |
| profile、profiles | Project-level cannot select a config profile |
| experimental_realtime_ws_base_url、otel | Telemetry and real-time channels |
These keys must be placed in the user-level~/.codex/config.tomlin.
Relative paths (e.g.,model_instructions_file) are resolved relative to their.codex/containing directory.
If the project is untrusted and you setmodel_provider = "proxy", Codex will print a warning at startup and use the default provider; this is a security design, not a bug.
Hooks: Lifecycle Hooks
Hooks allow you to insert custom scripts at key nodes such as tool calls, round starts, etc., suitable for auditing, sensitive-word filtering, commit checks, and more.
Hooks configuration supports two forms, and the event structure is exactly the same:
| Form | Location | Suitable scenarios |
|---|---|---|
| hooks.jsonFile | Same directory as config.toml | JSON toolchain friendly, easy to reuse |
| Inline[[hooks.*]]Table | Inside config.toml | All configuration managed centrally |
The four most commonly used loading locations:
- ~/.codex/hooks.json
- ~/.codex/config.toml
- <repo>/.codex/hooks.json
- <repo>/.codex/config.toml
Project-level hooks are loaded only when the project is trusted; user-level hooks are always loaded independently.
Inline TOML Hook Example
The following is a pre-check hook that executes before Bash tool calls:
Example
# Matches all Bash tool calls
[[hooks.PreToolUse]]
matcher = "^Bash$"
[[hooks.PreToolUse.hooks]]
type = "command"
# Invoke the pre-check script in the repository (path relative to the directory where .codex/ resides)
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"'
timeout = 30 # Timeout in seconds; after timeout, treat as rejected
statusMessage = "Checking Bash command" # Status bar hint
If the same layer already existshooks.jsonInline also exists[hooks]In the table, Codex will load both and print a warning.It is recommended to choose only one form per layer, to avoid doubling maintenance costs.
Custom Model Providers
When you need to connect Codex to non-OpenAI official endpoints such as LLM proxies, Ollama, Mistral, Azure, etc.,model_providersis the entrance.
Built-in provider ID (openai、ollama、lmstudio)Cannot be reusedotherwise it will conflict.
Basic Definition
Example
model = "gpt-5.4" # The model name is determined by the selected provider
model_provider = "proxy" # Points to the following [model_providers.proxy]
[model_providers.proxy]
name = "OpenAI using LLM proxy" # Display name
base_url = "http://proxy.example.com"
env_key = "OPENAI_API_KEY" # Read the secret from environment variable
[model_providers.local_ollama]
name = "Ollama"
base_url = "http://localhost:11434/v1"
[model_providers.mistral]
name = "Mistral"
base_url = "https://api.mistral.ai/v1"
env_key = "MISTRAL_API_KEY"
Adding HTTP Headers
Some proxies require additional headers (such as tenant identifier, feature flags):
Example
# Static header: sent with every request
http_headers = { "X-Example-Header" = "example-value" }
# Dynamic header: value read from environment variable
env_http_headers = { "X-Example-Features" = "EXAMPLE_FEATURES" }
Command-Backed Authentication
When the provider's bearer token needs to be dynamically pulled from an external credential helper, use[model_providers.<id>.auth]:
Example
name = "OpenAI using LLM proxy"
base_url = "https://proxy.example.com/v1"
wire_api = "responses" # Use the Responses API protocol
[model_providers.proxy.auth]
command = "/usr/local/bin/fetch-codex-token"
args = ["--audience", "codex"]
timeout_ms = 5000 # Single fetch timeout
refresh_interval_ms = 300000 # Active refresh interval (5 minutes)
Runtime constraints for the auth command:
- Not receivingstdin, must output the token tostdout
- Codex automatically trims leading/trailing whitespace, and an empty token is treated as an error.
- Settingsrefresh_interval_ms = 0Indicates refresh only after authentication failure
- Cannot be used together with env_key, experimental_bearer_token, requires_openai_auth
Amazon Bedrock Built-In Provider
Built into Codexamazon-bedrockFor built-in providers, you can directly usemodel_providerReference:
Example
model_provider = "amazon-bedrock"
model = "<bedrock-model-id>"
[model_providers.amazon-bedrock.aws]
profile = "default" # AWS profile; if not set, use the standard credential chain
region = "eu-central-1" # Bedrock region
The Bedrock built-in provider only supports nesting..awsthe profile and region overrides under ...; for other custom items, please use the standardmodel_providers.<id>Process.
OSS Local Mode
Codex supports connecting to local "open-source" models (e.g., Ollama, LM Studio), via--ossStart.
If only passing--ossIf no provider is specified, Codex will readoss_providerSelect default:
Example
# Default local provider; optional ollama or lmstudio
oss_provider = "ollama"
Usage example:
Example
codex --oss
# Explicitly specify this session to use lmstudio
codex --oss --oss-provider lmstudio
Azure Provider and Fine-Tuning
Azure OpenAI integration example, demonstrating request-level parameters and retry strategy:
Example
[model_providers.azure]
name = "Azure"
base_url = "https://YOUR_PROJECT_NAME.openai.azure.com/openai"
env_key = "AZURE_OPENAI_API_KEY" # Read key from environment variable
query_params = { api-version = "2025-04-01-preview" }
wire_api = "responses" # Use the Responses API protocol
request_max_retries = 4 # Normal request retry count
stream_max_retries = 10 # Streaming request retry count
stream_idle_timeout_ms = 300000 # Streaming idle timeout (5 minutes)
Requires modifying built-inopenaiWhen using the provider's base URL, useopenai_base_url, do not create a new one[model_providers.openai]。
ChatGPT Data Residency Projects
ChatGPT projects with "Data Residency" enabled need to use a region-prefixed base URL:
Example
model_provider = "openaidr"
[model_providers.openaidr]
name = "OpenAI Data Residency"
# Replace "us" with the prefix required by the data residency documentation (e.g., eu, jp, etc.)
base_url = "https://us.api.openai.com/v1"
Not all models support data residency; before choosing, consult the "eligible models" list in the OpenAI data residency documentation.
Model Reasoning, Verbosity, and Context
The most common quartet for controlling model behavior:
Example
model_reasoning_summary = "none" # Disable reasoning summary
model_verbosity = "low" # Shorten answer length
model_supports_reasoning_summaries = true # Force the model to output reasoning
model_context_window = 128000 # Override context window size
wheremodel_verbosityOnly takes effect on providers using the Responses API; the Chat Completions provider will ignore this setting.
Approval Policies and Sandbox Modes
approval_policyControls when Codex stops to ask for human confirmation,sandbox_modeControls which files and networks it can access.
The two dimensions can be combined independently; below is a set of typical configurations:
Example
approval_policy = "untrusted" # Other options: on-request, never, or { granular = { ... } }
approvals_reviewer = "user" # "auto_review" goes through automatic review
sandbox_mode = "workspace-write"
allow_login_shell = false # Disable login shell (hardening)
# Example of fine-grained approval policy (distinguished by prompt category)
# approval_policy = { granular = {
# sandbox_approval = true,
# rules = true,
# mcp_elicitations = true,
# request_permissions = false, # request-permission categories fail closed directly
# skill_approval = false # skill-script categories fail closed directly
# } }
[sandbox_workspace_write]
exclude_tmpdir_env_var = false # Allow writing to $TMPDIR
exclude_slash_tmp = false # Allow writing to /tmp
writable_roots = ["/Users/YOU/.pyenv/shims"] # Additional writable directories
network_access = false # Disallow external network by default
[auto_review]
policy = """
Use your organization's automatic review policy.
"""
]
Common Combinations Quick Reference
| Scenarios | approval_policy | sandbox_mode | Description |
|---|---|---|---|
| Local daily development | on-request | workspace-write | Prompt before each tool call; safest |
| CI automation | never | workspace-write | No human intervention, must be tested in a sandbox |
| Fully trusted environment | never | danger-full-access | Disable sandbox; use only in isolated environments |
| Strict audit scenarios | untrusted | workspace-write | All write operations require secondary confirmation |
Disable Sandbox (Use with Caution)
When your environment already has process-level isolation, you can disable the Codex sandbox:
Example
sandbox_mode = "danger-full-access"
Inworkspace-writeIn mode, some environments will.git/and.codex/Keep read-only. This meansgit commitCommands such as ... still require approval when executed outside the sandbox. To commit directly inside the sandbox, userulesExplicitly allow.
Shell Environment Variable Policy
shell_environment_policyControls which environment variables Codex passes through when launching subprocesses.Default targetis "leaking fewer secrets + preserving the paths needed for the task."
Example
[shell_environment_policy]
inherit = "none" # Clean start (recommended)
set = { PATH = "/usr/bin", MY_FLAG = "1" } # Explicitly set variables
ignore_default_excludes = false # Keep default KEY/SECRET/TOKEN filtering
exclude = ["AWS_*", "AZURE_*"] # Additionally exclude variables matching globs
include_only = ["PATH", "HOME"] # Only pass through whitelist (overrides exclude)
Pattern syntax: case-insensitive glob,*Matches any character,?Matches a single character,[A-Z]Is a character class.
ignore_default_excludes = falseIt will preserve Codex's built-in KEY/SECRET/TOKEN filters, taking effect before include/exclude.
MCP Servers
MCP (Model Context Protocol) servers allow Codex to connect to external tool sets.
For complete MCP configuration instructions, see the officialMCP documentation, not expanded in this article. Common patterns:
Example
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
enabled = true
# Temporarily disable a specific MCP service with --config
# codex --config mcp_servers.context7.enabled=false
Observability and Telemetry (OTel)
Codex supports exporting runtime logs via OpenTelemetry, disabled by default. How to enable:
Example
[otel]
environment = "staging" # Default dev
exporter = "none" # Optional: otlp-http or otlp-grpc
log_user_prompt = false # Do not export user prompt content (recommended)
Choosing an HTTP Exporter
Example
exporter = { otlp-http = {
endpoint = "https://otel.example.com/v1/logs",
protocol = "binary",
headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" }
}}
Choosing a gRPC Exporter
Example
exporter = { otlp-grpc = {
endpoint = "https://otel.example.com:4317",
headers = { "x-otlp-meta" = "abc123" }
}}
Exported Event Types
Codex emits structured events, including but not limited to:
| Events | Meaning |
|---|---|
| codex.conversation_starts | Model, reasoning settings, sandbox/approval policy |
| codex.api_request | API request attempts, status, duration, errors |
| codex.sse_event | SSE stream events, token count (on response.completed) |
| codex.websocket_request、codex.websocket_event | WebSocket requests and messages |
| codex.user_prompt | User prompt length; content is redacted by default. |
| codex.tool_decision | Tool call decisions: approve/reject, whether the source is configuration or the user |
| codex.tool_result | Tool execution duration, success/failure, output snippets |
Whenexporter = "none"When ..., Codex still records events in memory but does not send them.When closed, it still triggers asynchronous batch per event, flush on process exit.
Anonymous Metrics (Enabled by Default)
By default, Codex periodically reports a small amount of anonymous usage and health data to detect anomalies.Does not contain PII。
To completely disable:
Example
[analytics]
enabled = false
Hiding and Showing Reasoning Events
In CI logs, reasoning output is often "noisy" and can be globally disabled:
Example
hide_agent_reasoning = true
Conversely, when you need to view the model's raw reasoning content:
Example
show_raw_agent_reasoning = true
Only enable it when you have confirmed that your workflow allows exposing raw reasoning. Some models (such asgpt-oss) do not output raw reasoning themselves; in that case, this setting has no effect.
External Notifications (notify)
When Codex completes a turn, it can trigger external scripts to implement desktop notifications, chat webhooks, CI status updates, etc.
Example
notify = ["python3", "/path/to/notify.py"]
The script receives a JSON argument; common fields:
| Fields | Meaning |
|---|---|
| type | Currently onlyagent-turn-complete |
| thread-id | Session ID |
| turn-id | Turn ID |
| cwd | Working directory |
| input-messages | User message list |
| last-assistant-message | The last assistant message |
A simple script example that calls terminal-notifier:
Example
#!/usr/bin/env python3
import json, subprocess, sys
def main() -> int:
notification = json.loads(sys.argv[1])
# Only handle turn completion events
if notification.get("type") != "agent-turn-complete":
return 0
title = f"Codex: {notification.get('last-assistant-message', 'Turn Complete!')}"
message = " ".join(notification.get("input-messages", []))
subprocess.check_output([
"terminal-notifier",
"-title", title,
"-message", message,
"-group", "codex-" + notification.get("thread-id", ""),
"-activate", "com.googlecode.iterm2",
])
return 0
if __name__ == "__main__":
sys.exit(main())
Difference Between notify and tui.notifications
| Mechanism | Suitable scenarios | Key configuration |
|---|---|---|
| notify | webhooks, desktop notifications, CI hooks | External program |
| tui.notifications | TUI built-in notifications | Supports filtering by event type |
| tui.notification_method | Terminal notification method | auto / osc9 / bel |
| tui.notification_condition | Trigger Condition | unfocused / always |
autoIn this mode, Codex prefers the OSC 9 terminal escape sequence; some terminals treat it as a desktop notification; when unsupported, it falls back to BEL (\x07)。
Feedback and History
Disable Feedback Collection
By default, Codex is provided in the TUI./feedbackcommand. To disable:
Example
[feedback]
enabled = false
After closing/feedbackwill display a disabled message, and Codex refuses to submit feedback.
History Records (history.jsonl)
By default, Codex writes local session transcripts to~/.codex/history.jsonlHow to disable:
Example
[history]
persistence = "none"
Limits file size (when exceeded, drops the oldest entries and compresses):
Example
[history]
max_bytes = 104857600 # 100 MiB
Clickable Citations
When the terminal or editor supports it, Codex can render file references as clickable links.
Example
# Optional: vscode, cursor, windsurf, vscode-insiders, none
file_opener = "vscode"
After setting, reference/home/example/project/main.py:42Will be rewritten asvscode://file/.../main.py:42, and clicking in VS Code will jump to that location.
Project Instruction Discovery (AGENTS.md)
Codex will readAGENTS.md(and related files), automatically bring project conventions into the "first turn" of the session.
Two core knobs:
| Configuration Key | Function | Typical values |
|---|---|---|
| project_doc_max_bytes | Maximum number of bytes read per AGENTS.md | Default approximately 32 KiB |
| project_doc_fallback_filenames | Fallback file name when AGENTS.md is missing | For exampleCLAUDE.md |
Terminal User Interface (TUI) Options
RuncodexRunning without a subcommand enters the interactive TUI.[tui]Common keys under the table:
| Key | Function | Optional values |
|---|---|---|
| tui.notifications | Enable/disable TUI notifications; can filter by event type | Boolean / event type array |
| tui.notification_method | Terminal notification method | auto、osc9、bel |
| tui.notification_condition | Trigger Condition | unfocused、always |
| tui.animations | ASCII animations and shimmer effects | true / false |
| tui.alternate_screen | Whether to use the alternate screen (set to never to preserve scroll history) | auto、always、never |
| tui.show_tooltips | Whether the welcome page shows beginner tips | true / false |
Common Configuration Quick Reference Table
Treat this section as a "look it up when you forget" quick-reference checklist:
| Requirements | Configuration location / command |
|---|---|
| Switch model | --model gpt-5.4ormodel = "..." |
| Scenario-based configuration | --profile <name> + ~/.codex/<name>.config.toml |
| Temporarily change any field | --config key=value(TOML syntax) |
| Change OpenAI endpoint | openai_base_url |
| Connect proxy/self-built vendor | [model_providers.<id>] + model_provider |
| Local Ollama | --oss + oss_provider |
| Disable sandbox (use with caution) | sandbox_mode = "danger-full-access" |
| Disable anonymous metrics | [analytics] enabled = false |
| Disable history | [history] persistence = "none" |
| Disable feedback | [feedback] enabled = false |
| Desktop notifications | notify = ["python3", "/path/to/notify.py"] |
Frequently Asked Questions
The model_provider set in project-level configuration is not taking effect?
For security reasons,model_provider、model_providers、profile、notifyand other sensitive keysWill be ignored in the project-level .codex/config.toml, and prints a warning at startup.
Move these keys to user level~/.codex/config.tomlJust in it.
Changes written with --config have no effect?
Check three things: whether the value conforms to TOML syntax (arrays use[], strings use"", nesting uses dots); whether the shell split multiple values by treating spaces as separators; whether key names are spelled correctly. Open~/.codex/logs/Under the latest logs, you can see the original string that failed to parse.
Still the default model after launching with a Profile?
Most common cause: the Profile file is still using the old[profiles.<name>]table, or the filename suffix is not.config.toml. Starting from 0.134.0, only ... is recognized~/.codex/<name>.config.tomlformat.
Why is git commit still subject to approval after the sandbox is disabled?
Closesandbox_modedoes not affect approval policy.git commitand other commands may still becauseapproval_policy = "on-request"and triggers a prompt; if you want "fully automatic commits", you need to also set the approval policy tonever。
Can custom models select the gpt-5 series?
Depends onbase_urlwhether the service it points to supports it. Codex itself does notmodelstrictly validate the field; the key is whether your provider side can recognize and respond to it.
other extensions