Environment variables
Variables warden reads, and the ones warden run sets.
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
| 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) |
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) |
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); warden dev ignores it |
WARDEN_PRIORITY | boot queue priority (interactive|high|normal|low|N) when no --priority is given (Capacity & queue) |
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) |
| 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) |
| 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) |
| 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
| 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
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":
// app.config.ts
const worktree = process.env.WARDEN_WORKTREE;
export default { name: worktree ? `My App · ${worktree}` : "My App" /* … */ };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):
| 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
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); 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); 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) |
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) |
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) |
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
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).
| 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 |