Setting Per-Project Defaults in config.toml
A global config default is a reasonable starting point, but different projects genuinely need different defaults — a personal side project and a production monorepo don't warrant identical sandbox or model settings, and setting per-project defaults is how the CLI lets a project's own configuration override your global preference without you having to remember to pass different flags every time you switch between them.
Why per-project defaults matter more than they seem to at first
Without per-project configuration, every difference between how you want to work on one project versus another has to be remembered and manually applied as flags, every single session, on every project where your global default isn't the right fit. That's exactly the kind of repeated manual step that eventually gets skipped under time pressure — the one time you forget to pass the stricter sandbox flag on the sensitive project is the one time it would have mattered. A per-project default removes that dependency on remembering entirely.
What a project-level config typically overrides
Sandbox mode and approval mode are the most consequential settings worth overriding per project — a project you're just exploring might warrant a more cautious default than one you work in daily and trust completely. Model preference is worth setting per project too, in cases where one project's work genuinely benefits from a different tradeoff than your general default — a cost-sensitive automated task living in one project directory, for instance, reasonably defaulting to a cheaper model than your general interactive default elsewhere.
Where a project's config file actually lives
Checking the current config reference for exactly where a project-level config is expected to live — typically somewhere within the project's own directory structure, read by the CLI when a session starts inside that project — is worth doing directly rather than assumed from general familiarity with how other tools structure per-project configuration, since the exact convention is specific to this CLI's own design.
The override relationship between global, project, and flag
Layered configuration like this typically resolves in a specific order — flags override project config, project config overrides global config — and it's worth confirming that hierarchy explicitly for your installed version rather than assuming it matches some other tool's convention you're more familiar with. Getting this backward in your own mental model is a reliable way to be confused about why a setting you configured doesn't seem to be taking effect.
Committing project config to version control
A per-project config committed into the repository itself, rather than kept as an uncommitted local file, means every contributor working on that project — not just you — gets the same sensible defaults automatically, which matters more the larger a team working on a shared project is. This is a meaningful advantage over a purely personal, local override: it turns a per-project default from "something I remember to set on my machine" into "something the project itself specifies for anyone working in it."
When NOT to override the global default per project
Not every project needs its own configuration — a project fully consistent with your general working style doesn't benefit from an override that just duplicates the global default with extra steps. Reaching for a project-level override specifically when a project's actual needs genuinely diverge from your general default, rather than adding project configuration reflexively to every repository, keeps the practice meaningful rather than becoming boilerplate nobody actually reads.
Testing a new project override before trusting it
The same validation habit that applies to global config changes applies here — after setting a project-level override, start a session inside that project and check /status to confirm the override actually took effect as intended, rather than assuming it worked correctly from the moment you saved the file. A silently-ignored or misconfigured project override is exactly as easy to miss as a global one, and arguably riskier, since you may be relying on it specifically because the project's stakes are higher than your general default assumes.
A practical starting point for a new project
For any new project with different needs than your general default — heightened sensitivity, an unfamiliar codebase you're still building trust in, a cost-sensitive automated use case — setting a project-level override at the very start, before any real work happens in it, is a cheap habit that removes an entire category of "I meant to pass that flag and forgot" mistake for every session in that project going forward.
Auditing project overrides across a portfolio of repositories
For anyone maintaining more than a handful of projects with Codex configured across them, it's worth periodically checking each project's actual override against what you'd currently choose if setting it up fresh today — a project's needs can shift as it matures from an early, exploratory phase into something more established, and an override set appropriately at the start can become either too permissive or unnecessarily restrictive as the project itself changes shape over time.
Why this small feature reflects a bigger idea about tool configuration generally
Per-project defaults are a specific instance of a general principle worth internalizing: the right configuration for a tool usually isn't one global setting applied uniformly everywhere, it's the narrowest, most context-appropriate setting for each specific situation the tool gets used in. Codex's layered configuration — global default, project override, single-invocation flag — is built around that principle explicitly, and using all three layers deliberately, rather than relying on just one, is what actually gets the configuration right for the full range of situations a real workflow encounters.
Verified 2026-08-09 against CodexHow facts module (src/data/facts/) — see /about/#accuracy.