Strict mode is safer, and it is the default. It passes a task only the environment variables listed in the env and globalEnv keys of turbo.json, so undeclared values cannot silently change output while the cache keeps hitting. Loose mode exposes everything, which makes wrong cache hits easy.
The core problem
A cache key is a hash of declared inputs. An environment variable that affects a build but is not in the hash is a hidden input. Change it and the hash stays the same, so the tool restores an old output. Turborepo's documentation gives a concrete case: a build made with an API address for a preview environment, then a production build that gets a cache hit and ships the preview address.
The two modes
| Strict (default) | Loose | |
|---|---|---|
| Variables a task can see | Only those in env and globalEnv | All variables in the process |
| Missing a declaration | Task likely fails | Task works, hash is wrong |
| Best for | CI and production builds | Short term migration |
| Enable | Default | The loose value for the env mode flag |
The documentation is candid that strict mode is not a guarantee. If your application gracefully handles a missing variable, a task can succeed and still produce an unintended cache hit.
How to declare variables
{
"globalEnv": ["IMPORTANT_GLOBAL_VARIABLE"],
"tasks": {
"build": {
"env": ["MY_API_URL", "MY_API_KEY"]
}
}
}
The globalEnv list changes the hash for every task. The env list is per task, so a lint task need not miss cache when an API key changes while a build task should.
Where secrets fit
Variables that must exist at runtime but should not affect the hash go in the passthrough lists. That keeps them available without changing the key. Be careful: a build that bakes a secret into its output is a problem regardless of hashing, because outputs are cached and may be shared through a remote cache.
Practical rules
- Stay in strict mode for CI and releases.
- Use loose mode only while migrating, with a ticket to move back.
- Declare every variable that changes build output, including those from frameworks. Turborepo infers common framework prefixes per package, but check the list for your stack.
- Add .env files to inputs or global dependencies so a changed value changes the hash.
- Account for variables from dependencies that have no build step, by declaring them in the consuming package.
- Watch for CI vendor variables: strict mode filters them out until you declare them.
A cheap test
Build with one value, change the variable, build again. A hit on the second build means your declaration is missing.
Quick check
Does Turborepo load my .env files?
No. It leaves loading to your framework or a tool like dotenv, but you must add the files to inputs so changes affect the hash.
Is strict mode a complete guarantee?
No. It makes failures more likely when declarations are missing, but an app that tolerates a missing variable can still produce a wrong cache hit.
Sources
Last reviewed: 19 October 2026.