Remote Execution

Remote execution is a build mode where the build tool dispatches each action to a pool of worker machines instead of running it on the local machine.

Remote execution is a build mode where actions run on a pool of worker machines instead of the machine that started the build, coordinated by a build tool that decides what each action is. The build tool still reads the workspace and resolves the dependency graph locally, then ships individual compile, link, and test actions across the network and fetches back only the outputs it needs.

The word action is doing the work in that definition. A build tool that supports remote execution has already broken the build into discrete units with declared inputs, declared outputs, and a command line, and it is those units that travel.

Definition

Remote execution is defined by an open protocol rather than by any one build tool. The Remote Execution API, version 2, is published in the bazelbuild/remote-apis repository under the Apache License 2.0, and the repository describes its aim as enabling large scale parallel execution that would not be feasible on a single system while minimizing uploads by storing data in a content addressable format (read on 2026-08-13).

The protocol is four gRPC services. Names and methods below are from remote_execution.proto, read on 2026-08-13.

ServiceKey methodsWhat it carries
ContentAddressableStorageFindMissingBlobs, BatchUpdateBlobs, BatchReadBlobs, GetTreeEvery input file, command, and output blob, addressed by digest
ActionCacheGetActionResult, UpdateActionResultThe mapping from an action digest to the result of running it
ExecutionExecute, WaitExecutionThe request to run an action and the stream of progress back
CapabilitiesGetCapabilitiesWhat digest functions and API versions a server accepts

An Action message is small. It holds command_digest, input_root_digest, an optional timeout, a do_not_cache flag, a salt that separates cache entries into their own space, and a platform block of key and value properties. The command line and the entire input tree are referenced by digest rather than embedded, which is why an action can describe a build of a large tree in a few hundred bytes.

How one action travels

A client that wants an action executed hashes its inputs, calls FindMissingBlobs to learn which of those digests the server has never seen, uploads only those, and then calls Execute. The server responds with a stream of operations whose metadata reports one of four stages defined in the proto: CACHE_CHECK, QUEUED, EXECUTING, and COMPLETED. The result is an ActionResult listing output files by digest, exit code, and the digests of stdout and stderr.

Two consequences follow. The first is that identical work is deduplicated across every client pointed at the same server, because the action digest is the cache key. The second is that actions have to be reproducible for that sharing to be correct, so a rule that reads an undeclared file or stamps a timestamp into its output poisons results for everyone.

The platform properties are how an action reaches the right worker. They are opaque key and value pairs, commonly a container image reference, an operating system family, or a pool name, and the server routes the action to a worker that satisfies them. A build that needs two toolchains sends two sets of properties.

Where remote execution sits

Three build modes are often discussed together, and they move different things off the local machine.

ModeWhat runs the actionWhat crosses the networkWhat the local machine needs
Local executionThe local machineNothingCores and RAM for peak action concurrency
Remote cachingThe local machine on a missAction digests and cached resultsCores and RAM for every miss
Remote executionA worker in the poolInputs, action metadata, requested outputsBandwidth and open connections, little CPU

Parallelism is the practical difference. Local execution is bounded by the cores on the machine, while a remote execution client can hold hundreds of actions in flight against a worker pool, which is the reason build tools raise their concurrency setting well past the local core count when remote execution is turned on.

Support is per build tool. The Remote Execution API repository lists Bazel, Buck2, BuildStream, Justbuild, Pants, Please, Recc, Reclient, and Siso as clients that distribute build actions to workers through the API (read on 2026-08-13). A tool with no action graph has nothing to send.

Example

This GitHub Actions workflow runs a Bazel build on a hosted Linux runner and points the compile and test actions at a remote execution endpoint. Flag names are from Bazel's remote options, and the endpoint address, instance name, and platform properties come from whichever server is being used.

name: build
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build with remote execution
        run: |
          bazel build //... \
            --remote_executor=grpcs://remote.example.internal:443 \
            --remote_instance_name=default_instance \
            --remote_default_exec_properties=OSFamily=Linux \
            --remote_timeout=3600 \
            --jobs=200 \
            --remote_download_minimal

When that push lands, the runner checks out the repository, loads and analyzes the build graph, and computes a digest for every action in it. It then asks the server which input blobs are missing, uploads those, and issues up to 200 concurrent Execute calls. Each action compiles on a worker, and the resulting object files and archives stay in the content addressable storage rather than coming back, because --remote_download_minimal tells the build tool to materialize only the outputs it actually needs locally. A later action that consumes one of those object files references it by digest, and the worker that runs that action fetches it from the same storage.

Related flags in the same family are worth naming, since they change what the runner does. --remote_cache points at the cache alone with no execution service. --remote_download_outputs takes all, toplevel, or minimal and controls how much comes back. --remote_local_fallback runs an action locally when the remote call fails, which trades a slower build for a build that still completes. --remote_execution_priority orders requests inside a shared pool.

What the runner still does

The residual work on the machine that started the build is real and does not shrink as the worker pool grows: checkout, graph loading and analysis, input hashing, blob upload for anything the server has not seen, and the bookkeeping for hundreds of open gRPC streams. That work is dominated by single thread speed, disk throughput, and network round trips rather than by core count, so the machine profile that suits a remote execution client differs from the one that suits a local build of the same repository.

Distance matters for the same reason. Every cache check, upload, and result fetch is a round trip, so a client far from the endpoint spends wall clock time in latency that no worker count recovers.

FAQ

What is the difference between remote execution and remote caching?

A remote cache stores the results of actions that already ran somewhere and returns them when an action digest matches, so a miss still compiles on the machine running the build. Remote execution sends the action itself to a worker pool, so a miss compiles on a worker. Both are defined by the same API, and a build can use the cache alone, execution alone, or both with the cache checked first.

Which build tools support remote execution?

The Remote Execution API repository lists Bazel, Buck2, BuildStream, Justbuild, Pants, Please, Recc, Reclient, and Siso as clients that distribute builds to workers through it, read on 2026-08-13. Support is per tool: the tool has to model its work as discrete actions with declared inputs before it can send one anywhere.

What still runs on the local machine under remote execution?

Source checkout, loading and analysis of the build graph, hashing inputs to compute action digests, uploading blobs the server reports missing, holding many concurrent gRPC calls open, and downloading whatever outputs the build asked to materialize. That residual work is single threaded and network heavy rather than CPU heavy.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.