# Environment variables (/docs/reference/environment)



Every environment variable warden reads, and the ones `warden run`, `batch` and `e2e` set for your commands; look here when a setting needs to apply to a whole shell or CI job instead of one flag.

## Read by warden [#read-by-warden]

| Variable                                                           | Effect                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `WARDEN_HOME`                                                      | state directory (default `~/.warden`): `warden.db`, `builds`, `batches`, `ccache`, `affected`, `logs`, `skill`, `ui`, `simd`, `android`, `perf`, `tools`                                                                                                                                                                                                                       |
| `WARDEN_SESSION_ID`, `CLAUDE_CODE_SESSION_ID`, `CLAUDE_SESSION_ID` | treat the caller as this agent session (first one set wins; a CI owner outranks them)                                                                                                                                                                                                                                                                                          |
| `CI` (`true` / `1`), `GITHUB_ACTIONS=true`                         | detected as a CI owner; run id from `GITHUB_RUN_ID`, `CI_PIPELINE_ID` or `BUILDKITE_BUILD_ID`. `CI` also flips the e2e `shutdown` default to `always`                                                                                                                                                                                                                          |
| `WARDEN_TRACE_PROCESSOR`                                           | path to a `trace_processor_shell` for `warden perf` Android native analysis, instead of the pinned copy in `$WARDEN_HOME/tools/` ([profile an app](/docs/devices/perf))                                                                                                                                                                                                        |
| `WARDEN_GOLDEN=0`                                                  | don't clone new sims from golden images                                                                                                                                                                                                                                                                                                                                        |
| `WARDEN_ANDROID_SNAPSHOT=0`                                        | Android: cold-boot claimed emulators instead of launching them from warden's quick-boot snapshot of the AVD, and never build one; `=1` turns it on over a config `android.snapshot: false`. Only `0/1/true/false/on/off/yes/no` count; another value is warned about and the config decides ([Golden images](/docs/devices/golden-images#android-wardens-quick-boot-snapshot)) |
| `WARDEN_EMULATOR_AUDIO=1`                                          | Android: keep the emulator's audio (claimed emulators run `-no-audio` otherwise)                                                                                                                                                                                                                                                                                               |
| `WARDEN_ARRANGE=0`                                                 | don't tile Simulator.app windows after iOS claims                                                                                                                                                                                                                                                                                                                              |
| `WARDEN_HEADLESS=1` (or `true`)                                    | `claim`, `run`, `batch`, `e2e`: close the claimed sims' Simulator.app windows when no flag or config `headless` decides ([Headless e2e](/docs/devices/headless)); `warden dev` ignores it                                                                                                                                                                                      |
| `WARDEN_PRIORITY`                                                  | boot queue priority (`interactive\|high\|normal\|low\|N`) when no `--priority` is given ([Capacity & queue](/docs/devices/capacity))                                                                                                                                                                                                                                           |
| `WARDEN_CAPACITY=off`                                              | skip the capacity gate and boot queue                                                                                                                                                                                                                                                                                                                                          |
| `WARDEN_CAPACITY_MAX`                                              | hard cap on weighted cpu units booted machine-wide (1 per iOS sim, 2 per Android emulator)                                                                                                                                                                                                                                                                                     |
| `WARDEN_CAPACITY_HEADROOM_GB`                                      | RAM held back from the budget (default `max(8, 25% of RAM)`)                                                                                                                                                                                                                                                                                                                   |
| `WARDEN_AUTO_GC=0`                                                 | turn off background auto-gc                                                                                                                                                                                                                                                                                                                                                    |
| `WARDEN_MAX_CACHE_SIZE`                                            | build cache cap (e.g. `50G`, default `20G`) for `warden gc` (`--max-cache-size`), `warden builds prune` (`--max-size`) and auto-gc, which runs when the cache is over it                                                                                                                                                                                                       |
| `ANDROID_HOME`, `ANDROID_SDK_ROOT`                                 | Android SDK location (`adb`, `emulator`); without them warden falls back to `PATH` and cannot build the Android snapshot                                                                                                                                                                                                                                                       |
| `NO_COLOR`, `FORCE_COLOR`                                          | output colour (a non-empty `NO_COLOR` turns it off)                                                                                                                                                                                                                                                                                                                            |

\| `WARDEN_ANDROID_UIAD=0` | Android: don't use the resident UiAutomation server (`warden-uiad`); snapshots, settle and input go through `uiautomator dump` and `input` ([Android driver](/docs/reference/e2e-driver#android-driver)) |
\| `WARDEN_ANDROID_SETTEXT=1` | Android: `fill` over a field that already holds text replaces it with one `warden-uiad` `setText` call instead of select-all and typing (specs, `warden ui fill`); off by default until the server re-reads focus before setting ([Android driver](/docs/reference/e2e-driver#android-driver)) |
\| `WARDEN_BACKGROUND=0` | turn off every detached worker (auto-gc, SessionEnd shutdown) |
\| `WARDEN_RELEASE_REPO` | GitHub repo `warden update` downloads releases from (default `delacournz/warden`) |
\| `WARDEN_SIMD`, `WARDEN_HELPER_DIR`, `WARDEN_SIMD_SOCKET`, `WARDEN_HELPER_URL` | the iOS driver helper: binary, directory, running socket, download URL ([iOS driver helper](/docs/reference/e2e-driver)) |
\| `WARDEN_SIMD_GUEST_MAX_AGE_S` | read by the `warden-simd` helper: seconds after which it replaces its iOS accessibility guests before they wedge (default 12; `0` never; a non-positive or non-numeric value keeps the default) |
\| `CODEX_HOME` | Codex config directory (default `~/.codex`) |
\| `WARDEN_BINARY` | npm package only: run this binary instead of the platform package's |
\| `WARDEN_NPM_REGISTRY` | npm registry `warden update` uses for npm installs (default `https://registry.npmjs.org`) |

## Set by `warden run` [#set-by-warden-run]

| Variable                             | Value                                                                                                               |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `WARDEN_UDIDS`                       | every claimed udid / serial                                                                                         |
| `WARDEN_UDID_<i>`                    | the i-th device                                                                                                     |
| `WARDEN_LEASE_IDS`                   | every lease id                                                                                                      |
| `WARDEN_PORTS`                       | every leased port                                                                                                   |
| `WARDEN_PORT_<i>`                    | the i-th `--port` lease                                                                                             |
| `WARDEN_APP_PATH`, `WARDEN_APP_HASH` | with `--app`: installed build + fingerprint                                                                         |
| `WARDEN_HEADLESS`                    | `1` when the run is headless (also for `batch` / `e2e` serve, setup and jobs, and `dev`'s Metro); removed otherwise |

## Set for Metro [#set-for-metro]

Every Metro warden starts (`warden dev`, `warden e2e` / `batch` serve) also gets these, so `app.config.ts` can tell worktrees apart. A native build never sees them, so the binary and its fingerprint stay the same.

| Variable          | Value                                                       |
| ----------------- | ----------------------------------------------------------- |
| `WARDEN_WORKTREE` | the checkout's folder name (`basename` of the git toplevel) |
| `WARDEN_BRANCH`   | the current branch; absent on a detached HEAD               |

Name the dev server after the worktree in expo-dev-launcher's server list and "Recently opened":

```ts
// app.config.ts
const worktree = process.env.WARDEN_WORKTREE;
export default { name: worktree ? `My App · ${worktree}` : "My App" /* … */ };
```

## Set for batch jobs [#set-for-batch-jobs]

`warden batch` and `warden e2e` add these to every job, on top of the `warden run` variables above (details in [e2e scripts](/docs/devices/scripts)):

| Variable                                                                                          | Value                                                                                                             |
| ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `WARDEN_UDID`                                                                                     | this job's device                                                                                                 |
| `WARDEN_WORKER`, `WARDEN_JOB`, `WARDEN_JOB_SEQ`, `WARDEN_PASS`, `WARDEN_BATCH_DIR`, `WARDEN_PORT` | worker index, job, run count on the worker, consecutive-pass index, batch directory, first leased port            |
| `WARDEN_LEG`                                                                                      | with `devices` legs: the leg's profile name; `WARDEN_UDIDS` / `WARDEN_UDID_<i>` then hold only that leg's devices |
| `WARDEN_METRO_URL`                                                                                | `e2e` suites with a `metro` section: the run's Metro, also set for `serve` and `setup`                            |
| `WARDEN_LOG_TIMESTAMPS`                                                                           | `1`: warden stamps every log line with its arrival time, so the job prints no clock of its own                    |

## Set for spec processes [#set-for-spec-processes]

`warden e2e` starts each run of `*.e2e.ts` specs as `bun test` with these set. Setting `WARDEN_UDID` yourself runs a spec by hand ([Write e2e specs](/docs/e2e/specs#run-a-spec-by-hand)); without it the `@delacour/warden-e2e` preload does nothing.

| Variable                                                                          | Value                                                                                                                                                                                                                                                                                          |
| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WARDEN_UDID`                                                                     | the device (iOS udid or Android serial). Required for a by-hand run                                                                                                                                                                                                                            |
| `WARDEN_DEVICE_NAME`                                                              | the leased device's name; pins devtools to its own Metro inspector page when several devices share a Metro                                                                                                                                                                                     |
| `WARDEN_PLATFORM`                                                                 | `ios` (default) or `android`                                                                                                                                                                                                                                                                   |
| `WARDEN_BUNDLE_ID`                                                                | the app under test: `spec.bundleId`, else the project's. Required for a by-hand run                                                                                                                                                                                                            |
| `WARDEN_APP_PATH`                                                                 | the installed build, for `app.reinstall()`                                                                                                                                                                                                                                                     |
| `WARDEN_VERSION`                                                                  | the `warden` version that started the run. The `@delacour/warden-e2e` preload exits 2 with the `bun add -d @delacour/warden-e2e@<version>` fix when its own (published) version differs; unset in a by-hand run, nothing is compared                                                           |
| `WARDEN_METRO_URL`                                                                | warden's Metro, for a dev client                                                                                                                                                                                                                                                               |
| `WARDEN_LAUNCH_ARGS`                                                              | JSON array of the launch arguments `app.open()` passes: `--initialUrl <metro url>`, `metro.launchArgs`, `spec.launchArgs`                                                                                                                                                                      |
| `WARDEN_E2E_OUT`                                                                  | where `results.jsonl` and the per-test artifacts go: `<batchDir>/e2e/<worker>-<seq>`                                                                                                                                                                                                           |
| `WARDEN_BIN`                                                                      | JSON array (or a bare path) of the `warden` that started the run; `device.perf` shells out to it. Set by `warden e2e`; without it `device.perf` throws `SESSION` ("WARDEN\_BIN is not set")                                                                                                    |
| `WARDEN_PERF`                                                                     | comma list of captures every attempt records (`js`, `react`, `native`, `metrics`, `frames`, `logs`, `all`), from `warden e2e --perf[=…]`; empty means the default set (metrics, frames, logs). Unset: no automatic profiling                                                                   |
| `WARDEN_PERF_BUDGETS`                                                             | JSON of the suite's `perf.budgets` (`startupColdMs`, `jankPct`, `maxCommitMs`, `p95CpuPct`, `peakMemoryMb`); a breach fails the test with `PERF_BUDGET`. Set by `warden e2e` when the config has budgets                                                                                       |
| `WARDEN_E2E_TIMEOUT`, `WARDEN_E2E_ACTION_TIMEOUT`, `WARDEN_E2E_ASSERTION_TIMEOUT` | the `spec` timeouts in milliseconds (defaults 120000, 30000, 5000)                                                                                                                                                                                                                             |
| `WARDEN_E2E_SPECS_DIR`                                                            | the suite's `specsDir`, used to name each spec's spec id                                                                                                                                                                                                                                       |
| `WARDEN_SIMD_SOCKET`                                                              | the pre-warmed iOS helper's socket (`warmDrivers`); unset, the driver starts a helper itself                                                                                                                                                                                                   |
| `WARDEN_SIMD`, `WARDEN_HELPER_DIR`                                                | where the `warden-simd` binary is ([iOS driver helper](/docs/reference/e2e-driver)); `warden e2e` sets `WARDEN_HELPER_DIR`                                                                                                                                                                     |
| `WARDEN_SIMD_PACE_MS`, `WARDEN_SIMD_HOLD_MS`, `WARDEN_SIMD_REPEAT_GAP_MS`         | read by the helper process: default keyboard pacing in ms (quiet after each key 30, key down to up 40, extra pause before a repeated key 300). A request's `paceMs` / `holdMs` / `repeatGapMs` wins. A React Native text field digests about 45-50 ms per key, so lower values lose characters |
| `WARDEN_SIMD_IDLE_TIMEOUT`                                                        | how long a `warden-simd` that `warden` starts keeps running with no client and no request: a duration, `10m` by default ([iOS driver helper](/docs/reference/e2e-driver))                                                                                                                      |
| `WARDEN_SIMD_TIMEOUT`                                                             | how long one request to a `warden-simd` that `warden ui` connects to may take before the client reports `E_TIMEOUT`: a duration, the client's 10 s by default. The first `snapshot` of a cold simulator on a hosted worker can need more ([iOS driver helper](/docs/reference/e2e-driver))     |
| `WARDEN_SIMD_STREAM_IDLE_S`                                                       | read by the helper process: close the quiescence stream after this many seconds without a `settle`. Unset or `0` keeps it open for the helper's life ([iOS driver helper](/docs/reference/e2e-driver))                                                                                         |
| `WARDEN_SIMD_TRACE`                                                               | `1`: the helper logs one debug event per quiescence sample (same as `warden-simd --trace`). Set it yourself                                                                                                                                                                                    |
| `WARDEN_HEADLESS`                                                                 | `1` on a headless run                                                                                                                                                                                                                                                                          |
| `WARDEN_E2E_DEBUG`                                                                | `1`: log every helper operation and `simctl` call with its duration to stderr. Set it yourself                                                                                                                                                                                                 |
| `WARDEN_BUN`                                                                      | read by `warden e2e`: the `bun` to run specs with (default: `bun` on `PATH`)                                                                                                                                                                                                                   |

## Read by `warden ui record` [#read-by-warden-ui-record]

Set these in the shell that runs `warden ui fill --secure` (and `record`) so the recorded spec holds a variable name, never the text ([Author specs by recording](/docs/e2e/author-specs#secrets)).

| Variable                  | Value                                                                                                                                                                                                                                                                                                                 |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WARDEN_UI_SECRET_<NAME>` | a secret value. A `fill --secure` (or any typed text equal to a non-empty value) is recorded as `process.env.WARDEN_UI_SECRET_<NAME>`, guarded to throw when unset. Set it for the spec run too (suite `env`). With none set, a secure fill uses `WARDEN_UI_SECRET_SECURE_TEXT` and a note to set it or edit the step |
