Use a standard Bazel remote cache server as the backend for Gradle's build cache, so one cache serves both your Bazel and your Gradle builds.
Gradle's built-in HttpBuildCache cannot talk to a Bazel remote cache, even though
both are content-addressed HTTP blob stores. Gradle PUTs to <url>/<key> where the key
is a 32-character hash; bazel-remote routes on
^/?(.*/)?(ac/|cas/)([a-f0-9]{64})$ — 64 lowercase hex, SHA-256 only. The request is
rejected before it reaches storage.
There is a deeper mismatch behind the key width. Bazel's cache is not a key-value store:
the Action Cache holds ActionResult protos that point at blobs in the
Content Addressable Store, and a CAS key must be the SHA-256 of the blob's own
content. At load time Gradle hands you a cache key but not the content, so the CAS
address cannot be recomputed. Bridging the two requires an AC indirection.
This plugin implements that bridge against the unmodified REAPI protocol — no server flags, no proprietary extensions — over Bazel's HTTP cache protocol or over gRPC.
The approach was presented in "Making a faster Gradle build cache with Bazel's Remote
APIs" (Zach Gray, BazelCon). In that talk Bitrise describes building exactly this, then
says they "considered open sourcing this project but … held off", and instead built a
proprietary gRPC protocol for their own servers. Their plugin is a closed-source jar that
cannot talk to bazel-remote. EngFlow's equivalent is likewise closed and vendor-locked.
No open-source Gradle build-cache backend speaks REAPI. This project is that missing piece.
For a working reference in another ecosystem, see Mill's BazelRemoteCache.scala.
cd examples/quickstart
podman compose up -d # or docker compose up -d
../../gradlew :app:compileJava --build-cache # cold: stores
rm -rf app/build .gradle
../../gradlew :app:compileJava --build-cache # warm: FROM-CACHESee examples/quickstart.
// settings.gradle.kts — pluginManagement must be the first block
pluginManagement { repositories { gradlePluginPortal() } }
plugins { id("io.github.pplr.bazel-cache") version "1.0.0" }
buildCache {
remote(BazelRemoteBuildCache::class) {
endpoint = "https://cache.internal:8080"
isPush = System.getenv("CI") != null // Gradle defaults remote push to false
}
}endpoint is spelled as Bazel's --remote_cache, and the scheme picks the transport:
http:// / https:// for the HTTP cache protocol, grpc:// / grpcs:// for the
Remote Execution API over gRPC (plaintext / TLS). As in Bazel, an endpoint with no
scheme is gRPC over TLS.
endpoint = "grpcs://cache.internal:1985"
instanceName = "team-a" // optionalinstanceName has the same meaning as Bazel's --remote_instance_name: sent over
gRPC and — like Bazel — no effect over HTTP. For an HTTP path prefix, put it in the
endpoint, as you would in Bazel's --remote_cache:
endpoint = "https://cache.example.com/team-a/".
Credentials are referenced by environment variable name, never by value, so no secret is written into Gradle's configuration cache entry on disk:
tokenEnvironmentVariable = "BAZEL_CACHE_TOKEN" // defaultThe token is sent as Authorization: Bearer … — an HTTP header, or gRPC metadata.
| Server | HTTP | gRPC |
|---|---|---|
bazel-remote |
✅ verified against v2.6.2 | ✅ verified against v2.6.2 |
| nginx + WebDAV, S3 / GCS / MinIO, Artifactory | expected, untested | — |
| Buildbarn, BuildBuddy, NativeLink, EngFlow | — (gRPC-only) | expected, untested |
Verified means the integration suite runs against a real server with AC validation enabled — its default. No server flags are required. Details in docs/SERVER-MATRIX.md.
- 1.0 — HTTP
/ac//cas/, resilience layer, quickstart, Plugin Portal + Maven Central - 1.1 — gRPC REAPI (
grpc-okhttp),FindMissingBlobs - Next — Buildbarn / BuildBuddy interop in CI, custom CA and client certificates
./gradlew buildRequires a JDK 17 or newer. No container needed: the tests that need a real
cache server are tagged out of build. See CONTRIBUTING.md
for the other test layers.
| Gradle | 8.0 and newer — each version in the matrix is exercised in CI |
| Java | 17 and newer |
| Configuration cache | supported |
Published to the Gradle Plugin Portal, which
plugins { } resolves from by default — no extra repository configuration.
Maven Central is an optional mirror; see docs/RELEASING.md.
- How entries are stored — the Action Cache indirection, the key derivation, and why it is frozen
- Server compatibility
- Troubleshooting
- Releasing
MIT — see LICENSE. Vendored .proto files and the bundled (relocated)
gRPC, Guava, gson, okio and perfmark libraries are Apache-2.0, protobuf is BSD-3-Clause;
see NOTICE.