Gradle and Bazel Remote Build Cache
A CloudRepo Maven repository can be the remote build cache for Gradle (HttpBuildCache) and for Bazel
(--remote_cache). Both clients store a cache entry with an HTTP PUT and read it back with a GET, and
the repository returns each entry byte for byte. There is no separate repository type for this: you create
one repository for the cache, change one setting on it, and point your builds at it.
The settings below were measured with Gradle 9.7.1 and Bazel 8.4.2.
Create a repository for the cache
Section titled “Create a repository for the cache”- Create a Maven repository for the cache only, such as
gradle-cacheorbazel-cache. See Create Your First Repository. Keep it apart from the repositories that hold your releases. - Turn Overwrite Protection off on that repository, from the Overwrite Protection card in its settings. See Overwrite Protection.
Why Overwrite Protection must be off
Section titled “Why Overwrite Protection must be off”A new repository has Overwrite Protection on, so it refuses a second upload of a key it already holds with
409 Conflict and the message Version already exists. Overwrites are disabled for this repository. A
build cache sends such uploads routinely: a build that runs its tasks again (Gradle’s --rerun-tasks), two
jobs that miss the same key at the same time, or two Bazel actions whose outputs are the same bytes.
- Gradle logs
Could not store entry, thenThe remote build cache was disabled during the build due to errors., and stops using the remote cache for the rest of that build. - Bazel logs a warning and does not store that action’s result, so the action misses in every later build.
Both builds still succeed, so the log is the only place this shows. With Overwrite Protection off, the repository accepts the repeated upload and both clients keep their cache.
Create the tokens
Section titled “Create the tokens”Authenticate with repository tokens, not your password. See Repository Tokens: Create One and Authenticate for how to create one, and which account to create it as.
- For CI, a Read + write token that reaches only the cache repository. CI fills the cache.
- For developer machines, a Read only token for the same repository. It downloads cache entries;
an upload with it is refused with
403 Forbidden. Turn uploads off on developer machines too (shown for each client below), so their builds only read.
The username is the email address of the account that created the token, and the password is the token.
The examples read both from two environment variables, CLOUDREPO_USERNAME and CLOUDREPO_TOKEN. Never
commit a real token to version control.
Gradle
Section titled “Gradle”Add the remote cache to settings.gradle or settings.gradle.kts:
buildCache { remote(HttpBuildCache) { url = 'https://your-org.mycloudrepo.io/repositories/your-repo/' push = System.getenv('CI') != null credentials { username = System.getenv('CLOUDREPO_USERNAME') password = System.getenv('CLOUDREPO_TOKEN') } }}buildCache { remote<HttpBuildCache> { url = uri("https://your-org.mycloudrepo.io/repositories/your-repo/") isPush = System.getenv("CI") != null credentials { username = System.getenv("CLOUDREPO_USERNAME") password = System.getenv("CLOUDREPO_TOKEN") } }}Then turn the build cache on, with org.gradle.caching=true in the project’s gradle.properties or
--build-cache on the command line.
push is on only where the CI environment variable is set. GitHub Actions, GitLab CI/CD and CircleCI
set CI=true in their jobs; on another CI service, set it in the job. On a developer machine, where CI
is not set, Gradle reads from the cache and uploads nothing.
Add two lines to the workspace’s .bazelrc:
build --remote_cache=https://your-org.mycloudrepo.io/repositories/your-repobuild --credential_helper=your-org.mycloudrepo.io=%workspace%/tools/cloudrepo-credential-helper.shBazel asks the credential helper for the header to send. Save this one as
tools/cloudrepo-credential-helper.sh in the workspace and make it executable:
#!/bin/sh# Bazel credential helper for CloudRepo. Bazel writes a request on stdin; this answers with a# Basic Authorization header built from two environment variables.cat > /dev/nullprintf '{"headers":{"Authorization":["Basic %s"]}}\n' \ "$(printf '%s:%s' "$CLOUDREPO_USERNAME" "$CLOUDREPO_TOKEN" | base64 | tr -d '\n')"chmod +x tools/cloudrepo-credential-helper.shOn developer machines, add this line to your own ~/.bazelrc so Bazel reads from the cache and uploads
nothing:
build --remote_upload_local_results=falseWhat the repository holds
Section titled “What the repository holds”Cache entries are ordinary files in the repository. Gradle stores each entry at the repository root, named
by a 32 character key. Bazel stores its entries under ac/ and cas/, named by a SHA-256 digest. A key
nobody stored answers 404 Not Found, and both clients read that as a cache miss.
To empty the cache, delete its files in the portal. See Deleting Files and Folders.
Events
Section titled “Events”Each cache upload is a file upload and each cache hit a file download. Each one fires the repository’s File Upload or File Download webhooks, if it has any. See Webhooks. A build can store and read many entries, so leave webhooks off the cache repository unless you want one event for each.
When it does not work
Section titled “When it does not work”| What you see | Why | What to do |
|---|---|---|
Gradle: Could not store entry, then The remote build cache was disabled during the build due to errors. |
Overwrite Protection is on: the repository refused a repeated upload with 409. |
Turn Overwrite Protection off on the cache repository. |
| Bazel: a warning about a refused upload, and the same action misses in later builds | The same 409. |
The same. |
403 Forbidden on an upload |
The token is Read only, or it does not reach this repository. | On developer machines, turn uploads off as shown above. In CI, use a Read + write token that includes the cache repository. |
401 Unauthorized |
No credential, or one CloudRepo refused: a mistyped, expired or revoked token, or the wrong username. | Check the token and the username as in When a publish is refused. |
Next steps
Section titled “Next steps”- CI/CD overview: hold the token as a CI secret and read it from the environment.
- Repository Tokens: Create One and Authenticate.
- Repository Management: Overwrite Protection and the Trash.