Settings and Configuration Hierarchy
Every time you launch Claude Code, you operate within a four-level settings system — even if you have never explicitly touched a single settings.json. Understanding this hierarchy matters for two reasons: it determines what the agent is and is not allowed to do, and it decides which settings apply to the entire team versus which remain strictly personal.
The Four Levels and Their Locations
Read the table from bottom to top: each higher level overrides the one below it.
| Level | File | Scope | In git? |
|---|---|---|---|
| Managed (enterprise) | /etc/claude-code/managed-settings.json (Linux) | All users on the machine | No — deployed by IT |
| Local | .claude/settings.local.json | Only you, in this repo | No (gitignore) |
| Project | .claude/settings.json | The whole team in the repo | Yes |
| User | ~/.claude/settings.json | You, across all projects | No |
CLI flags (--model, --allowedTools, etc.) override everything except managed settings — and they only apply for the duration of a single run.
Full priority order, from lowest to highest:
User → Project → Local → CLI flags → ManagedIn practice this means: if managed-settings.json sets a Bash(curl *) deny rule, no settings.local.json can override it. And settings in .claude/settings.json are automatically applied to everyone who clones the repository.
flowchart TD
M["🏢 Managed settings\n/etc/claude-code/managed-settings.json\n(set by IT, cannot be overridden)"] --> U
U["👤 User settings\n~/.claude/settings.json\n(personal, all projects)"] --> P
P["📁 Project settings\n.claude/settings.json\n(team, in git)"] --> L
L["💻 Local settings\n.claude/settings.local.json\n(you only, gitignored)"] --> C
C["⚡ CLI flags\n--model, --allowedTools\n(current run)"]
style M fill:#ff6b6b,color:#fff
style U fill:#ffa94d,color:#fff
style P fill:#51cf66,color:#fff
style L fill:#339af0,color:#fff
style C fill:#cc5de8,color:#fffFile Structure
All four files share the same JSON schema. A minimal working example:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": ["Bash(npm run lint)", "Bash(npm run test *)"],
"deny": ["Read(./.env)", "Read(./secrets/**)", "Bash(curl *)"]
},
"model": "claude-sonnet-4-6"
}
}The schema is available on JSON Schema Store — editors with JSON Schema support (VS Code and JetBrains) will provide autocompletion and validation directly in the file.
The Permissions System
Permissions are the most critical part of the configuration. Claude Code asks for confirmation before every potentially dangerous action (writing a file, executing a command), and permissions is what determines what is silently allowed, what is always denied, and what requires a prompt.
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git *)",
"Read(~/.zshrc)"
],
"deny": [
"Bash(curl *)",
"Bash(rm -rf *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"WebFetch(*)"
]
}Rule format. Each rule is a string of the form ToolName(glob-pattern). Supported tools: Bash, Read, Edit, Write, WebFetch, mcp__ServerName__ToolName.
Glob characters work in the standard way:
*— any characters except/**— any characters including/(recursive)npm run *— allow any script via npm run
Processing order. Deny always wins over allow — this does not depend on the order of entries in the file. If one rule allows Bash(curl *) and another denies Bash(curl *), the request will be rejected.
Level merging. Permissions from all files are concatenated, not overwritten. If the user settings have allow: ["Bash(git *)"] and the project settings have allow: ["Bash(npm run test)"], the resulting allow list contains both rules. Deny lists are also merged — and if even one level denies an action, it is denied.
What Goes Where: A Practical Split
The most common mistake is putting everything in one file. Here is a working approach:
~/.claude/settings.json (user) — personal preferences needed across all projects:
{
"editorMode": "vim",
"permissions": {
"allow": [
"Bash(git *)",
"Read(~/.zshrc)",
"Read(~/.gitconfig)"
]
}
}.claude/settings.json (project) — team rules, committed to the repo:
{
"model": "claude-sonnet-4-6",
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(npm run build)"
],
"deny": [
"Bash(rm -rf *)",
"Read(./.env*)",
"Read(./secrets/**)"
]
},
"attribution": {
"commit": "🤖 Claude Code"
}
}.claude/settings.local.json (local) — personal overrides for this repo that your teammates don't need:
{
"model": "claude-opus-4-5",
"permissions": {
"allow": ["Bash(./scripts/dev-reset.sh)"]
}
}Important: .claude/settings.local.json must be added to .gitignore. If it isn't, Claude Code will warn you about this the first time the file is created.
Environment Variables
Some settings are more convenient to keep in environment variables rather than JSON — especially secrets and CI/CD parameters:
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY | API key (required without OAuth) |
ANTHROPIC_MODEL | Default model |
CLAUDE_CODE_EFFORT_LEVEL | Effort level: low, medium, high, xhigh |
MAX_THINKING_TOKENS | Extended thinking token budget |
DISABLE_AUTOUPDATER | Disable auto-update (1) |
CLAUDE_CODE_DISABLE_AUTO_MEMORY | Disable auto-memory (1) |
CLAUDE_CODE_ENABLE_TELEMETRY | OpenTelemetry telemetry (1) |
CLAUDE_CODE_SKIP_PROMPT_HISTORY | Do not write transcripts (1) |
In addition, settings.json has an env field that passes variables to child processes and tools:
{
"env": {
"NODE_ENV": "development",
"DATABASE_URL": "postgres://localhost:5432/mydb",
"CLAUDE_CODE_ENABLE_TELEMETRY": "1"
}
}This is useful when you need to ensure that Claude always runs npm run test with specific variables — without having to export them manually in every terminal session.
When Changes Take Effect
Not all settings are applied immediately:
Hot reload (no restart required):
permissions— allow/deny rules are updated immediately after saving the filehooks— changes are picked up on the flyapiKeyHelperand credential helpers
Only after a restart:
model— the selected model is locked in at startupoutputStyle— output styleeditorMode— editor mode
If Claude keeps using the old model after you edit model in settings.json, that is expected behavior — just restart the session.
Enterprise: Managed Settings and MDM
For enterprise deployments there is a managed-settings level that no user can override:
/Library/Application Support/ClaudeCode/managed-settings.json # macOS
/etc/claude-code/managed-settings.json # Linux
C:\Program Files\ClaudeCode\managed-settings.json # WindowsIT departments can also distribute settings via Jamf, Kandji (macOS), or Windows Group Policy (registry key HKLM\SOFTWARE\Policies\ClaudeCode). A typical configuration:
{
"forceLoginOrgUUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"allowManagedMcpServersOnly": true,
"deniedMcpServers": [{"serverName": "filesystem"}],
"availableModels": ["claude-sonnet-4-6", "claude-haiku-4-5"],
"enforceAvailableModels": true,
"companyAnnouncements": ["Use only internal company MCP servers"]
}To verify configuration correctness before deployment: claude doctor.
Modular merge via managed-settings.d/ allows different teams to contribute separate policies without conflicts — files are processed in alphabetical order, arrays are concatenated, and objects are deep-merged.
Quick Recipe: Initial Setup for a New Project
# 1. Create the directory
mkdir -p .claude
# 2. Add the local file to gitignore
echo '.claude/settings.local.json' >> .gitignore
echo '.claude/CLAUDE.local.md' >> .gitignore
# 3. Create project settings
cat > .claude/settings.json << 'EOF'
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git status)",
"Bash(git diff *)"
],
"deny": [
"Read(./.env*)",
"Read(./secrets/**)",
"Bash(git push --force*)",
"Bash(rm -rf *)"
]
}
}
EOF
# 4. Commit
git add .claude/settings.json
git commit -m "feat: add Claude Code project settings"After this, everyone who clones the repository and launches Claude Code will get the same baseline permissions. Personal overrides go in settings.local.json; global preferences go in ~/.claude/settings.json.
See also
- CLAUDE.md and the Memory System — CLAUDE.md is read on top of settings.json and supplements the configuration with text-based instructions
- Managing the Context Window —
autoMemoryEnabledin settings.json controls auto-memory, which is loaded into the context - Hooks — Lifecycle Events — hooks can be defined directly in
settings.jsonvia thehooksfield; they support hot reload - Connecting MCP Servers in Claude Code —
.mcp.jsonlives alongside.claude/settings.jsonand manages MCP servers at the project level - Model Selection and Thinking Modes —
model,effortLevel, andalwaysThinkingEnabledare fields from settings.json - The Permissions Model, Security, and Trust — more detail on
bypassPermissionsmodes and protection against prompt injection