Self-hosted service
buildfarm/buildfarm avatar
buildfarm/buildfarm

Buildfarm: a self-hosted remote cache and execution service for Bazel

Bazel remote caching and execution service

778 stars243 forksJavaApache-2.0

At a glance

What is it?
Buildfarm implements the remote caching and remote execution APIs so that Bazel, buck2 and pants builds can run on a fleet of workers you control. It is a server plus worker deployment with Redis, Java logging and Kubernetes or Helm packaging, aimed at teams whose build time is worth paying for.
Who is it for?
Buildfarm suits a team already on Bazel whose build time dominates its feedback loop and which is willing to run Redis, a server, a worker fleet and its own container images, and it is the wrong choice for a single developer or a project that has not yet measured where its build minutes go.
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 1 day ago.
What is it written in?
Mainly Java, 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

What problem Buildfarm actually solves

Buildfarm hosts a remote caching and execution system, and it is compatible with the build systems Bazel, buck2, pants and more. The mechanism it implements is the remote APIs project, which defines the protocol for a client to hand work to a server, have that work executed somewhere else, and retrieve the artifacts.

The audience is a build infrastructure team, not an application developer. The pain it addresses is build latency: when every developer rebuilds the same targets on the same laptop, the same compilation happens many times a day, and the wall clock cost of that repetition is paid by everyone. Remote execution moves the work onto a shared fleet; remote caching means a target that was already built can be fetched as an artifact instead of rebuilt.

The project also states plainly that all command line options override corresponding config settings, which is a small but important design detail. Every tunable exists in the YAML configuration and can be overridden at launch, so an operator can keep a checked-in baseline and adjust behaviour per environment without forking the config.

The three processes: Redis, a server, and a shard worker

The deployment is three moving parts, and the documentation gives a run command for each.

Redis comes first, and it is launched as a container with the standard port mapping:

shell
$ docker run -d --rm --name buildfarm-redis -p 6379:6379 redis:7.2.4
redis-cli config set stop-writes-on-bgsave-error no

That second line is not incidental. Turning off stop-writes-on-bgsave-error tells Redis to keep accepting writes when a background save fails, which is what a build cache wants, since a failed background save should not turn into a service outage for in-flight builds.

The server and the worker are both run through bazelisk, and both take a log file and a config file as positional arguments:

shell
$ bazelisk run //src/main/java/build/buildfarm:buildfarm-server -- <logfile> <configfile>

Ex: bazelisk run //src/main/java/build/buildfarm:buildfarm-server -- --jvm_flag=-Djava.util.logging.config.file=$PWD/examples/logging.properties $PWD/examples/config.minimal.yml

The worker target is named buildfarm-shard-worker, and the shard in the name is the important word. Execution is sharded, so a deployment is one server and one or more workers, each taking a slice of the work rather than every worker competing for every action.

The logfile argument is a java.util.logging config, not a log path

The argument convention in those run commands trips people up, so it is worth spelling out. The documentation says the logfile has to be in the standard java util logging format and be passed as a --jvm_flag=-Dlogging.config=file:, and that the configfile has to be in YAML format.

So the first positional argument is not a filename the service writes to. It is a java.util.logging configuration, passed into the JVM through a flag, and the example uses the -Djava.util.logging.config.file variant pointing at examples/logging.properties. The second positional argument is the service configuration in YAML, and the minimal one lives at examples/config.minimal.yml.

The examples directory makes the shape of a configuration visible: there are separate files for a backplane, for queues, for the server, for the worker, and a fuller config.yml alongside the minimal one. Buildfarm uses Java's Logging framework and writes routine behaviour at the NICE Level, meaning you can filter what you see using ordinary java.util.logging configuration and watch the flow of executions through the running services.

For debugging there is also a remote debugger flag, and the example shows the server started on a port:

shell
$ bazel run //src/main/java/build/buildfarm:buildfarm-server -- --debug=5005 $PWD/examples/config.minimal.yml

Pointing Bazel at the service through .bazelrc

The client side is short. For Bazel version 1.0 or higher, the documentation says to configure .bazelrc with a remote_executor flag pointing at the server:

shell
$ cat .bazelrc
$ build --remote_executor=grpc://localhost:8980

After that, the build runs as you would normally do. There is no separate command and no wrapper binary, which is the practical appeal of the remote execution model: the client keeps using the build system it already has, and the work moves.

Two things the documentation leaves open are worth noticing. The example only shows remote_executor, and the flag set for pointing a client at a remote cache rather than a remote executor is not shown here, so a setup that only wants caching and not execution needs the configuration reference on the documentation site. The port 8980 in the example is also just the example's choice, and the README does not say which port the server binds by default, so do not treat it as a default value.

Because the flag is a plain gRPC endpoint, the same server can serve several clients, and the same client can be pointed at a different deployment by changing one line in .bazelrc.

Shipping Buildfarm as an external Bazel module and OCI image

Buildfarm is meant to be composed into a deployment of your own rather than only run from the repository. The documentation shows adding it to MODULE.bazel through a git_override pinned to a commit, together with the transitive dependencies it needs:

starlark
bazel_dep(name = "build_buildfarm")
git_override(
    module_name = "build_buildfarm",
    commit = "<COMMIT-SHA>",
    remote = "https://github.com/buildfarm/buildfarm.git",
)

The block also pins remoteapis with an archive_override and an integrity hash, with a TODO noting it should be removed once the referenced pull request in remote-apis is merged. That TODO is a useful signal about the maintenance state of this integration: the pins are not entirely under the project's own control, and an adopter inherits them.

From there, the layer targets are used to build your own OCI image, with the deploy jar as the entrypoint:

starlark
oci_image(
    name = "mycompany-buildfarm-server",
    base = "@<YOUPROVIDE>",
    entrypoint = [
        "/usr/bin/java",
        "-jar",
        "/app/build_buildfarm/buildfarm-server_deploy.jar",
    ],
    tars = [
        "@build_buildfarm//:layer_buildfarm_server",
    ],
)

The base is a placeholder for an oci.pull you define in your own MODULE.bazel, which is how you choose a Java runtime image to build on.

The Helm chart is versioned separately from the server releases

There is an OCI bundled Helm chart published to ghcr.io, and the install command is a standard helm install with a namespace created on the fly:

bash
helm install \
  -n buildfarm \
  --create-namespace \
  buildfarm \
  oci://ghcr.io/buildfarm/buildfarm \
  --version "0.5.0"

Note the version number, 0.5.0, and note that the server releases are on a different track, with v2.17.1 published on 2026-07-03, v2.17.0 on 2026-05-12 and v2.16.0 on 2026-02-14. The chart carries its own version, so pinning the chart does not pin the server image it deploys unless the chart's values say otherwise, and the README does not show the values file that would let you set the image tag.

The repository also carries a Dockerfile that describes a minimal build and run container, based on ubuntu:24.04 with a pinned digest and with wget, git, zip, python3-pip, python3-dateutil, gcc, default-jdk-headless, g++, redis and redis-server installed. That image is described in the file as a container for building and running buildfarm services, used to test against pull request changes. It is a development and test image, not a production image, and the distinction matters when you decide what to run in production.

Where the operational burden lands on the team that adopts it

Buildfarm is infrastructure with a licence to be complicated, and the honest limits are visible in the layout rather than stated in the README. The tree contains kubernetes/, a Tiltfile, a bazel.Tiltfile, a config/ directory, a persistentworkers/ directory, contrib/ and third_party/, plus scripts such as as-nobody.c and as-nobody-windows.c, macos-wrapper.sh, a cgexec-wrapper and generate_coverage.sh.

Those files describe the parts of running a build farm that are not the server itself: running actions as an unprivileged user, controlling cgroups for resource limits, wrapping macOS builds, and driving a development cluster with Tilt. The examples include a development-redis-cluster.sh, which suggests running Redis in a cluster is a supported configuration rather than a hypothetical one.

What that means in practice is that Buildfarm gives you a protocol implementation and a reference deployment, and leaves the operational part to you: worker image management, per-action sandboxing, garbage collection of cached artifacts, and the capacity planning that keeps a burst of CI jobs from starving interactive developers. The README does not document cache eviction policy, authentication on the gRPC endpoint, or multi-tenant isolation between teams sharing one deployment, so treat those as open questions to answer in the documentation site and in the configuration reference rather than as features you can assume.

Editorial conclusion

Buildfarm suits a team already on Bazel whose build time dominates its feedback loop and which is willing to run Redis, a server, a worker fleet and its own container images, and it is the wrong choice for a single developer or a project that has not yet measured where its build minutes go. Before deploying, confirm that your build system speaks the remote execution API, read the configuration reference for the YAML files under examples/, and check the version pin of the Helm chart you install, since the chart is versioned separately from the release tags of the server.

Frequently asked questions

What is Buildfarm and which build systems does it work with?

Buildfarm is a remote caching and execution service, compatible with Bazel, buck2, pants and more. It implements the remote APIs protocol so a build client can send work to a server and have it executed on a separate worker fleet.

How do I run Buildfarm locally for the first time?

Start Redis with docker run -d --rm --name buildfarm-redis -p 6379:6379 redis:7.2.4, then run the server and the worker with bazelisk, each taking a java.util.logging configuration and a YAML config file, for example examples/logging.properties and examples/config.minimal.yml.

How do I make Bazel use the Buildfarm server?

For Bazel 1.0 or higher, set the remote_executor flag in .bazelrc, for example build --remote_executor=grpc://localhost:8980, and then run the build as usual. The README shows only the executor flag, so a cache-only setup needs the configuration reference.

What does the buildfarm-shard-worker do and how many do I need?

The worker target is //src/main/java/build/buildfarm:buildfarm-shard-worker and it is launched with the same logfile and configfile arguments as the server. Execution is sharded, so a deployment runs one server and one or more workers that each take a share of the work.

How do I install Buildfarm with Helm?

The chart is published as an OCI artifact at oci://ghcr.io/buildfarm/buildfarm and installed with helm install, creating the buildfarm namespace, with a version flag such as --version "0.5.0". The chart version is separate from the server release tags.

Official sources

  1. buildfarm/buildfarm 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/buildfarm-buildfarm.svg)](https://hysenlabs.com/projects/buildfarm-buildfarm)

Was this useful?