Codex's configuration is not one file but a stack of layers with a defined precedence, and understanding this stack matters more than knowing individual keys. The CLI, the IDE extension and the local app use compatible config.toml layers. The effective value of any setting is chosen by the documented precedence of layers, not by where you wrote it last. Meanwhile project layers load only for a trusted project - exactly like the rest of the executable configuration.
The precedence runs from the one-off to the permanent, and this is logical. Highest are the CLI flags and one-off overrides via -c key=value: they are for an experiment here and now. Below is the project .codex/config.toml, read from the repository root to the current directory. Lower still is the selected profile file, then the user config.toml, then the system /etc/codex/config.toml on Unix if it exists, and only at the very bottom the built-in defaults. The system layer is easy to forget, but it is real: a setting you cannot find in the project or in your own files may be coming from there. A more specific and more recent layer overrides the general one, so the same setting in different layers meets a different decision.
It helps to see a one-off override and the diagnostic commands side by side once. Below is codex -c with a temporary value and the /debug-config and /status commands in the TUI. You return to this form when behavior does not match expectation: most often the matter is not a "bug" but that some layer overrode yours. A one-off -c is handy precisely for an experiment - to check a setting's effect without touching the permanent files.
Managed requirements.toml stands apart and changes the very logic of the stack. It is not just another layer of defaults: it can forbid values that lower layers have no right to expand. An ordinary layer can be overridden by a layer above; a managed requirement cannot, and that is its point. An organization sets mandatory boundaries with it, and an attempt to weaken them with a project or user config will not work - this is exactly how centralized policy should behave.
It helps to gather the layer precedence into a table once, so as not to guess which of them wins. Below is a map from the CLI flags to the built-in values. You return to it when working out why a setting did not apply: it is enough to go through the precedence from top to bottom and find the layer that overrode it. Understanding the order turns a mysterious "does not work" into a predictable "overridden right here", visible in /debug-config.
Configuration diagnostics rest on two commands. /debug-config shows the order of layers, their active or disabled state and the policy sources - that is, it answers the question "which layer came from where and what won". /status shows the effective result as a whole. Together they remove the uncertainty: instead of assuming which value is in play, you see its source. Checking a setting with these commands is cheaper than guessing from the files' content.
| Priority | Layer | Example |
|---|---|---|
| 1 - highest | CLI flags and -c key=value | A one-off experiment |
| 2 | Project .codex/config.toml (root -> CWD) | Repository and module defaults |
| 3 | The selected profile file | $CODEX_HOME/review.config.toml |
| 4 | User config.toml | Personal permanent settings |
| 5 | System /etc/codex/config.toml (Unix, if present) | Machine-wide settings |
| 6 - lowest | Built-in defaults | Codex's own values |
| managed | requirements.toml forbids | Org boundaries, not weakenable from below |
--strict-config deserves separate attention, especially in CI and rollout. By default an unknown field may be silently ignored by supporting runtime commands - and you will not notice a typo in a key. With --strict-config an unknown field becomes an error rather than a silently skipped line. In CI this matters: it is better to fail on an unclear key at once than to discover that a setting has not applied for years because it was misspelled and silently ignored.
The typical failures around configuration are predictable. Writing a setting in one layer and being surprised it was overridden by another, higher-priority one. Expecting a project config to weaken managed requirements - though it forbids exactly that. Guessing the effective value instead of /debug-config and /status. And not enabling --strict-config in CI, leaving typos silently ignored. Understand the layer precedence, remember that managed forbids, check the effective value with a command and enable strict checking where precision matters.
codex -c 'model_reasoning_effort="high"' # a one-off override (highest priority)
# In the TUI:
/debug-config # layer order, active/disabled, policy sources
/status # the effective result
# --strict-config: an unknown field becomes an error, not silently ignored (CI/rollout)