Claude Code Environment Variables That Actually Matter

Generally yes, which is what makes them right for CI and containers and wrong for a personal preference. It also means a variable you exported for another project months ago still wins today. If a settings file appears to be ignored, check the environment first — the status and diagnostics commands show what the session actually resolved.
Lower the retry count so a pipeline fails fast instead of sitting through a long back-off, and raise the request timeout only if your network is slow. Bound Bash commands with the default and maximum timeout variables, and consider raising the Bash output cap for a run where you need a full log.
Feature-flag fetching, telemetry and error reporting collection, mobile push notifications for Remote Control, and background requests that are not essential to core functionality. Because some features depend on flag fetching, those stop working too, without an error naming them. Set it for policy reasons, not to reduce noise.
Set the auto-updater disable variable and the plugin auto-update override together. The first pins your CLI version to whatever your organisation has approved; the second keeps marketplace plugins current. Without the second, disabling the updater also stops plugin updates.
Yes. One environment variable is set in every subprocess Claude Code spawns, including Bash, PowerShell, tmux, hooks and MCP servers. A second is set only in direct tool spawns and not in stdio MCP servers, so a script can also tell those two contexts apart and behave accordingly.

Key Takeaway
Claude Code reads dozens of environment variables, and about fifteen are worth knowing. The most useful group tunes timeouts and limits for automation, where the interactive defaults are wrong. The most dangerous group are three switches whose effects reach further than their names suggest.
I once spent a morning working out why a pipeline sat for eleven minutes before failing. The answer was the retry count: the default is ten, which is generous and correct when a person is watching and would rather wait than start over, and completely wrong for a build agent that should have failed in thirty seconds and told someone.
That is the theme of this post. The defaults are chosen for an interactive session on a laptop, and a build agent, a container image, or a multi-tenant host wants different ones. Here is where environment variables sit in the precedence order, the groups worth setting deliberately, the switches that turn more off than you expect, and the two variables your own scripts can read.
Environment variables generally beat settings files, which is what makes them right for CI and wrong for a personal preference you will want to change later. If you find yourself exporting the same variable in every shell, it probably belongs in a settings file instead — and if you find a settings file being ignored, an environment variable is the first thing to check.
One consequence catches people out: a variable set for one project months ago is still set, and still wins. Authentication is the usual casualty, but the same applies to a model pin or a timeout you set once for a slow network and forgot. Check what the running session actually resolved rather than what you believe your configuration says.
This is the group with the best return on attention. The API timeout defaults to ten minutes, which is right for a long thinking turn on a slow link and far too patient for a pipeline. The retry count defaults to ten, which is the variable behind most mysterious multi-minute delays before a failure. And the Bash defaults bound how long a command may run and how much of its output comes back.
# Timeouts and retries — the defaults are tuned for a person
# sitting in front of a terminal, not for a build agent.
API_TIMEOUT_MS=600000 # default 10 minutes
CLAUDE_CODE_MAX_RETRIES=10 # default; LOWER it to fail fast
BASH_DEFAULT_TIMEOUT_MS=120000 # default 2 minutes
BASH_MAX_TIMEOUT_MS=600000 # ceiling the model may request
BASH_MAX_OUTPUT_LENGTH=30000 # default; hard maximum 150000
# Context and compaction
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80 # 1-100, default around 80
CLAUDE_CODE_DISABLE_1M_CONTEXT=1
# Background work
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1
CLAUDE_AUTO_BACKGROUND_TASKS=1 # auto-background after ~2 min
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS=600000The output cap is worth raising before you go hunting for a bug. By default Claude reads back 30,000 characters of Bash output, and a truncated test log looks exactly like a test suite that stopped early. The hard maximum is 150,000 — raising it costs tokens on a noisy command, so raise it for the run where you need the whole log rather than leaving it high everywhere.
Two groups that overlap more than they look. Authentication variables select and supply a credential; model variables decide what an alias resolves to. On a cloud provider both matter, because the aliases resolve to a built-in default that can lag the newest release. The table below is the shape of it rather than the full list.
The four you will actually set:
| Variable group | What to know |
|---|---|
| The API key variable | Overrides your subscription completely and silently. This is the single most common cause of confusing authentication behaviour |
| The provider switches | One variable per cloud provider selects where inference runs. The provider's own credentials and region settings then apply |
| The default-model pins | One per alias. On a third-party deployment, pinning is how you control when your team moves to a new model |
| The base URL variables | Point Claude Code at a gateway. Note that pointing the main one at a non-first-party host turns off MCP tool search by default |
Four of these come up constantly in locked-down environments, and one of them is much broader than its name. Auto-updates, telemetry and error reporting each have their own variable and do what they say. The non-essential-traffic switch is the blunt instrument.
# Updates
DISABLE_AUTOUPDATER=1
FORCE_AUTOUPDATE_PLUGINS=1 # keep PLUGIN updates while the
# CLI updater stays off
# Telemetry and reporting
DISABLE_TELEMETRY=1
DISABLE_ERROR_REPORTING=1
# The blunt instrument. Turns off feature-flag fetching, telemetry
# and error reporting, mobile push for Remote Control, and any
# background request that is not essential — which silently
# disables features that DEPEND on flag fetching.
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1That last switch turns off feature-flag fetching along with telemetry and push notifications, which means it also disables features that depend on flag fetching — quietly, with no error to tell you which ones. Set it because your security posture requires it, not to reduce noise, and expect a handful of capabilities to be absent afterwards. The same logic applies to the do-not-track family.
Three variables control where state lives, and the second one exists specifically for hosts that embed Claude Code. By default the project folder is derived from the whole working directory path, which is fine on a laptop and wrong when a host wants each session in its own place. Naming it yourself requires setting the config directory too, and the value is read once at startup — so a settings file cannot supply it.
# Where state lives
CLAUDE_CONFIG_DIR=/srv/tenant-a
CLAUDE_CODE_PROJECT_DIR_NAME=work # name the <project> folder
# yourself; IGNORED unless
# CLAUDE_CONFIG_DIR is set,
# 1-64 [A-Za-z0-9_-], read
# once at startup so an env
# block in settings cannot
# set it
CLAUDE_CODE_SKIP_PROMPT_HISTORY=1 # write no transcripts at all
# Detection, for hooks and scripts that need to know:
# CLAUDECODE=1 set in every spawned subprocess
# CLAUDE_CODE_CHILD_SESSION=1 set in direct tool spawns only,
# not in stdio MCP serversTwo are set by Claude Code rather than read by it, and they are the reliable way for a hook or a wrapper to know where it is running:
A few variables reach further than their names imply, and each has produced at least one confusing afternoon. The default Haiku model variable is the clearest example: it is read everywhere the small fast model is used, so changing it also changes conversation summarisation and other background work, not only whatever you were aiming at.
One pairing is genuinely useful and non-obvious. Disabling the auto-updater stops Claude Code updating itself, which many organisations require — but it also stops plugin updates. Setting the plugin-update override alongside it keeps your marketplace plugins current while the CLI version stays pinned to whatever your organisation has approved.
Set the timeout and retry variables on any build agent, because the interactive defaults will otherwise turn a fast failure into a slow one. Keep the blunt traffic switch for environments that genuinely require it, and read the list of what it disables first. And whenever behaviour differs between two machines that should be identical, look at the environment before the configuration — it wins, and it is the part nobody has written down.
Sources & further reading