Bazel Caching Without Rebuilding Everything: Disk Cache, Remote Cache, and Hermeticity
Reuse Bazel action outputs across machines and CI runs with --disk_cache and --remote_cache, but only if your actions stay hermetic.
24 Feb 2026, 12:07 UTC

You merge a one-character typo fix, and CI still spends ten minutes recompiling the world. The runner is fresh, so it has no memory of the build that finished five minutes ago on your laptop or last night on the main branch. Bazel can avoid this repetition, but only if you tell it where to store previous results and trust that those results are safe to reuse.
The useful takeaway: Bazel supports two caching targets—--disk_cache for a local on-disk store and --remote_cache for a shared server reachable over gRPC or HTTPS. They reuse action outputs keyed by content, but the feature is only trustworthy when build actions are hermetic, meaning they declare every input and avoid hidden state such as timestamps, absolute paths, or network access during the build.
How Bazel Keys an Action
Before running a step such as compiling a C++ translation unit or executing a test, Bazel derives an action key. The key combines the exact command line, the content digests of declared input files, and relevant environment values. If that key exists in the configured cache, Bazel copies or downloads the stored outputs instead of executing the command. That is a cache hit. If the key is absent, Bazel runs the action and may write the outputs to the cache for the next consumer.
An action cache is different from Bazel's built-in output base. The output base is local to one workspace and one machine; a disk cache survives bazel clean and can be shared across workspaces on the same machine, while a remote cache can be shared across machines and teams.
Local Disk Cache: Start Here
The --disk_cache flag points Bazel at a directory on your local filesystem. It requires no server and works across branches on the same workstation.
Run this from your project root. You need write permission to the cache directory:
bazel build //my/app --disk_cache=~/.cache/bazel/my-project
Then clear local metadata without touching the cache:
bazel clean
Rebuild with the same flag:
bazel build //my/app --disk_cache=~/.cache/bazel/my-project
Check the status summary at the end of the build. A warm run should report a high number of cached actions and lower wall time than the first run. Exact numbers vary by project and are not a benchmark.
Limitation: --disk_cache helps one machine. It does not help a CI runner that starts from a clean virtual machine for every pull request.
Shared Remote Cache for CI and Teams
For CI, point builds at a shared cache server speaking the Remote Execution API:
bazel build //... \ --remote_cache=grpc://cache.example.com:9092 \ --remote_upload_local_results=false
Run this in CI with permission to read the cache and, optionally, write to it. --remote_upload_local_results=false stops the current machine from uploading; set it to true only on trusted CI builders that produce canonical results. Allowing every developer laptop to upload increases the risk of cache poisoning, where a broken or non-hermetic build stores bad outputs under a valid key.
Third-party cache servers differ in eviction, authentication, and protocol support. Confirm your server's behavior before treating it as a drop-in replacement for local builds.
Diagnose Misses With aquery
If hit rates drop suddenly, inspect the actions Bazel plans to run:
bazel aquery //my/app --output=textproto
Look for actions with unexpected inputs or environment differences. A changed --copt value, a toolchain bump, or a different compilation mode changes the action key and invalidates downstream results. Treat a miss as a hermeticity audit trigger: an undeclared input, embedded timestamp, absolute path, or network access during the build can make two different outputs share one key, which Bazel describes as the tree-swinging hazard.
Trade-Offs and Safety
- Exact-key matching means small flag changes can invalidate many actions.
- Shared caches need storage, eviction, and access-control decisions.
- Never put secrets in build actions; anyone with cache read access may retrieve stored artifacts.
- Cached outputs are only as trustworthy as the actions that produced them.
Adopt in Stages
Start with --disk_cache locally to confirm warm rebuilds work. Add a shared remote cache for CI so pull-request builds start warm instead of cold. Gate uploads to trusted builders, monitor hit rates, and audit any sudden drop. A cache miss is often a hermeticity bug wearing a performance costume.
Verify flags for your installed Bazel version with bazel help build --long | grep -i cache, because flag names and defaults have shifted across major releases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.