Compared

Architectural comparison: what each design does with the same work.

Each claim rests on GF's architecture principles. Quotations come from competitor documentation read on the date shown.

GitHub Actions

GitHub-hosted runners, larger runners, Actions Runner Controller, the job queue and runner assignment

Documentation read

  1. GF schedules remote actions on a shared worker pool; CI runners coordinate admission rather than supply the execution capacity. Worker capacity and queue behavior determine action throughput, not runner count. On GitHub Actions a runner runs one job at a time, ARC scales runner pods one per assigned job up to an operator-typed maxRunners, and GitHub-hosted concurrency is a plan table (Free 20 through Enterprise 500; larger runners 1000). A CTO on GF sizes pooled worker capacity per capability class plus a thin ARC admission floor that the architecture calls maintenance, never the product; a CTO on GitHub Actions sizes job concurrency, because the job is the unit.

    • Each runner can run a single job at a time.

      GitHub Docs, Understanding GitHub Actions

    • The target number of runners created will be calculated as a sum of minRunners and the number of jobs assigned to the scale set.

      GitHub Docs, Deploying runner scale sets with Actions Runner Controller

    • maxRunners is the max number of runners the auto scaling runner set will scale up to.

      GitHub Docs, Deploying runner scale sets with Actions Runner Controller

    • GitHub Support can increase job concurrency limits for GitHub Actions.

      GitHub Docs, Actions limits

  2. GF's reuse model shares available verified results with authorized callers whose tenant, digest, platform and policy identity match. Developer and CI credentials remain distinct; shared storage never grants visibility by itself. The GitHub Actions cache is a file-tree dependency store keyed by an author-written string, scoped to the repository (10 GB default, 7-day idle eviction) and readable by a workflow run only from its own branch, the default branch, or a PR's base branch, never from sibling or child branches; no read path outside a workflow run is documented (the cache REST API offers usage, list, limits and delete only).

    • Use caching when you want to reuse files that don't change often between workflow runs, such as dependencies downloaded by a package management system

      GitHub Docs, Dependency caching

    • Workflow runs can restore caches created in either the current branch or the default branch (usually main).

      GitHub Docs, Dependency caching reference

    • Workflow runs cannot restore caches created for child branches or sibling branches.

      GitHub Docs, Dependency caching reference

    • By default, the limit is 10 GB per repository

      GitHub Docs, Dependency caching reference

    • GitHub will remove any cache entries that have not been accessed in over 7 days.

      GitHub Docs, Dependency caching reference

  3. Developers and CI use the same authorized remote-result pool. Matching work reuses available results. On GitHub Actions the system is workflows triggered by repository events running on runners; each workflow run on GitHub-hosted runners executes in a fresh, newly-provisioned virtual machine, ARC runners are ephemeral, caches are scoped to the branch of a workflow run, and no developer-machine caller of the cache or of execution is documented.

    • A runner is a server that runs your workflows when they're triggered.

      GitHub Docs, Understanding GitHub Actions

    • Each workflow run executes in a fresh, newly-provisioned virtual machine.

      GitHub Docs, Understanding GitHub Actions

    • A workflow is a configurable automated process that will run one or more jobs.

      GitHub Docs, Workflows

    • Caches are shared based on the branch or tag a workflow run uses, not on the identity of the workflow or job.

      GitHub Docs, Dependency caching

  4. GF requires validated remote results to be published before reporting qualifying success. Nix results additionally require recursive publication and independent substitution; a cache lookup alone does not satisfy that obligation. On GitHub Actions the cache is an action a workflow author adds with a required, author-written key and optional restore-keys, artifacts are a further opt-in, and no documented rule ties a job's success to publishing anything.

    • key: Required The key created when saving a cache and the key used to search for a cache.

      GitHub Docs, Dependency caching reference

    • restore-keys: Optional A string containing alternative restore keys

      GitHub Docs, Dependency caching reference

    • Use artifacts when you want to save files produced by a job to use or view after a workflow run has ended

      GitHub Docs, Dependency caching

  5. GF requires capacity claims to be backed by measurements tied to the worker and its resource envelope. A configured slot count is not measured throughput. GitHub Actions' ceilings are a plan table for hosted runners, an operator-typed 'Maximum concurrency' for larger runners, and an operator-typed maxRunners for ARC; ARC's target is computed from live assigned-job count, but its ceiling and its per-runner capacity of one job are declared, not measured.

    • GitHub Support can increase job concurrency limits for GitHub Actions.

      GitHub Docs, Actions limits

    • In the "Capacity" section, under "Maximum concurrency", enter the maximum number of jobs you would like to allow to run at the same time.

      GitHub Docs, Managing larger runners

    • maxRunners is the max number of runners the auto scaling runner set will scale up to.

      GitHub Docs, Deploying runner scale sets with Actions Runner Controller

In fairness

GitHub Actions and GF serve different roles. GitHub provides workflow orchestration and runner management; GF submits remote work through a thin, dispatch-only GitHub edge; GF's scheduler places it. GF does not replace GitHub's event model, workflow syntax, marketplace actions, or hosted operating-system images. Shared reuse also introduces a trust obligation: every read and write must remain authorized under tenant policy.

Blacksmith

faster runners for GitHub Actions

Documentation read

  1. A CTO adopting Blacksmith changes the CI job's host (a single workflow line) and, for developers and coding agents, rents a second CI-identical microVM per agent that mirrors the working tree over rsync; the developer's own machine never attaches to the cache, and reuse is bounded by the repository (and, for Bazel, by CPU architecture). GF gives developers and CI distinct authenticated access to the adopter's shared execution and cache policy. Available verified results can be reused by authorized callers with matching work. Missing authority or inputs stops the action; neither trusted nor untrusted callers gain a local fallback.

  2. Blacksmith's unit of scheduled compute is the job VM, so parallelism is bounded by the vCPUs of the VM a job rents; GF's unit is the REAPI action, which GF's scheduler admits and leases to a pooled worker slot, so one thin runner fans out ready actions within bounded slots and tenant quotas.

  3. Blacksmith's fixed reuse boundary is the repository across all four of its caches (Bazel, actions, Docker layers, sticky disks), with the Bazel cache further separated by CPU architecture; branch scoping on the actions cache and sticky disks is a default an admin can toggle. GF's reuse boundary is tenant, digest, platform and policy identity: a result deposited once by a trusted lane is withdrawn by every authorized caller in the tenant that shares that identity, across repositories (withdrawal is admissible with no repository binding), while deposit rights stay bound to a repository and a trusted identity and physical deduplication never grants logical visibility.

  4. On a Bazel cache miss Blacksmith executes the action inside the requesting job's VM (its docs gloss '4 linux-sandbox' as 'ran four actions locally') and saves the result to that repository's cache for later jobs in the same repository. GF requires a cache miss to execute on compatible authorized remote capacity or fail explicitly. A backend outage is not a cache miss. Local developer deposits are not qualifying authority. Nix derivations use the same remote fabric with their own result rules: recursive publication and independent substitution are required after execution or a cache hit.

  5. Blacksmith's documented path for a developer or coding agent to reach CI-grade execution is a Testbox: a per-agent Blacksmith microVM warmed as a real GitHub Actions job, mirrored from the working tree with rsync, billed per minute at the same runner rate as CI, with the generated agent skill instructing 'Never run tests locally'. GF separates developer identity from Actions identity and applies the adopter's shared policy. Authorized cache reuse avoids repeating remote work when matching results remain available.

In fairness

This comparison contrasts Blacksmith's documented runner-based product with GF's shared-action design. Repository and branch scoping are deliberate security choices, not defects. GF's broader reuse boundary must preserve tenant isolation and trusted publication. Adoption is also a different commitment: GF requires an adopter-owned GitHub App, signed organization policy, and a checked-in action plan, rather than a runner-label change. A job script is not a remote action until an ActionPlan names it.

Buck2

Meta's large-scale build tool, including its remote-execution client

Documentation read

  1. GF's execution contract forbids local fallback for remote-required work and distinguishes backend unavailability from a cache miss. Under Buck2 whether an action may run locally, remotely, or race both, and whether a remote infrastructure failure falls back to local, are execution-platform rule fields a project's rule authors choose (local_enabled, remote_enabled, allow_limited_hybrid_fallbacks, allow_hybrid_fallbacks_on_failure on the CommandExecutorConfig page; 'Allows Buck2 to race local and remote execution and get whichever finishes first' in the Glossary of Terms), so the same action can legitimately complete locally in one invocation and remotely in the next, and, when fallbacks are enabled, a remote outage is absorbed as local execution rather than surfaced as a typed failure. Buck2 does this well and on purpose ('remote execution first' with local as 'a special case', on the Why Buck2 page); the paradigm difference is that GF has no vocabulary in which a local alternative for remote-required work is admissible.

    • local_enabled: Whether to use local execution for this execution platform. If both remote_enabled and local_enabled are True, we will use the hybrid executor

      Buck2 docs, CommandExecutorConfig

    • allow_limited_hybrid_fallbacks: Whether to allow fallbacks

      Buck2 docs, CommandExecutorConfig

    • allow_hybrid_fallbacks_on_failure: Whether to allow fallbacks when the result is failure (i.e. the command failed on the primary, but the infra worked)

      Buck2 docs, CommandExecutorConfig

    • Execution platform: A type of rule that includes information such as what execution types a target supports, which can be remote, local, and hybrid execution.

      Buck2 docs, Glossary of Terms(opens in a new tab)

    • Buck2 is remote execution first - local execution is considered a special case of remote execution

      Buck2 docs, Why Buck2(opens in a new tab)

  2. GF shares validated remote results between authorized developer and CI callers; local execution is not a qualifying source. Buck2 documents the same developer-to-CI sharing when actions execute remotely ('everyone writes to the cache through sandboxed remote execution' on the Benefits When Compared to Buck1 page; 'caching action outputs across Buck2 users' in the Glossary of Terms), and that is to its credit. The distinction is the no-fallback execution policy, not an exclusive claim to shared remote caching.

  3. GF requires publication before qualifying success; for Nix, recursive publication and independent substitution are separate obligations. Under Buck2, remotely executed actions are cached by the remote execution service ('everyone writes to the cache through sandboxed remote execution' on the Benefits When Compared to Buck1 page), while results of locally executed actions reach the shared cache only when the project's execution platform opts in with allow_cache_uploads, subject to max_cache_upload_mebibytes (CommandExecutorConfig). GF refuses the local path as a qualifying origin at all. Whether a Buck2 build's success waits on cache upload is not established by its documentation.

    • allow_cache_uploads: Whether to upload local actions to the RE cache

      Buck2 docs, CommandExecutorConfig

    • max_cache_upload_mebibytes: Maximum size to upload in cache uploads

      Buck2 docs, CommandExecutorConfig

    • remote_cache_enabled: Whether to query RE caches

      Buck2 docs, CommandExecutorConfig

    • with Buck2 everyone writes to the cache through sandboxed remote execution

      Buck2 docs, Benefits When Compared to Buck1(opens in a new tab)

  4. GF's scheduler places remote work on the pooled worker fleet; CI runners dispatch and never execute the actions. Worker capacity and queue latency are measured, not declared. Buck2 consumes remote execution from a separately operated REAPI service named in the project's .buckconfig (engine_address under [buck2_re_client]; EngFlow, BuildBarn and BuildBuddy 'successfully tested', on the Remote Execution page), describes remote execution as 'scaling the nodes available for parallel actions, and by caching action outputs across Buck2 users' (Glossary of Terms), and leaves queue behaviour to that service (remote_execution_queue_time_threshold_s; priority 'up to the RE server', on the CommandExecutorConfig page). Buck2 is a build tool and does not claim to be an execution pool; the paradigm difference is that GF owns the scheduler as a product surface with SLOs, while for Buck2 the throughput question belongs to whichever backend a project attaches.

    • Remote execution (RE): Distributed execution of actions on remote workers. It can speed up builds significantly by scaling the nodes available for parallel actions.

      Buck2 docs, Glossary of Terms(opens in a new tab)

    • the build can send actions to a set of remote servers to be executed, increasing the parallelism significantly.

      Buck2 docs, Why Buck2(opens in a new tab)

    • services that expose Bazel's remote execution API

      Buck2 docs, Remote Execution

    • successfully tested for remote execution

      Buck2 docs, Remote Execution

  5. Under GF action-cache write authority is a server-side policy decision bound to authenticated identity: a depositing lane is admissible only with a writer grant naming a trusted identity and matching subject binding, the tenant overlay's ActionCacheWritePolicy admits only Push invocations on listed refs while every Developer invocation and every PullRequest subject is refused, developer work reaches the cache only through cell-validated remote Execute, tenant isolation is a required core invariant, and an unauthorized write is rejected without making CAS or AC state visible. Under Buck2 the client presents an instance name, an optional client certificate and headers from the project's .buckconfig (the Remote Execution page), remotely executed actions are cached by the service ('everyone writes to the cache through sandboxed remote execution' on the Benefits When Compared to Buck1 page), and locally executed results are uploaded only when the project's execution platform sets allow_cache_uploads (CommandExecutorConfig). Server-side write authorization is the attached RE service's responsibility and is not established by Buck2's documentation; the paradigm difference is that GF states the write policy in the tenant overlay and refuses the client-side path as a matter of law, whereas Buck2 leaves both the local-upload switch and the trust decision to configuration outside the tool's own contract.

    • instance identifier for requests

      Buck2 docs, Remote Execution (the instance_name field)

    • client certificate and private key (PEM-encoded)

      Buck2 docs, Remote Execution (the tls_client_cert field)

    • comma-separated header pairs for RE requests

      Buck2 docs, Remote Execution (the http_headers field)

    • allow_cache_uploads: Whether to upload local actions to the RE cache

      Buck2 docs, CommandExecutorConfig

    • remote_execution_use_case: The use case to use when communicating with RE

      Buck2 docs, CommandExecutorConfig

In fairness

Buck2 is a build tool and REAPI client; GF is an execution fabric with its own scheduler, authenticated admission and publication policy. These are different layers, not interchangeable products. Speaking REAPI alone does not make Buck2 a GF frontend: GF's current action frontend is Bazel, with a separate Nix derivation integration. Shared remote caching is not unique to GF. GF does not implement Buck2's graph engine.

BuildBuddy

Documentation read

  1. GF shares authorized Bazel results and Nix closures between developer and CI callers under the adopter's policy. Those result types retain distinct publication and read-authorization rules; a Bazel cache hit is not proof that a Nix closure can be substituted. BuildBuddy's cache is documented as a Bazel cache ('the open-core developer productivity platform built for Bazel', in the BuildBuddy Docs introduction); it is shared across an organization by default, with per-context API keys and remote_instance_name namespaces offered as opt-in separation (the Authentication Guide and the RBE Setup page); whether it carries Nix closures is not established.

  2. GF separates thin CI admission from remote execution: the shared pool supplies action capacity, while the runner coordinates the request. BuildBuddy's remote runners carry warmth as a whole-VM snapshot of the Bazel client (its docs give a 66GB example and a 100GB disk limit on the Introduction to Remote Runners page) and BuildBuddy itself recommends keeping that runner small and farming compute to executors.

  3. In GF's Bazel execution path, workers execute actions and upload outputs to content-addressed storage. The cell validates the worker result and publishes action-cache state; workers do not hold action-cache write authority. Developer invocations cannot mint their own action-cache writes, and writer authority remains bound server-side to authenticated identity and policy. Nix closure publication and independent substitution remain separate obligations. Under BuildBuddy, uploading locally executed results is a client bazelrc flag (--remote_upload_local_results in the recommended GitHub Actions config, on the RBE with GitHub Actions page), and cache write permission travels with the key: org-level keys via a read-only checkbox, user-owned keys via the org role, where the Developer role reads but does not write the action cache (the Authentication Guide).

  4. GF refuses a locally executed result as qualifying evidence and requires the core to declare outage distinct from miss, with no fallback lane. Under BuildBuddy the Bazel client's default strategy list is 'remote,worker,sandboxed,local' (RBE Setup), so local execution remains a reachable strategy within the same invocation, and eviction of a blob Bazel believes present is documented as happening 'without Bazel's knowledge' with detection and recovery left to client flags (Troubleshooting RBE Failures).

  5. Under GF an application repository's checked-in ActionPlan names only an abstract capability class and never an executor pool, runner label or provider endpoint, and the controller joins that demand to provider supply. Under BuildBuddy, a pool is an operator-chosen string on the executor deployment (MY_POOL) and selecting a specific pool per platform or target is written into BUILD files as exec_properties (RBE Executor Pools, RBE Platforms); a server-side default_pool_name and a bazelrc --remote_default_exec_properties cover the default case, so a source change is required only to bind a target or platform to a non-default pool.

In fairness

BuildBuddy is a close comparison for Bazel remote execution: shared executors and shared results are not unique to GF. GF's distinction is the combined admission, no-fallback and publication contract across Bazel actions and Nix derivations. VM snapshots address client startup and warm-state costs that an action cache alone does not eliminate.

Buildbarn

the REAPI execution backend: bb-storage, bb-remote-execution, bb-clientd, bb-deployments

Documentation read

  1. GF binds each admitted action to verified caller identity, a checked-in ActionPlan and the adopter's policy. Attachment is an admission requirement, not a claim that GF intercepts arbitrary commands on a developer's machine. Buildbarn exposes remote execution and storage services that build clients configure separately.

    • When using only this storage daemon, build actions will still be executed on the local system.

      Buildbarn, bb-storage README

    • Note that the name and SHA of the container image is configured in multiple places: BUILD.bazel, .jsonnet configuration for Buildbarn and for the actual runner container (docker compose, kubernetes yaml, etc...)

      Buildbarn, bb-deployments README, the Remote execution section

  2. Choose GF when a green lane must mean the result is already in the shared cache for the next caller, because GF requires deposit on success as a lane invariant and the axiom orders publication before the lane reports success, and the cell validates the result, while Buildbarn leaves publication to the worker and, for cache-only callers, to a client flag. Buildbarn's read-time completeness check also defends against CAS eviction after AC publication, which a write-time gate alone does not.

    • orchestrates their execution. This includes downloading the build action's input files and uploading its output files

      Buildbarn, bb-remote-execution README

    • Only return ActionResult messages for which all output files are present in the Content Addressable Storage (CAS). Certain clients, such as Bazel, require the use of this decorator.

      Buildbarn, bb-storage, blobstore.proto

    • Two ACs are made, corresponding with instance names foo and bar. The former is intended just for remote caching, which is why it's made client-writable

      Buildbarn, bb-storage README

  3. Both designs place action parallelism in the worker pool. GF additionally requires a thin CI admission edge and forbids a local alternative for dispatched work. More CI runners alone are not evidence of greater action throughput. Buildbarn's documented contract stops at its remote workers; keeping the calling CI runner thin remains a pipeline configuration choice.

    • Most setups will run a single instance of bb_scheduler and a large number of pairs of bb_worker/bb_runner processes

      Buildbarn, bb-remote-execution README

    • Number of actions to run concurrently on this runner

      Buildbarn, bb-remote-execution, bb_worker.proto

    • container images from Act ... used for running GitHub Actions locally under Ubuntu 22.04

      Buildbarn, bb-deployments README

  4. Choose GF when "it fell back to local and rebuilt" must be unrepresentable as success rather than merely discouraged, because GF types local execution of remote-required work as inadmissible at the lane, at admission and as evidence, and requires outageDistinctFromMiss on the core so the lane, not the caller, owns the reaction to unavailability. Buildbarn's storage layer distinguishes UNAVAILABLE from NOT_FOUND internally but its contract ends at the returned status: whether the calling build tool retries, treats it as a miss, or executes locally is the client's setting, local execution is the documented behaviour when only storage is deployed, and one documented decorator reports a size-limit refusal as NOT_FOUND, which GF forbids.

    • does, however, facilitate remote execution by allowing execution requests to be forwarded to a separate remote execution service

      Buildbarn, bb-storage README

    • When using only this storage daemon, build actions will still be executed on the local system

      Buildbarn, bb-storage README

    • The empty string can be used to match all instance names, thereby causing all requests to be forwarded to a single scheduler

      Buildbarn, bb-storage, bb_storage.proto

    • Platform queues are removed when no workers have been present during this time period.

      Buildbarn, bb-remote-execution, bb_scheduler.proto

  5. Choose GF when the tenant boundary must be derived from verified ownership, so that the instance name a caller presents resolves to a tenant bound server-side through ResolvedActionBinding/v1 rather than being trusted as the tenant, because GF types tenancy as a server-side invariant while Buildbarn's boundary is an instance-name prefix in the request plus an operator-written authorizer. Buildbarn's JMESPath authorizer is evaluated against authentication metadata obtained from TLS client certificates or JWT bearer tokens (bb-storage, grpc.proto), so an operator can bind instance names to an authenticated identity; the difference is that in Buildbarn this is operator-written policy whose documented examples default to allow-all and prefix matching, while in GF the binding is a typed invariant the core cannot omit.

    • Allows requests whose instance names are prefixed by one of these values

      Buildbarn, bb-storage, auth.proto

    • corresponds to the REv2 instance name that was part of the client request

      Buildbarn, bb-storage, auth.proto

    • Two ACs are made, corresponding with instance names foo and bar. The former is intended just for remote caching, which is why it's made client-writable

      Buildbarn, bb-storage README

    • The prefix of the instance name for which requests from clients should be routed to this worker

      Buildbarn, bb-remote-execution, bb_worker.proto

In fairness

Buildbarn supplies REAPI storage, scheduling and worker components. GF adds an adopter-owned admission and publication contract around each action. A compatible REAPI implementation can be a component of such a fabric, but protocol compatibility alone does not establish an integrated or qualified deployment.