Self-hosted service
buchgr/bazel-remote avatar
buchgr/bazel-remote

bazel-remote serves a build cache over HTTP and gRPC with disk eviction

A remote cache for Bazel

786 stars212 forksGoApache-2.0

At a glance

What is it?
bazel-remote is an HTTP/1.1 and gRPC server that acts as a remote build cache for REAPI clients such as Bazel, storing content addressed blobs and action results on disk with least recently used eviction, and optionally proxying to S3, GCS or Azure blob storage. It suits CI teams whose build time is dominated by repeated compilation.
Who is it for?
Adopt bazel-remote when your CI rebuilds the same artefacts repeatedly and the compile time dominates the pipeline, since it speaks both the HTTP REST interface and the gRPC REAPI services and stores content on disk with automatic eviction. Do not adopt it as a general remote execution service, because the repository describes itself as a cache, usable as a component of one, and it does not schedule actions.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A cache first, an execution service second

The scope is stated in the first paragraph. bazel-remote is an HTTP/1.1 and gRPC server intended to be used as a remote build cache for REAPI clients like Bazel, or as a component of a remote execution service. That distinction is the first thing to hold on to, because a cache and an execution service have different failure costs. A cache miss means a build runs again. A cache that serves the wrong bytes means a build produces the wrong result, and the protocol is designed to prevent that with content addressing. The project reports a long track record, with the README stating that bazel-remote has been serving TBs of cache artefacts per day since April 2018, both on commodity hardware and on AWS servers, and that outgoing bandwidth can exceed 15 Gbit/s on the right instance type.

Two kinds of key on the REST interface

The HTTP interface is small and worth reading in full, because it is also the easiest way to test a deployment. Cache entries are set and retrieved by key, and there are two kinds of key. The first is content addressed storage, where the key is the lowercase SHA256 hash of the entry, served at `/cas/<key>` or with an optional instance name at `/<instance>/cas/<key>`. The second is the action cache, where the key is an arbitrary 64 character lowercase hexadecimal string, and Bazel uses the SHA256 hash of an action as that key to store the metadata the action created, served at `/ac/<key>` or `/<instance>/ac/<key>`. Values are stored with HTTP PUT and retrieved with GET, and HEAD can be used to confirm whether a key exists.

Compression is negotiated, and the digest header matters

The compression story has two halves and both have sharp edges. On the HTTP side, if a GET request specifies `zstd` in the `Accept-Encoding` header, then zstandard-encoded data may be returned. On the upload side, PUT requests must set `Content-Encoding: zstd` and include a custom `X-Digest-SizeBytes` header with the size of the uncompressed entry, and the key must also refer to the uncompressed entry. That last rule is the one that bites, since the key is a hash of the uncompressed content and uploading a compressed body with a mismatched key will not do what you expect. There is a parallel mechanism for gRPC clients, where the Byte Stream compressed-blobs REAPI feature lets clients upload and download CAS blobs compressed with zstandard to improve network efficiency, and uploaded CAS blobs are stored compressed by default, which can increase the effective cache size and reduce load on the server if clients also download compressed blobs.

Instance names, key mangling and a path cleaning caveat

Both key types accept an optional instance name in the path, which is how one server can host several logically separate caches. There is a flag for it, `--enable_ac_key_instance_mangling`, and when it is specified and the instance name is not empty, action cache keys are hashed along with the instance name to produce the lookup key. The caveat is about how the instance name is extracted. Since the URL path is processed with Go's `path.Clean` function before the instance name is taken, clients should avoid repeated slashes, `./` and `../` in the URL. The same advice is repeated on the gRPC side, where clients using the mangling feature are advised to avoid those same strings in the instance name for consistency with the HTTP interface. It is a small detail that turns into a confusing 404 if you ignore it.

Disk with LRU eviction, and object storage behind it

Storage is where the operational decisions live. The cache contents are stored in a directory on disk with a maximum cache size, and bazel-remote automatically enforces that limit as needed by deleting the least recently used files. The `--dir` flag, which gives the directory path where the cache contents are stored, is the one required flag. Beyond local disk there are proxy backends, with S3, GCS and experimental Azure blob storage supported, so a large cache can sit in object storage without changing the protocol. The proxy mode also comes with a compression decision attached, since the zstd storage mode is described as increasing the effective cache size and reducing server load when clients also download compressed blobs, and `--storage_mode uncompressed` is the escape hatch if you would rather store CAS blobs uncompressed.

gRPC services, an experimental asset API and the status endpoint

Over gRPC, bazel-remote supports the ActionCache, ContentAddressableStorage and Capabilities services from the Bazel Remote Execution API v2, plus the corresponding parts of the Byte Stream API. There is also very experimental support for a subset of the Fetch service in the Remote Asset API, enabled with the `--experimental_remote_asset_api` flag, and to use it with Bazel you would set the remote downloader to a gRPC URL pointing at your host. For operating the server, the HTTP side has a `/status` endpoint that returns the cache status and info, including the current size, the reserved size, the maximum size, the number of files, the server time, the Git commit and tags, and the number of goroutines. The empty CAS blob is always available, even if the cache is empty, and the README suggests it as the way to test that an instance is running and accepting requests:

bash
$ curl --head --fail http://localhost:8080/cas/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
HTTP/1.1 200 OK
Content-Length: 0

Prometheus metrics are exposed for both protocols, and the README points at query examples for the HTTP metrics and at the gRPC Prometheus documentation for the other side.

Configuration precedence and the module caveat

Configuration is simple to reason about. If a YAML configuration file is specified by the `--config_file` flag or the `BAZEL_REMOTE_CONFIG_FILE` environment variable, then all other command line flags and environment variables are ignored, and otherwise the flags and environment variables in the help text apply, with flags overriding the corresponding environment variables. The repository ships a systemd unit example, a docker-compose example and a Kubernetes example under `examples/`. The caveat is the one in the README about use as a Go module: the module is `github.com/buchgr/bazel-remote/v2`, it is consumable as a Go module, but no guarantees are made about the stability or backwards compatibility of those APIs, while the standalone executable is kept backwards compatible between releases and cache directory format changes are only allowed in major version upgrades. The release history reflects that discipline, with v2.6.1 in September 2025, v2.6.2 in July 2026 and v2.6.3 on 2026-10-12, followed by a more recent push in October 2026.

Editorial conclusion

Adopt bazel-remote when your CI rebuilds the same artefacts repeatedly and the compile time dominates the pipeline, since it speaks both the HTTP REST interface and the gRPC REAPI services and stores content on disk with automatic eviction. Do not adopt it as a general remote execution service, because the repository describes itself as a cache, usable as a component of one, and it does not schedule actions. Verify first which storage mode you want, since CAS blobs are stored zstd compressed by default and `--storage_mode uncompressed` changes that, and read the compatibility note before embedding it, because the Go module API carries no stability guarantee even though the standalone executable stays backwards compatible between releases.

Frequently asked questions

What is bazel-remote and what does it store?

It is an HTTP/1.1 and gRPC server used as a remote build cache for REAPI clients such as Bazel, storing content addressed storage entries keyed by the lowercase SHA256 hash of the entry, and action cache entries keyed by a 64 character lowercase hexadecimal string.

How do I upload zstd compressed blobs to bazel-remote over HTTP?

The PUT request must set `Content-Encoding: zstd` and include a custom `X-Digest-SizeBytes` header with the size of the uncompressed entry, and the key must refer to the uncompressed entry as well.

Does bazel-remote limit the size of its cache?

Yes. The cache contents are stored in a directory on disk with a maximum cache size, and the server enforces the limit automatically by deleting the least recently used files. The `--dir` flag, giving the directory where contents are stored, is required.

Can I use bazel-remote as a Go module?

Yes, it is consumable as a Go module at `github.com/buchgr/bazel-remote/v2`, but the README states that no guarantees are made about the stability or backwards compatibility of the module APIs, while the standalone executable is kept backwards compatible between releases.

Official sources

  1. buchgr/bazel-remote on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/buchgr-bazel-remote.svg)](https://hysenlabs.com/projects/buchgr-bazel-remote)

Was this useful?