Environment
Sources & precedence
Sources are merged — never replaced. A task's env adds to and overrides the inherited set; it does not wipe PATH and friends. Highest wins:
task env > task env_file(s) > workspace [env] > root .env file > process env
[env] NODE_ENV = "development" [tasks.ci] env = { CI = "true" } # adds CI, keeps everything else
.env loading
Only the workspace-root
.envis auto-loaded (next totasks.toml) — no flag.Per-package
.envfiles are not auto-loaded, by design: frameworks (Next, Vite, …) load their own app-level.envat runtime;tsrowns only the shared, workspace-level vars.
env_file — per-task files
A task can load its own .env-style files with env_file, as a single path or a list. This is how a task overrides the default .env — e.g. a test task that needs .env.test:
[tasks.test] run = "vitest" env_file = [".env.local", ".env.test"] # .env.test overrides .env.local
Precedence:
env_filevalues layer above the root.envand workspace[env], and below the inline taskenv.Order: applied left to right — later files override earlier ones. A single string is a one-element list.
Paths are relative to the task's
dir(or the workspace root when unset).Missing files are skipped (like the root
.env), so an optional.env.localneed not exist — convenient in CI.
Expansion
$VAR/${VAR}are expanded after the full merge, against the final resolved env.[env]values may reference the process env and earlier keys in the same block. No forward references; there is no dependency graph for env resolution.
[env] ROOT = "/srv/app" LOGS = "$ROOT/logs" # → /srv/app/logs
A $VAR referenced in a run string but defined nowhere (task env, env_file, workspace [env], or .env) is a config error, caught at load time, exit 64:
✗ config error: task 'deploy': '$TARGET' is not defined…
Process env
The process environment is fully inherited; there is no filtering or allow-listing in v1.