Chapter 02 · Section I · 12 min read
2.1 Settings files: hierarchy, permissions.json, env
Where Claude Code reads settings from, in what order, and what belongs in each layer. Learn the hierarchy and you stop fighting your own config.
Claude Code layers configuration from several places. Any given setting comes from the most specific layer that mentions it. Understanding the hierarchy is what lets you put team defaults in the repo and personal overrides on your machine without one silently killing the other.
The hierarchy
From lowest to highest precedence:
- Built-in defaults. What ships with the CLI.
- User settings —
~/.claude/settings.json. Applies to every project on your machine. - Project settings —
<repo>/.claude/settings.json. Applies to this repo; committed with it. - Project local settings —
<repo>/.claude/settings.local.json. Applies to this repo, gitignored, for your personal overrides. - Environment variables + CLI flags. Highest — override everything for this invocation.
Higher layers override lower ones key-by-key. Arrays (like permissions.allow) are merged, not replaced — so a project can extend the user allowlist without repeating it.
What lives in each layer
| Layer | Good use |
|---|---|
~/.claude/settings.json | Model preference, personal theme, generally-useful slash-command paths |
<repo>/.claude/settings.json | Team defaults: permissions.allow, hooks, project skills paths |
<repo>/.claude/settings.local.json | Personal overrides for this repo — API endpoint, temporarily loosened permissions |
| Env / flags | One-off overrides: --permission-mode plan for a single session |
Commit the project settings; do not commit settings.local.json. Standard .gitignore entry:
.claude/settings.local.json
Anatomy of settings.json
The main sections you will actually touch:
{
"model": "claude-sonnet-4-6",
"permissions": {
"allow": ["Read", "Grep", "Bash(npm run test:*)"],
"deny": ["Bash(rm -rf /*)"]
},
"hooks": { /* section 3.3 */ },
"additionalDirectories": ["/Users/you/shared"],
"env": {
"MY_INTERNAL_API": "https://internal.example.com"
}
}
env deserves calling out: keys defined here are exported to any Bash tool call Claude runs. Handy for passing an internal endpoint or a feature flag without editing shell profiles.
Debugging which layer won
claude config get permissions.allow --explain
The --explain variant tells you which file the value came from. When two layers disagree and you cannot tell why, this is the fastest way to find out.
Check your understanding
Quick check
—