Measure the right hit rate
Split local hits, remote hits, and misses by task. Then weight them by cold execution time. Ten remote hits on a six-minute compile matter more than hundreds of hits on a one-second formatting task. Compare equivalent branches and warmed time windows so first-run population does not distort the result.
Correctness comes first. A suspiciously high hit rate can mean the key omits an environment variable, toolchain version, or source input that changes the output. Every optimization must preserve the rule: same key means interchangeable output.
Diagnose one miss end to end
Choose one expensive task that should have hit, then reproduce it from the same commit in two clean environments:
1. Pin the same build-tool and runtime versions.
2. Run the task once and record its hash, inputs, outputs, and environment.
3. Run it on the second machine with the same source revision.
4. Compare the first differing input.
5. Fix that category and repeat until the keys match.Do not start by deleting caches. A cache clear proves only that a miss can execute; it does not explain why two equivalent builds produced different keys.
The common causes of cache misses
- Over-keying: documentation, tests, or unrelated global files invalidate production builds.
- Environment drift: CI and laptops use different runtimes, flags, paths, locales, or declared variables.
- Volatile values: timestamps, build numbers, temporary paths, and runner IDs enter the key.
- Non-deterministic outputs: generated ordering, random seeds, or embedded dates change identical builds.
- Output mistakes: required files are undeclared, so a hit restores only part of the result.
- Fragmented cache scope: machines authenticate to different workspaces or provider namespaces.
Tool-specific checks
Nx: inspect inputs, namedInputs, runtime inputs, and declared outputs. Use production filesets so specs and docs do not invalidate builds.
Turborepo: review task inputs, env, globalEnv, and globalDependencies; use run summaries to compare hashes.
Gradle: confirm the task is cacheable, inspect --info, align JDK and Gradle versions, and remove absolute-path or machine-specific inputs.
Bazel: compare action inputs, execution platform, toolchain, flags, and environment. Undeclared host reads are both a cache-rate and correctness problem.
Hit-rate improvement checklist
- Pin runtime, package manager, compiler, and build-tool versions.
- Declare every output-changing environment value and exclude irrelevant volatile values.
- Narrow task inputs without omitting correctness-critical files.
- Make generated outputs reproducible across machines.
- Confirm all clients use the same remote endpoint and workspace.
- Track time saved after each change, not just the headline percentage.
For Nx-specific examples, continue with tuning Nx inputs and outputs.
Related guides
- What is a remote cache?The pillar guide to content-hashed keys, local vs remote caching, and safety.
- Nx remote cacheSet up the official Nx self-hosted cache and see how the task hash works.
- Monorepo build performanceWhy monorepo builds get slow and the four cache layers that fix it.
- CI savings calculatorEstimate the build minutes, engineer hours, and CI cost remote caching saves.