.agents/skills/turborepo/references/caching/RULE.md
Turborepo's core principle: never do the same work twice.
fingerprint(inputs) → stored outputs
If inputs haven't changed, restore outputs from cache instead of re-running the task.
These affect ALL tasks in the repo:
package-lock.json / yarn.lock / pnpm-lock.yamlglobalDependencies (or global.env when using globalConfiguration)globalEnv (or global.env)turbo.json configuration{
"globalDependencies": [".env", "tsconfig.base.json"],
"globalEnv": ["CI", "NODE_ENV"]
}
These affect specific tasks:
inputs)package.json contentsenv keydependsOn)global.inputs (when using futureFlags.globalConfiguration — see below){
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["src/**", "package.json", "tsconfig.json"],
"env": ["API_URL"]
}
}
}
global.inputs Changes the Hash EquationWhen futureFlags.globalConfiguration is enabled, global.inputs files are not part of the global hash. Instead, they are prepended to every task's inputs and folded into the task hash. This is a fundamental change from globalDependencies.
With globalDependencies (default):
task cache key = hash(global hash, task hash)
↑ includes globalDependencies file hashes
Changing a globalDependencies file invalidates every task, regardless of task-level inputs. There is no way for a task to opt out.
With global.inputs (futureFlags.globalConfiguration):
task cache key = hash(global hash, task hash)
↑ includes global.inputs file hashes (merged with task inputs)
global.inputs files are merged into each task's input globs. This means:
"inputs": ["$TURBO_DEFAULT$", "!$TURBO_ROOT$/tsconfig.json"]global.env, etc. — but not file hashes from global.inputs){
"futureFlags": { "globalConfiguration": true },
"global": {
"inputs": ["tsconfig.json", ".env"]
},
"tasks": {
"build": {
"outputs": ["dist/**"]
},
"lint": {
"inputs": ["$TURBO_DEFAULT$", "!$TURBO_ROOT$/tsconfig.json"]
}
}
}
In this example, changing tsconfig.json invalidates build (it's in the task's inputs) but not lint (which explicitly excludes it). With globalDependencies, both would have been invalidated.
outputs{
"tasks": {
"build": {
"outputs": ["dist/**", ".next/**"]
}
}
}
.turbo/cache/
├── <hash1>.tar.zst # compressed outputs
├── <hash2>.tar.zst
└── ...
Add .turbo to .gitignore.
On cache hit, Turborepo:
FULL TURBO in output)# First run - executes build, caches result
turbo build
# → packages/ui: cache miss, executing...
# → packages/web: cache miss, executing...
# Second run - same inputs, restores from cache
turbo build
# → packages/ui: cache hit, replaying output
# → packages/web: cache hit, replaying output
# → FULL TURBO
outputs array means task runs but nothing is cachedoutputs key cache nothing (use "outputs": [] to be explicit)