The complete guide

Bazel remote cache: HTTP setup, security, and managed options

A Bazel remote cache shares action outputs across developers and CI without adopting remote execution. This guide explains the action cache and CAS, configures Bazel's HTTP protocol, locks down pull-request uploads, and compares managed and self-hosted backends.

What is a Bazel remote cache?

Bazel breaks a build into actions with explicit inputs, output names, commands, and environment. It hashes those inputs into an action key. A remote cache lets one machine upload the action result and output files so another machine with the same key can download them instead of executing the action.

Remote caching is independent of remote execution. A cache reuses completed work; remote execution sends work to a remote worker. Teams can get most of the repeat-build benefit by adding --remote_cache while continuing to execute misses locally. For the general caching model, read the complete guide to remote build caching.

How the action cache and CAS work

Bazel's remote cache stores two related kinds of data:

  • Action cache (AC). Maps an action digest to result metadata: exit code, output paths, output digests, and execution metadata.
  • Content-addressable store (CAS). Stores the output bytes by content digest. Identical files are stored once even when several actions reference them.

On a lookup, Bazel asks for /ac/<action-digest>, then downloads referenced blobs from /cas/<content-digest>. On a miss it executes locally and uploads the new result. The HTTP server can treat the payloads as opaque bytes; correctness comes from Bazel's digests and declared action model.

Bazel HTTP remote cache setup

Commit the endpoint in .bazelrc, but keep credentials outside source control. Cachely uses the bare host because Bazel appends the AC and CAS paths:

# .bazelrc
build --remote_cache=https://remote.cachely.dev

# Read the cache everywhere, upload from trusted CI only.
build --remote_upload_local_results=false
build:cachely-ci --remote_upload_local_results=true

try-import %workspace%/user.bazelrc
# user.bazelrc (gitignored)
          build --remote_header="Authorization=Bearer ${BAZEL_CACHE_TOKEN}"

In CI, inject the header from the secret store instead of writing user.bazelrc. Cachely also accepts HTTP Basic authentication with the workspace token as the password. The same token works across Bazel, Gradle, Nx, and Turborepo.

Secure CI and pull-request builds

A cache hit becomes trusted build input, so write access deserves the same care as artifact publishing. Untrusted pull requests should benefit from trusted entries without being allowed to create entries that protected branches later consume:

# Pull-request jobs: read the cache, never write to it
build --remote_upload_local_results=false
          build --remote_header="Authorization=Bearer ${BAZEL_CACHE_TOKEN}"

Pair the client flag with a server-enforced read-only token. The flag prevents normal uploads; the token protects against a changed configuration or malicious build step - .bazelrc options apply in order and the command line overrides them, so the flag alone is guidance. Protected branch CI gets a separate read-write token and opts into uploads with --config=cachely-ci. Cachely scopes tokens per workspace and permission so one pipeline can be revoked without rotating every developer and repository.

The same reasoning applies to laptops. Bazel can upload locally produced action results, but a developer environment is rarely as hermetic as CI: an absolute path, an untracked host tool, or a file edited mid-build can produce an action result that is wrong for everyone else who replays it. Keep developer machines on read-only tokens until your builds are demonstrably reproducible across machines.

Managed vs self-hosted Bazel remote cache

Self-hosted bazel-remote or HTTP storage

Open-source servers such as bazel-remote support HTTP and gRPC with disk or object storage. A generic HTTP backend can also implement Bazel's GET/PUT protocol. Self-hosting gives control over locality and retention, but your team owns authentication, TLS, garbage collection, capacity, monitoring, and uptime.

Object storage

Bazel can use compatible object-storage HTTP endpoints. This reduces server code but still leaves identity, least-privilege credentials, lifecycle rules, visibility, and cache-poisoning controls to your team. A broad bucket key shared by trusted and untrusted builds is not a safe boundary.

Managed standalone cache

A managed endpoint is the focused option when you want native Bazel caching without operating its backend or buying a larger remote-execution platform. Cachely implements the HTTP AC/CAS routes, manages storage and retention, offers read-only tokens, and reports cache usage alongside other build tools. See the self-hosted Bazel cache comparison for the operational trade-offs.

Troubleshooting Bazel remote cache misses

  • Treat 404 as a normal miss. Bazel executes the action and uploads it when writes are enabled.
  • Check endpoint shape. Use the bare Cachely host. Adding /ac or /cas duplicates the path Bazel appends.
  • Confirm the authorization header. A 401 is an invalid, missing, or revoked token, not a cache miss.
  • Align toolchains and platforms. Different compilers, flags, environment, execution platforms, or repository inputs create different action keys.
  • Find non-hermetic actions. Undeclared host inputs and non-reproducible outputs undermine safe reuse even when the protocol is configured correctly.

Validate with two clean output bases and identical source inputs. The first run should execute and upload; the second should report remote cache hits. Cache activity in the Cachely dashboard confirms whether requests reached the expected workspace.

Related guides

Try a managed Bazel remote cache
Free for developers. Point Bazel at one HTTP endpoint and keep executing misses locally.
Start freeSee pricing
FAQ

Bazel remote cache: frequently asked questions

What does a Bazel remote cache store?
It stores action-result metadata in the action cache (AC) and output files in a content-addressable store (CAS). Bazel maps an action key to its result, then downloads the referenced output blobs from the CAS.
How do I enable Bazel remote caching?
Set --remote_cache to an HTTP or gRPC cache endpoint in .bazelrc or on the command line. For an authenticated HTTP cache, also send credentials such as an Authorization header or use supported HTTP Basic authentication.
Is Bazel remote caching the same as remote execution?
No. Remote caching reuses outputs from actions that ran elsewhere. Remote execution sends actions to workers that execute them remotely. A team can use remote caching by itself without adopting remote execution.
How do I make a Bazel remote cache read-only in pull requests?
Use a server-enforced read-only token and keep build --remote_upload_local_results=false in the committed .bazelrc, letting only trusted CI opt back in with --config=cachely-ci. Pull requests can consume trusted cache entries but cannot upload results that protected builds may later reuse.
Can Cachely host a Bazel remote cache?
Yes. Cachely implements Bazel HTTP remote caching for both /ac and /cas. Point --remote_cache at https://remote.cachely.dev and authenticate with a workspace token; no proxy or custom Bazel plugin is needed.
Why does Bazel miss the remote cache?
Common causes are different action inputs, environment or toolchain drift, non-hermetic actions, a missing authorization header, or a URL with an extra path. Use --remote_cache and Bazel execution logs to distinguish a normal 404 miss from an authentication or connectivity failure.