Environment Variables: The Hidden Row of "Master Switches"
📚 Series Navigation: The previous article 41 Parallel Tasks taught you how to split tasks and let multiple Claudes run concurrently. This article dives under the surface—the "master switches" controlling how Claude Code connects to models, how long it times out, and whether to report telemetry data, all rely on environment variables. They aren't in any menu; instead, they hide in your shell and
settings.json. Today, we'll get familiar with this row of switches and learn how to configure them.
It's often said that environment variables are "only for advanced players." To be honest, that advice has led many astray.
Too many people connect to models, configure proxies, or tweak timeouts by manually running /model or editing settings every single time they start a session, only to have to repeat it all over again in the next. When asked why they don't configure them permanently, the answer is usually: "Environment variables sound too hardcore, I'm afraid of breaking something."
But look back at the previous 41 chapters—Chapter 04 configuring API keys, Chapter 05 connecting domestic models, Chapter 19 managing context, Chapter 21 disabling telemetry... At the core of all these tasks, almost the exact same mechanism serves as the foundation: environment variables. You've already been using them; it's just that no one has laid them out side-by-side for you.
Put it this way: environment variables are not "only for advanced players," but a row of master switches that you set once and benefit from forever. Fearing them is purely because no one told you which ones are common and what is the worst-case scenario if you configure them wrong. Today, we'll break down that barrier.
After reading this article, you will get:
- A brief explanation of what environment variables actually govern in Claude Code, and why they are worth ten minutes of your time
- A table explaining when to use the three configuration methods (temporary shell, persistent shell profile,
settings.json'senvblock) - How the four categories of
settings.jsonfiles differ in scope and which ones should enter git (directly connecting with Chapter 31) - A checklist of the "must-know environment variables": connection, timeouts, privacy, and config directories, along with defaults and tips
- When a setting can be configured via both an environment variable and a setting field, who actually wins (precedence rules)
- A hands-on practice with expected output: set a variable yourself and verify that it works
01 Understand First: What Environment Variables Govern in Claude Code
Conclusion first: environment variables are a set of key-value pairs that Claude Code reads upon startup, governing low-level behaviors such as model connections, authentication, timeouts, and telemetry.
Most features you used earlier—like /model to switch models, claude mcp add to add services, or /config to adjust options—are "temporary tweaks during a session." However, for certain behaviors, you want them to follow your rules on every startup without manual configuration: e.g., "always use my self-built proxy," "extend all request timeouts to 20 minutes," or "never report telemetry from this machine." These "set-and-forget" low-level rules are governed by environment variables.
The official documentation defines them in one sentence:
Environment variables can control the behavior of Claude Code, such as model selection, authentication, request routing, and feature toggles.
Analogy: Preset buttons on a coffee maker. With a customizable coffee maker, you can adjust water volume, strength, and milk every single time; or you can save your favorite settings as a "preset," so you can get your coffee with a single press, leaving the machine to remember the parameters. Environment variables are like the latter: they persist settings you want to reuse, allowing Claude Code to read them upon startup, saving you from manual configurations every time.
In real-world scenarios, you will likely think of them when:
- "I connected DeepSeek, and don't want to specify the model manually every time"—writing the model and URL into environment variables (which is the core of the setup in Chapter 05)
- "The company network is slow, and the default 10-minute timeout is not enough"—extending the timeout with a single variable
- "This is a company machine, and compliance requires disabling telemetry"—disabling it with a single variable (mentioned in Chapter 21)
- "I have both work and personal accounts, and want to keep them separated without interference"—switching config directories via a variable
See that? These are all "set once, benefit long-term" requirements. Environment variables are born for exactly these needs.
💡 Summary in one sentence: Environment variables are a set of key-value "master switches" read by Claude Code upon startup, governing connections, authentication, timeouts, and privacy; their value lies in permanently saving your preferred configurations so you don't have to configure them manually every time.
02 Where to Set: Three Ways, from "Just Once" to "Always in Effect"
Now that we know what they govern, the next practical question is: where do we set them? The answer points to three locations, with the difference lying in a single line: how long and who does the setting affect?
The official documentation puts it simply:
Variables set in the shell are only valid during that terminal session, whereas variables in settings files apply every time you run
claude.
Here are the three methods sorted by scope (narrowest to broadest) for you to choose from:
Method 1: Set Temporarily in the Shell (Affects Only the Current Terminal)
Run export in the terminal before starting claude. It only affects the current terminal window, and disappears once closed.
macOS / Linux / WSL:
export API_TIMEOUT_MS="1200000"
claudeWindows PowerShell:
$env:API_TIMEOUT_MS = "1200000"
claudeThis is suitable for "testing it just this once"—temporarily extending a timeout or swapping an address, and leaving no trace behind once the terminal is closed.
Method 2: Add to Shell Profile (Affects Every Session on This Machine)
If you want the variables to be loaded every time you open a terminal, append the export command to your shell profile. On macOS, this is typically ~/.zshrc, and on Linux, it is often ~/.bashrc:
# Add to the end of ~/.zshrc
export API_TIMEOUT_MS="1200000"It takes effect once you save the file and open a new terminal (or run source ~/.zshrc). This applies globally across all projects and terminals on your machine.
To persist variables on Windows, run
setx API_TIMEOUT_MS "1200000"(CMD) or[Environment]::SetEnvironmentVariable("API_TIMEOUT_MS", "1200000", "User")(PowerShell), and open a new terminal to apply.
Method 3: Add to settings.json under the env Block (Travels with Config, Indifferent to Startup Method)
The third and most common approach—write them into the env block in settings.json. The official docs clarify its benefit:
Claude Code reads them directly from the file at startup, so they take effect regardless of how you start
claude.
{
"env": {
"API_TIMEOUT_MS": "1200000",
"BASH_DEFAULT_TIMEOUT_MS": "300000"
}
}The file path and syntax were introduced in Chapter 31 on
settings.json. Just remember:envis the dedicated slot for environment variables; placing variables here ensures Claude Code reads them regardless of how you runclaude.
The difference between these three approaches is summarized in the table below:
| Method | Scope | Persist after closing terminal? | Best for |
|---|---|---|---|
| Temporary shell export | Current terminal only | ❌ No | Testing just once |
| Add to ~/.zshrc etc. | All terminals on this machine | ✅ Yes | Global setup on personal machine |
| Add to env in settings.json | Aligns with the scope of the config file | ✅ Yes | Project/team-bound setup affecting all users |
A useful habit: verify a variable temporarily using a shell export; if it works and is needed long-term, move it to settings.json's env. Why prefer settings.json over ~/.zshrc? Because of the "file hierarchy" explained in the next section—settings.json lets you precisely control whether a variable is "only for me, for the entire team, or strictly for this project", which ~/.zshrc cannot distinguish.
💡 Summary in one sentence: The three configuration methods ranked by scope are—temporary shell
export(just once), shell profile like~/.zshrc(global on machine), andsettings.json'senvblock (travels with config, independent of startup); use the first for temporary trials and prefer the third for long-term settings.
03 The Four Categories of settings.json: Who They Govern, Which Enter Git
The previous section highlighted that settings.json's env is the recommended slot, but here is a common pitfall for beginners: there is more than one settings.json file. There are four categories, and writing variables into different files results in completely different scopes of effect.
We discussed this in detail in Chapter 31, and let's reinforce it here from the perspective of environment variables—because the consequences of placing variables in the wrong file are real: a proxy URL you intended only for yourself might accidentally get committed to git, throwing the entire team off track.
Analogy: Posting company reimbursement policies on the bulletin board vs. writing notes on your sticky note. Finance prints "taxi capped at $50" on the bulletin board, which everyone in the company must follow—this is a public rule. You write "don't reimburse coffee I paid for myself this month" on your sticky note—this is a private note affecting only you. Choosing which settings file to write environment variables into is like choosing between "posting on the bulletin board" and "writing on a sticky note."
The table provided in the official docs summarizes "who is affected" by each category of file:
| File | Applies to |
|---|---|
~/.claude/settings.json | You, across all projects |
.claude/settings.json | Everyone working in the project, checked into source control |
.claude/settings.local.json | You, only in this project, not checked in |
| Managed settings | Everyone in your organization, deployed by administrators |
In plain terms, remember these four rules:
~/.claude/settings.json(in your home directory)—"for myself, across all projects." Place your personal preferences here..claude/settings.json(project root)—"for everyone in this project," and it is checked into git, so teammates get it when pulling. Place team-wide rules here..claude/settings.local.json(project root)—"only for me, strictly in this project," and it is not checked into git (the.localsuffix is ignored by default). Put private credentials and temporary personal settings here.- Managed settings—globally deployed by administrators across the organization; you typically cannot edit them.
Here is a common slip-up. When connecting a custom gateway, you might write ANTHROPIC_BASE_URL with a URL containing a personal token into the project root .claude/settings.json for convenience, and run git commit—if you didn't check the diff before pushing, you're in trouble: once pushed, you expose private URLs (possibly with credentials) to anyone who can view the repository. Thus, remember this ironclad rule: any variable with "personal" characteristics (private URLs, personal config directories) must go into the .local file which is not tracked by git; reserve .claude/settings.json only for variables that "the entire team should share."
| Characteristics of the variable | Which file to write into |
|---|---|
| My personal preference, shared across all projects | ~/.claude/settings.json (home directory) |
| Team-wide, shared with the project | .claude/settings.json (tracked in git) |
| My private, credentialed setup strictly for this project | .claude/settings.local.json (❌ not in git) |
💡 Summary in one sentence: There are four categories of
settings.jsonfiles, and where you write a variable dictates who it affects—home directory affects yourself globally, project rootsettings.jsonis tracked in git and affects the team, and.localis untracked and affects only yourself; never write variables containing personal credentials into files tracked in git.
04 Must-Know Variables: Connection, Timeouts, Privacy, and Directories
The official environment variable table is, frankly, intimidating—dozens of variables spanning from AWS Bedrock to OpenTelemetry exporters, most of which you'll never use. So in this section, I won't list everything, but instead select the dozen you are most likely to encounter, grouped into four categories, explaining "what it does, its default, and when to modify it."
Category 1: Connection & Authentication (How to Connect to Models)
This group forms the foundation for Chapters 04 and 05. Three core variables:
ANTHROPIC_API_KEY # Your API key
ANTHROPIC_BASE_URL # Redirects API requests to your proxy or gateway
ANTHROPIC_MODEL # Specifies the default modelANTHROPIC_API_KEY: Your API key. There is an easy-to-trip-over detail explicitly stated in the docs: once this key is set, Claude Code will use it even if you have an active subscription (Pro / Max / Team / Enterprise). To revert to the subscription, rununset ANTHROPIC_API_KEYto clear it. Note: In interactive mode, Claude Code will prompt you once to approve or deny using this key, and you can choose to remember the choice; in non-interactive (-p) mode, it is used directly without prompts. It is easy to accidentally burn API credits while having a Max subscription due to a leftoverexport ANTHROPIC_API_KEYin~/.zshrc(discussed in Chapter 04).ANTHROPIC_BASE_URL: Redirects API requests to your proxy or gateway. This is the core variable for connecting domestic models or using custom relays (which is the main actor in the setup of Chapter 05).ANTHROPIC_MODEL: Specifies the default model. Note its precedence: the--modelflag and/modelcommands in the chat override it (detailed in the next section).
Category 2: Timeouts (Increase If It Gives Up Too Quickly)
When your network is slow or routing through proxies, timeouts are what you'll tweak most. Two common ones:
| Variable | Governs | Default |
|---|---|---|
API_TIMEOUT_MS | Timeout for a single API request | 600000 (10 minutes) |
BASH_DEFAULT_TIMEOUT_MS | Default timeout for long-running bash commands | 120000 (2 minutes) |
API_TIMEOUT_MS: Timeout duration for API requests. Official tip: increase this if you are on a slow network or using a proxy. But do not set it arbitrarily—it has a maximum cap of2147483647. Exceeding this number causes the underlying timer to overflow, making requests fail instantly (accidentally typing an extra 0 is a common mistake, leading to immediate request failures which take forever to debug).BASH_DEFAULT_TIMEOUT_MS: The default duration Claude allows for long bash commands (like installing dependencies or running builds). Default is 2 minutes; increase this if large builds fail due to timeout.
Category 3: Privacy & Telemetry (For Compliance/Strict Privacy)
These variables govern "whether data is sent outwards," mentioned in Chapter 21 on security:
DISABLE_TELEMETRY=1 # Set to 1 to opt out of telemetry
DO_NOT_TRACK=1 # Same as above, a cross-tool standardDISABLE_TELEMETRY: Set to1to opt out of telemetry. The official documentation clarifies that telemetry data does not contain your code, file paths, or bash commands; but for strict compliance environments, enable this to keep it clean.DO_NOT_TRACK: Set to1, equivalent toDISABLE_TELEMETRY. This is a widely adopted cross-tool convention among developer CLIs; setting it once applies to many tools.
There is also a one-click toggle—
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1. The official docs note it is equivalent to settingDISABLE_AUTOUPDATER,DISABLE_FEEDBACK_COMMAND,DISABLE_ERROR_REPORTING, andDISABLE_TELEMETRYsimultaneously. In high-privacy or isolated environments, disabling everything via a single variable is convenient.
Category 4: Multi-account & Context (Advanced but Useful)
Two highly recommended variables for advanced setups:
CLAUDE_CONFIG_DIR: Overrides the configuration directory (default is~/.claude). All settings, credentials, session histories, and plugins are stored here. Its most valuable usage is running multiple accounts in parallel—the official example is very practical:
# Create an alias with an isolated config directory for work
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'You can split your work and personal accounts this way: run claude normally for personal use, and run claude-work to use a completely isolated set of options and logins. Histories, credentials, and MCP configurations on both sides remain completely separated.
DISABLE_AUTO_COMPACT: Set to1to disable "automatic compaction when approaching the context limit" (the auto-compact mechanism explained in Chapter 19). Manual/compactremains functional. Set this only if you want complete control over when compaction happens—most users should keep it at default.
⚠️ Important Reminder: The official documentation makes it clear—Claude Code only reads environment variables at startup. So after modifying any variable (whether in the shell or
settings.json), you must exitclaudeand restart it for changes to take effect. If modifications seem ignored, it is likely because you haven't restarted.
💡 Summary in one sentence: The most commonly used variables fall into four groups—connections (
ANTHROPIC_API_KEY/BASE_URL/MODEL), timeouts (API_TIMEOUT_MS, default 10 minutes), privacy (DISABLE_TELEMETRY/DO_NOT_TRACK), and multi-account (CLAUDE_CONFIG_DIR); always restartclaudeafter editing.
05 Precedence: When Settings Overlap, Who Wins?
At this point, a natural question arises: if a model can be set via ANTHROPIC_MODEL, chosen via the /model command, and written as a model field in settings.json—if all three are configured, which one wins?
This is what "precedence" addresses. If you don't clear this up, you'll run into confusing situations where your settings are seemingly ignored.
First, remember the official general rule—Environment Variables > Setting Fields:
When the same behavior is controlled by both an environment variable and a setting field, the environment variable takes precedence. For example,
ANTHROPIC_MODELoverrides themodelsetting. When the environment variable is unset, the setting field applies.
Analogy: The boss's verbal instruction overrides the employee handbook. The employee handbook (settings.json fields) explicitly states "taxi capped at $50"; but today, the boss tells you verbally: "This is a long trip, just submit whatever the taxi costs" (environment variable)—for this trip, you naturally follow the boss's verbal instruction. The environment variable is like that "verbal instruction," taking precedence over written rules in the handbook. When the boss doesn't specify (the environment variable is unset), you fall back to the handbook (setting fields).
However, there is an intuitive exception that easily trips up beginners: environment variables do not override everything. For model selection, the official documentation explicitly notes:
--modeland/modeloverrideANTHROPIC_MODEL.
That means the precedence chain for models looks like this:
/model command / --model flag ← Highest (your on-the-spot choice)
↓ Overrides
ANTHROPIC_MODEL environment variable ← Medium
↓ Overrides
settings.json's model field ← FallbackWhy is model selection inverted? It actually makes sense—manually switching via /model in a session represents an explicit on-the-spot intent ("I want to use this model right now"), which should naturally override the pre-configured environment variable. This follows the same logic: the more "on-the-spot and explicit" the command is, the higher its precedence.
| Configuration Method | Relative Precedence | Summary |
|---|---|---|
/model command / --model flag | Highest | Your explicit choice during the session/at startup |
ANTHROPIC_MODEL environment variable | Medium | Your pre-configured default |
settings.json's model field | Fallback | Used only when neither of the above is set |
Note: This "command/flag overrides environment variable" rule applies to model configurations, and is not a universal rule. The official docs remind us that different features interact differently—for instance,
CLAUDE_CODE_EFFORT_LEVELactually overrides the/effortcommand. When in doubt, refer to the official environment variable table for specific behaviors instead of assuming.
This is easy to trip over once: you configure ANTHROPIC_MODEL in settings.json, but run /model to choose another model during a session and forget about it—continuing to use the temporary choice while wondering why it differs from your configuration. The logic is simple: the on-the-spot selection in /model overrides pre-configured variables. This is by design, not a bug.
💡 Summary in one sentence: The general rule is Environment Variables > Setting Fields (verbal commands override the handbook); but model selection is an exception—explicit commands like
/model/--modeloverrideANTHROPIC_MODEL; the pattern is "the more explicit and on-the-spot, the higher the precedence," so check official docs when in doubt.
06 Action: Set a Variable and Verify It Works
Theory is nothing without practice. Let's walk through a minimal feedback loop: set a variable → start a session → verify it works. This does not rely on any complex setup and takes just a few minutes.
We'll use BASH_DEFAULT_TIMEOUT_MS (default timeout for bash commands)—chosen because it is safe, observable, and won't break your connections if configured wrong.
Step 1: Check the default state (in terminal, before starting claude)
Start a session without setting anything first:
claudeOnce in, ask it about the current bash timeout:
What is my current default bash command timeout in milliseconds? Read BASH_DEFAULT_TIMEOUT_MS from your environment, or tell me the default value if unset.Expected: It will tell you the variable is not explicitly set, falling back to the default 120000 (2 minutes). Remember this baseline value for comparison later. Then exit the session (type /exit or press Ctrl+C twice).
Step 2: Set a temporary value in the shell and restart the session
Return to the terminal, run export with a different value (5 minutes), and start a session immediately:
export BASH_DEFAULT_TIMEOUT_MS="300000"
claudeWindows PowerShell uses:
$env:BASH_DEFAULT_TIMEOUT_MS = "300000"followed byclaude.
Once in, ask the same question:
What is my current default bash command timeout in milliseconds?Expected: This time it should read 300000 (5 minutes) instead of the default 120000. Seeing the value change from 120000 to 300000 confirms your temporary setting was read successfully. This verifies that "Method 1: Temporary shell settings" works.
Step 3: Verify that "it disappears after closing the terminal"
Exit the session, close this terminal window completely, open a new one, and run claude (without exporting anything this time), asking the same question again.
Expected: It reverts to 120000 (default). Because export only affects the terminal that was closed—proving what Section 02 noted: "temporary shell settings disappear once the terminal is closed."
Step 4 (Optional): Add to settings.json to make it persistent
If you want this value to persist long-term regardless of startup method, add it to settings.json. The safest way is to write it into the file that belongs only to you and is not tracked in git:
{
"env": {
"BASH_DEFAULT_TIMEOUT_MS": "300000"
}
}Add to
.claude/settings.local.json(current project root, not in git), or~/.claude/settings.json(your home directory, shared across all projects) based on your desired scope—refer to the table in Section 03.
After saving, open a new terminal and run claude directly (without export), then ask again—this time it should consistently read 300000, and it remains there no matter how you start claude in the future. This is the benefit of "Method 3: settings.json configuration" over temporary shell exports: configure once, save forever.
Running these four steps walks you through the entire process of "setting → verifying effect → verifying scope → persisting" environment variables. Configuring future variables follows this exact routine: test temporarily, then persist in settings.json once verified.
💡 Summary in one sentence: The most robust way to verify if a variable works is to ask Claude for its value; start with a temporary shell
export(disappearing when closing the terminal), and write it tosettings.jsononce confirmed—running this feedback loop demystifies environment variables.
07 Summary
In this article, we went under the surface to identify and configure the "master switches" controlling Claude Code's low-level behaviors—environment variables.
To summarize, the core covers five topics: what they govern, where they are set, which file to write into, what the common variables are, and precedence when they overlap. Let's lay them out in a table:
| What you need to clarify | Answer | Key points |
|---|---|---|
| What environment variables govern | Low-level behaviors like connections, authentication, timeouts, and privacy | Key-value switches read at startup; set once, benefit long-term |
| Where to set | Temporary shell / ~/.zshrc / env in settings.json | Choose based on scope; settings.json is recommended for long-term use |
| Which settings file to write into | Home directory / Project root (tracked in git) / .local (untracked) | Never commit credentials to git |
| Most common variables | ANTHROPIC_*, API_TIMEOUT_MS, DISABLE_TELEMETRY, CLAUDE_CONFIG_DIR | Each has a default; always restart to apply changes |
| Precedence when they overlap | Environment Variables > Setting Fields; but /model overrides ANTHROPIC_MODEL | The more explicit and on-the-spot, the higher the precedence |
You should now be able to: understand what environment variables govern in Claude Code, know the scope of the three setup methods, distinguish which settings files enter git, identify common variables for connections/timeouts/privacy/accounts, and understand precedence. More importantly—you set a variable, verified it works, and saw the difference between "temporary shell exports" and "persisted files." With this barrier broken, environment variables shift from "advanced black magic" to switches you can tweak at will.
Recall the opening quote: environment variables are not "only for advanced players," but "set once, benefit forever." Many of the repetitive manual configurations from the first 41 chapters can now be resolved once and for all using these variables.
The next chapter 43 "Git Workflows"—environment variables have helped you settle Claude Code's low-level behaviors, and next, it's time to integrate it into your daily development rhythm. Speaking of development, Git is unavoidable: letting Claude write commit messages, review diffs, open PRs, or resolve conflicts... How far can it help in Git, and what pitfalls must you watch out for? Let's discuss it in the next chapter. Think about it: you might delegate git commit to AI, but do you dare let it handle git push to your production branch?