Turbo Cache Hits in CI: What Goes Into the Hash and When It Breaks
Turbo's content-addressable pipeline caching can cut monorepo CI time, but only if you understand exactly what goes into the task hash and when remote caching stops paying off.
20 May 2026, 13:20 UTC

You push a branch that changes one package in a 40-package monorepo. CI runs for eighteen minutes. Fourteen of those minutes rebuild packages that have not changed since the last commit. Turbo's pipeline caching exists to eliminate that waste, but it only works if you understand what Turbo actually hashes—and what it ignores.
The core idea is simple: Turbo does not use file timestamps or directory mtimes to decide whether to rebuild. It generates a deterministic task identifier by hashing input file contents, the command arguments you pass, and a curated set of environment variables. This content-addressable approach means the cache key is derived from the data itself, not from filenames or timestamps. If two machines produce the same hash, Turbo assumes the task output is interchangeable. That is why a cache hit on your laptop can be reused in CI.
But the hash is exact. A plugin version bump, a changed lockfile, or an environment variable that Turbo tracks can produce a different identifier. The task re-executes. Nothing is broken; the inputs simply changed. Invalidation is automatic when source files change, which keeps outputs correct, but it also means invisible configuration drift can silently turn every task into a cache miss.
Inspecting cache decisions before you commit
You can ask Turbo what it intends to do without running the build. From the monorepo root, run:
turbo build --dry
This prints which tasks would be cached and which would re-execute. No build artifacts are written. If you expected a cache hit but see a miss, compare the inputs: did a tracked environment variable change? Did a dependency's source file change? Did the command arguments shift?
To test invalidation locally, modify a source file in a package and run turbo build --dry again. Tasks that depend on that file should flip from cache hit to re-execution. If they do not, your turbo.json dependency graph may be understating the relationship.
The remote cache trade-off
Local caching helps individual developers. Remote caching helps CI. When you set TURBO_REMOTE_URL to a shared cache endpoint, CI runners can download outputs produced by earlier runs or by other machines. Turbo's caching layer also integrates with remote execution protocols, which means you can distribute independent tasks across multiple machines instead of running them serially on one runner.
The trade-off is operational. Remote caching introduces network latency for every cache upload and download. It incurs storage costs on whatever server or service you point TURBO_REMOTE_URL at. And because the hash is exact, a mismatch between your local plugin versions and CI plugin versions can prevent remote hits entirely. You pay for the infrastructure without buying back the CI minutes.
A practical configuration decision
Here is a minimal turbo.json that scopes caching to build outputs while keeping the hash sensitive to source changes. This example assumes a Turbo version that uses the pipeline key; newer releases may expect tasks, so verify against your installed schema:
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
The outputs array tells Turbo which files to store in the content-addressable cache. The dependsOn field ensures upstream package builds are hashed into this task's identifier. If you omit outputs, Turbo runs the task but stores nothing useful for later retrieval.
To verify remote connectivity, check that TURBO_REMOTE_URL is set in your CI environment and observe whether task hashes are transmitted to the server. If the variable is missing, Turbo falls back to local caching only, and CI runners cannot share results.
Measure before you scale
Start by measuring. Run turbo build --dry on a clean branch and count the cache hits. Then change one tracked input and confirm the dependent tasks invalidate. Only enable remote caching after you have confirmed that local hits are stable and that your CI environment matches your local environment closely enough to produce identical hashes.
Caching is not free. It trades storage and network for compute. Make that trade deliberately.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.