Configure Bazel Remote Caching to Reuse Build Outputs Across Machines
Learn how to enable Bazel’s remote cache, verify it works, and avoid common pitfalls that silently degrade performance.
28 Jul 2026, 23:36 UTC

Enable Bazel remote caching in one step
The quickest way to start reusing build outputs is to add a --remote_cache flag to your Bazel invocation or to your .bazelrc file. When the flag is present, Bazel treats every action as a key‑value lookup: it computes a hash from the action’s inputs, command line, and toolchain, then asks the cache for that hash. On a hit it downloads the stored outputs; on a miss it runs the action locally (or on a remote executor) and uploads the result for future use.
Example configuration
Suppose you have an HTTP cache running at https://cache.example.com and you need to authenticate with a username/password stored in ~/.netrc. Add the following lines to a project‑level .bazelrc (checked into version control so all developers get the same setting):
# .bazelrc
build --remote_cache=https://cache.example.com
build --remote_timeout=120
# optional: enable experimental gRPC cache if your backend supports it
# build --experimental_remote_cache
If you prefer to set the cache only for a single invocation, you can pass the flag on the command line:
bazel build //my:app --remote_cache=https://cache.example.com
No special Bazel version is required; the feature has been stable since Bazel 0.15. However, the --experimental_remote_cache flag (which switches to the gRPC‑based cache) is only available in Bazel 5.0 and later.
How the remote cache works under the hood
When Bazel evaluates an action, it builds a action key that is a SHA256 hash of:
- the contents of all input files (including transitive dependencies)
- the exact command line used to run the tool
- the toolchain configuration (e.g., compiler version, libc)
This key is sent to the cache backend. If the backend returns a stored output bundle, Bazel skips the local execution step and extracts the files directly into its output tree. If the backend reports a miss, Bazel runs the action (either locally or on a remote executor if you have also configured --remote_executor) and then uploads the resulting outputs keyed by the same hash.
Cache hits are logged with the line remote cache hit; misses appear as remote cache miss. These messages appear in the standard console output, making it easy to spot whether the cache is being used.
Verifying that the cache is active
Follow these steps to confirm that your configuration is correct and that you are seeing real hits:
- Check startup options – Run
bazel infoand look for the--remote_cachevalue in the Startup options section. If it shows the URL you expect, the flag was read successfully. - Run a clean build – Execute
bazel clean --expungeto remove all local outputs, then build a target:
bazel build //my:app --remote_cache=https://cache.example.com
- Inspect the output – You should see lines like:
INFO: Remote cache miss for action @@@... (hash)
INFO: Remote cache hit for action @@@... (hash)
The first build will mostly show misses; a second immediate rebuild of the same target should show many hits and a noticeably shorter elapsed time.
- Measure improvement – Use the built‑in timing report:
bazel build //my:app --remote_cache=https://cache.example.com --nokeep_state_after_build
Compare the Total time reported at the end of the first and second runs. A reduction of 30‑70 % is typical for medium‑sized Java or C++ projects when the cache is warm.
Limits and common mistakes
Even though remote caching is robust, several configuration errors can silently degrade performance or produce incorrect results.
1. Wrong URL or missing credentials
If the cache URL is unreachable or the backend rejects the request (e.g., missing .netrc entry), Bazel treats every action as a miss. You will see only remote cache miss lines and no obvious error message. To detect this, run bazel info and verify that the Remote cache line shows OK (or check the backend’s logs for authentication failures).
2. Stale cache entries
Some cache backends (e.g., a simple HTTP server without expiration) will serve an entry indefinitely. If you change a source file but the action’s inputs hash stays the same (perhaps because you altered a file that is not declared as an input), Bazel will reuse the outdated output. The safeguard is to declare all inputs explicitly in your BUILD rules (using srcs, hdrs, data, etc.) and to avoid actions that read environment variables or timestamps unless they are part of the key.
3. Platform/toolchain mismatch
Remote execution workers must match the expected platform (OS, CPU architecture, libc version). If a worker runs on Ubuntu 20.04 while your toolchain expects Ubuntu 22.04, the action may produce different binaries even though the hash is identical. Bazel cannot detect this mismatch because the hash does not include the worker’s runtime environment. The remedy is to configure a homogeneous executor pool or to use --platforms and --extra_toolchains to force a matching toolchain.
4. Over‑reliance on the cache in CI
In a continuous‑integration pipeline, a warm cache can hide broken dependencies. Periodically run a bazel clean --expunge followed by a build without the remote cache to ensure that the hermeticity of your build is intact.
Practical way to check the result
After a build, you can inspect the cache directly if you have access to the backend. For an HTTP cache, a GET request to https://cache.example.com/ will return the stored output bundle if it exists. Comparing the hash of a locally built artifact with the hash returned by the cache confirms that the same bytes are being reused.
By following the configuration steps above, verifying with bazel info and the hit/miss logs, and watching for the three common pitfalls, you can reliably leverage Bazel remote caching to speed up incremental builds across developer workstations and CI agents.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.