Configuring the build cache for Bazel in local builds
You can use the Bitrise Build Cache for local Bazel builds too. Your local builds then read from the same cache as your CI builds, so an action your CI already ran doesn't have to run again on your machine.
The Bitrise Build Cache CLI sets this up for you by writing the cache flags to your ~/.bazelrc.
You can also set this up with an AI coding agent instead of following the steps manually. See Getting started with AI.
Before you startClick to copy link
Ensure you have:
- A working Bazel project on your machine, on Bazel 6 or later. The setup uses Bazel's credential helper support, which earlier versions don't have.
- A Bitrise workspace with the Build Cache enabled. Check it on the Build Cache page.
- Network access to the Bitrise hosts the Build Cache uses, if your machine is behind a VPN, a firewall, or an outbound proxy. See Network endpoints for firewalls and VPNs.
activate bazel writes a single block to ~/.bazelrc in your home directory. It never touches your project's .bazelrc.
Every Bazel project on your machine picks up the cache flags, not just the one you activated from. The repository URL reported to the dashboard is read from the git remote of the directory you ran the activation in, so on a machine with several Bazel projects they all report the first one. That affects dashboard attribution only, not cache correctness. Re-run the activation from another project's directory to change it.
Installing the CLIClick to copy link
Install the CLI with Homebrew (recommended):
brew install bitrise-io/bitrise-build-cache/bitrise-build-cache
Or, without Homebrew:
curl --retry 5 -sSfL \
'https://raw.githubusercontent.com/bitrise-io/bitrise-build-cache-cli/main/install/installer.sh' \
| sh -s -- -b ~/.local/bin
Make sure the install location is on your PATH, then check the install:
bitrise-build-cache --version
Keep the CLI on your PATH afterwards. Bazel runs the CLI on every build to fetch a fresh auth token, not only during setup.
Activating the cacheClick to copy link
Run the interactive wizard:
bitrise-build-cache activate --interactive
The wizard asks for the following:
- Sign in to Bitrise: opens your browser for authentication on the first run. The CLI stores the credentials in the OS keychain and refreshes them automatically, so later runs skip this step. On a machine without a usable keychain, the CLI falls back to storing them in a config file.
- Select a workspace: pick the workspace whose Build Cache you want to use. The CLI selects it automatically if you only have access to one.
- Which build tools should I set up: ensure Bazel is selected. Use space to toggle an option and enter to confirm.
- Cache for all projects, or only opted-in ones?: keep the default Always unless several projects live on this machine and you only want a subset to use the cache. See Per-project cache scoping.
- Display name for this machine's local invocations: the name your local builds show up under in the Build Cache dashboard, for example
local-<yourhandle>. - Enable cache push: keep the default Yes, push too. See Cache push mode.
The non-interactive equivalent, once you have signed in:
bitrise-build-cache activate bazel --cache
What the activation writesClick to copy link
A single marked block appended to ~/.bazelrc. Existing content is preserved, and re-running the activation only updates the block:
# [start] generated-by-bitrise-build-cache
build --credential_helper=*.services.bitrise.io=bitrise-build-cache
build --remote_cache=grpcs://bitrise-accelerate.services.bitrise.io
build --remote_timeout=600s
build --remote_header=x-flare-buildtool=bazel
build --remote_header=x-flare-builduser=
build --remote_upload_local_results
build --bes_backend=grpcs://flare-bes.services.bitrise.io:443
build --bes_results_url=https://app.bitrise.io/build-cache/invocations/bazel/
build --bes_timeout=2m
build --bes_upload_mode=wait_for_upload_complete
build --build_event_publish_all_actions
build --show_timestamps
build --remote_header='x-org-id=<workspace-id>'
build --bes_header='x-org-id=<workspace-id>'
build --remote_header='x-repository-url=https://github.com/<org>/<repo>.git'
build --bes_header='x-repository-url=https://github.com/<org>/<repo>.git'
build --bes_header='x-os=<detailed OS info>'
build --bes_header='x-locale=C'
build --bes_header='x-default-charset=UTF-8'
build --bes_header='x-cpu-cores=4'
build --bes_header='x-mem-size=16759259136'
# [end] generated-by-bitrise-build-cache
Two lines worth highlighting:
--credential_helperpoints at the CLI rather than storing a token, so Bazel fetches a fresh one on every build and an expiring login keeps working. This is why the CLI has to stay on yourPATH.--remote_upload_local_resultsis push mode, the default. Activating with--cache-push=falsewrites--noremote_upload_local_resultsinstead.
The x-os, x-cpu-cores, x-mem-size, x-locale, and x-default-charset headers describe your machine and are used for analytics only, and don't affect cache keys. They matter if you commit the configuration, because they're specific to the machine that generated the block. See What is safe to commit.
Verifying the setupClick to copy link
Run the CLI's health check:
bitrise-build-cache doctor
It reports the status of every part of the local setup and ends with an overall verdict. The probes for the other build tools report as skipped:
Bitrise Build Cache - doctor
CLI version: 3.x.y
Healthy:
✓ auth OAuth login (keychain) (workspace <id>), token valid until <iso-timestamp>
✓ keychain-smoke Set/Get/Delete round-trip OK
✓ auth-backend latency <ms>, source=keychain, workspace=<id>
✓ project-scope mode=always; cache_push=true (machine config); no .bitrise-build-cache.json found in <cwd> or parents.; would gate this directory: no
✓ xcelerate-proxy skipped (xcode not activated)
✓ xcelerate-wrapper-path skipped (xcode not activated)
✓ xcelerate-enrichment skipped (xcode not activated)
✓ ccache-helper skipped (c++ not activated)
✓ ccache-binary skipped (c++ not activated)
✓ log-dirs no activated tool writes logs
Overall: ok
To let the CLI repair the issues it can fix on its own, run bitrise-build-cache doctor --fix --interactive.
Running a buildClick to copy link
-
Run
bazel cleanfirst. Bazel's local action cache sits in front of the remote cache, so if a target is already built locally, Bazel serves it from disk and never contacts Bitrise - and you see no evidence that the remote cache works.cd path/to/your/bazel/projectbazel clean -
Build your target:
bazel build //your/target:name
A build that hits a warm cache ends like this:
(16:47:35) INFO: Invocation ID: <invocation-id>
(16:47:35) INFO: Streaming build results to: https://app.bitrise.io/build-cache/invocations/bazel/<invocation-id>
(16:47:36) INFO: Analyzed target //src/close-matching-prs:close-matching-prs (91 packages loaded, 9216 targets configured).
(16:47:36) INFO: Found 1 target...
(16:47:36) INFO: Elapsed time: 0.934s, Critical Path: 0.18s
(16:47:36) INFO: 11 processes: 4 remote cache hit, 7 internal.
(16:47:36) INFO: Build completed successfully, 11 total actions
Checking that it workedClick to copy link
The summary line is the proof. It reads differently in each of the three cases:
| Summary line | What it means |
|---|---|
11 processes: 4 remote cache hit, 7 internal. | Working. The actions were fetched from the Bitrise Build Cache. |
1 process: 4 action cache hit, 1 internal. | Served from your local cache. The remote cache was never contacted, so run bazel clean first. |
11 processes: 7 internal, 4 processwrapper-sandbox. | Built locally from scratch. Either the cache has no entries for this target, or authentication failed. Look for WARNING: Remote Cache: UNAUTHENTICATED earlier in the output. |
internal actions, such as symlinks and file writes, are never cacheable. The ratio that matters is remote cache hits against the actions that aren't internal.
| Signal | Where to find it | What success looks like |
|---|---|---|
INFO: N processes: X remote cache hit | Bazel build summary | A non-zero remote cache hit count after a bazel clean |
INFO: Streaming build results to: | First and last lines of the build | A link to the invocation is printed |
| Dashboard | The printed link, or the Build Cache page | A row appears under your display name, with the same hit counts |
Cache push modeClick to copy link
The setup in this guide activates the Build Cache with cache push enabled: your local builds read from and write to the shared cache. Every action your machine runs becomes an entry your teammates and CI can reuse, and your activity shows up on the Build Cache page.
If you'd rather have your local builds only read from the cache, see Opting into pull-only mode below.
Opting into pull-only modeClick to copy link
If your team already populates the cache from CI and you want your local builds to only read from it, turn cache push off. The trade-off is that your local builds contribute nothing, so if CI doesn't run yet the summary line keeps reporting 0 remote cache hit.
Re-run bitrise-build-cache activate --interactive and answer No, pull only at the cache push prompt, or run the non-interactive activate command:
bitrise-build-cache activate bazel --cache --cache-push=false
You can check which mode you're in without running a build:
grep upload_local_results ~/.bazelrc
# --remote_upload_local_results → push enabled (default)
# --noremote_upload_local_results → pull-only
Committing the configuration to your repositoryClick to copy link
Bazel already reads a .bazelrc from your workspace root, so the cache flags can be committed to the repository instead of being generated on every machine. Onboarding a dev machine then comes down to installing the CLI and signing in:
brew install bitrise-io/bitrise-build-cache/bitrise-build-cache
bitrise-build-cache auth login
How the files combineClick to copy link
Bazel reads its configuration files in this order, and the last value wins for a single-valued flag:
- The system file,
/etc/bazel.bazelrc - The workspace file,
<repo>/.bazelrc(the committed one) - The home file,
~/.bazelrc(what the activation writes, if it was ever run) - Command-line flags
So the home file takes precedence over the committed one, and command-line flags beat both. Add --announce_rc to a build to see what Bazel actually loaded.
Force pull-only for developers who don't run the activationClick to copy link
A developer who only installs the CLI and runs auth login never gets a ~/.bazelrc, so there is nothing to override the committed block. If you want those developers to only read from the cache regardless of what the CLI default is, commit the pull-only flag:
build --noremote_upload_local_results
On CI, run the activation. The CLI writes the push-enabled flag to ~/.bazelrc, and because the home file beats the committed workspace file, it overrides the committed pull-only flag on that machine only:
bitrise-build-cache activate bazel --cache
That gives you the split with no per-developer setup: the committed file is the pull-only default anyone who skips the activation inherits, and the environment that should write to the cache is the one that runs the activation.
What is safe to commitClick to copy link
| Lines | Commit | Why |
|---|---|---|
--remote_cache, --remote_timeout, --bes_backend, --bes_results_url, --bes_timeout, --bes_upload_mode, --build_event_publish_all_actions, --show_timestamps | Yes | Identical on every machine |
--remote_header='x-org-id=…' and the matching --bes_header | Yes | Workspace-wide, not machine-specific |
--remote_header='x-repository-url=…' and the matching --bes_header | Yes | It identifies this repository |
--noremote_upload_local_results | Optional, if you want to force pull-only for developers who skip the activation | Every developer who only runs auth login inherits it. Running the activation on CI writes --remote_upload_local_results to ~/.bazelrc, which overrides the committed value on that machine |
--credential_helper=*.services.bitrise.io=bitrise-build-cache | Yes, after checking the value | It's the bare binary name, which Bazel looks up on PATH. If the activation ran on a machine where the CLI wasn't on PATH, the CLI writes an absolute path instead. Don't commit that form |
--bes_header='x-os=…', x-cpu-cores, x-mem-size, x-locale, x-default-charset | No | Per-machine, and x-os contains your hostname. They're analytics only and don't affect cache keys, but committing them attributes everyone's builds to your machine |
--remote_header=authorization="Bearer …" | Never | A live credential. It only appears in a block generated on CI |
What happens without the CLI installedClick to copy link
If you commit the --credential_helper line, a teammate who hasn't installed the CLI can't build at all. Bazel looks the helper up on PATH, doesn't find it, and fails while initializing the remote cache:
ERROR: Could not find file with name 'bitrise-build-cache' on PATH '...'
ERROR: Could not find file with name 'bitrise-build-cache' on PATH '...'
ERROR: Could not find file with name 'bitrise-build-cache' on PATH '...'
ERROR: Error initializing RemoteModule
Bazel exits with code 2 and no targets are built. There's no fallback to a local build, because the failure happens before the build starts.
Installing the CLI and running bitrise-build-cache auth login on that machine resolves it.
Remote Build ExecutionClick to copy link
If Remote Build Execution is enabled for your workspace, you can use it locally by adding the --rbe flag to the activation. You need the workers set up for your workspace and the pool configuration in your repository's .bazelrc first. See Remote Build Execution for Bazel.
TroubleshootingClick to copy link
Start with the CLI's health check. It inspects every part of the local setup and repairs the issues it can fix on its own:
bitrise-build-cache doctor --fix --interactive
If you're still experiencing issues, check the following table:
| Issue | Fix |
|---|---|
| The wizard reports that it needs a terminal | Run TERM=dumb bitrise-build-cache activate --interactive for line-based mode. |
The build reports action cache hit instead of remote cache hit | Run bazel clean so Bazel has to fetch from the remote cache. |
The build fails with Could not find file with name 'bitrise-build-cache' on PATH | The CLI isn't installed, or isn't on your PATH. Install it, then run bitrise-build-cache auth login. |
| The build fails while fetching credentials, naming the credential helper | The CLI is installed but has no credentials. Run bitrise-build-cache auth login. |
doctor reports a problem it can't fix | Re-run it with --debug for the full context. |
| The build can't reach the cache, or the cache calls time out | Your VPN, firewall, or proxy may block the Bitrise hosts. Check them against Network endpoints for firewalls and VPNs. |