Hash every env var you read

A variable missing from env serves builds made with the old value.

From turborepo, environment/RULE.md and gotchas.md.

Staging URL in a prod build

Build, switch API_URL to production, build again.

Not declaredBad
dist/app.jsnot built
"build": { "outputs": ["dist/**"] }
In envGood
dist/app.jsnot built
"env": ["API_URL"]

Turbo never reads .env

Change a value in apps/web/.env.production.

Default inputsBad
"env": ["DATABASE_URL"]
.env in inputsGood
"inputs": ["$TURBO_DEFAULT$", ".env*"]
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "env": ["API_URL", "DATABASE_URL", "NEXT_PUBLIC_*", "!NEXT_PUBLIC_ANALYTICS_ID"],
      "inputs": ["$TURBO_DEFAULT$", ".env", ".env.local", ".env.production", ".env.production.local"],
      "outputs": [".next/**", "!.next/cache/**", "!.next/dev/**"]
    }
  }
}

Why it matters, pros and cons

Why it matters

Values in env are part of the hash. Your framework loads .env files, and they are gitignored, so only inputs makes turbo hash them.

Pros

  • Each value gets its own cache entry.
  • Switching back is an instant hit.
  • Wildcards and ! keep long lists short.

Cons

  • Framework prefixes like NEXT_PUBLIC_* are inferred already.
  • Vars set inside a script are invisible to turbo.
  • A long env list is fine, not a smell.