NativeLink (Remote execution)
A Nix-powered, single-binary, Rust implementation of Bazel's Remote Execution API (REAPI) — a content-addressed cache (CAS + ActionCache) and a worker scheduler — whose distinguishing idea is a composable store stack declared in one JSON5 file and a Local Remote Execution (LRE) framework that uses Nix to make a developer's local toolchain bit-for-bit identical to the remote one.
| Field | Value |
|---|---|
| Language | Rust (≈86% of the tree) + Nix (toolchains, LRE, packaging) + a small TypeScript docs/UI surface |
| License | FSL-1.1-Apache-2.0 (Functional Source License 1.1, converting to Apache-2.0 two years after each release) |
| Repository | TraceMachina/nativelink |
| Documentation | nativelink.com/docs · config reference (nativelink-config) |
| Category | Remote Execution Backend |
| Workspace model | None of its own — it is a REAPI server for a build client (Bazel, Buck2, Reclient, Goma); the "workspace" is the client's |
| First released | Public repo 2020-12-24 (as turbo-cache); rebranded NativeLink; v1.0.0 GA on 2026-03-23 |
| Latest release | v1.3.2 (2026-05-30) |
Latest release:
v1.3.2, cut 2026-05-30 — the tail of a rapidv1.xcadence (v1.0.0GA 2026-03-23, thenv1.1.02026-05-06,v1.2.02026-05-15,v1.3.02026-05-21). Like everyREAPIbackend, NativeLink ships no "workspace" version: its compatibility surface is the wire-level Remote Execution API (REAPIv2) over gRPC, not a manifest format. As of June 5, 2026 it is offered as a self-hostable single binary / container image (the open-source core underFSL-1.1-Apache-2.0) and as a managed cloud.
Overview
What it solves
NativeLink is not a workspace tool, a package manager, or a build system. It has no manifest, no members array, no dependency resolver, and no notion of a "project." It is the server side of the remote-build contract that Bazel, Buck2, Pants, Soong, Reclient, and Goma speak: a build client computes its own action graph, then ships individual actions — a command plus the exact Merkle tree of declared inputs — over gRPC, and NativeLink either (a) returns a cached ActionResult if that action digest has been seen before anywhere in the org, or (b) schedules the action onto a worker, runs it, and streams the outputs back into the CAS. It belongs in this monorepo survey for the same reason BuildBuddy and Buildbarn do: the caching and remote-execution dimension (dimension 4) of a large monorepo is frequently outsourced to a REAPI backend, and NativeLink is the Rust, single-binary, Nix-native member of that family.
The problem it attacks is the one that defeats every language-native package manager (Cargo, dub, Go modules) at monorepo scale: redundant work. In a repo with hundreds of thousands of targets, the same compile/test action is rerun across every engineer's machine, every CI shard, and every branch. Bazel models each action as a pure function of its inputs and hashes them into an action digest; NativeLink stores the result keyed by that digest in a content-addressable store (CAS), so the second time anyone requests the same digest, bytes are fetched instead of recomputed. Remote execution extends this from "cache the result" to "run the function on a shared farm," giving a small laptop the throughput of a cluster.
What sets NativeLink apart inside the REAPI-backend family is twofold: (1) it is written in Rust with a deliberately garbage-collector-free, memory-safe core, pitched at safety-critical and mission-critical native codebases; and (2) it treats the cache backend as a composable stack of small stores (fast_slow, dedup, compression, verify, shard, size_partitioning, …) wired together in one JSON5 config, rather than a fixed three-tier cache.
Design philosophy
From the project README (README.md):
"NativeLink is an efficient, high-performance build cache and remote execution system that accelerates software compilation and testing while reducing infrastructure costs."
The GitHub repository description sharpens the positioning to the cross-client, Nix-native angle (repository metadata):
"NativeLink is a Nix-powered, open source, high-performance build cache and remote execution server, compatible with Bazel, Soong, Pants, Buck2, Reclient, and other RE-compatible build systems. It offers drastically faster builds, reduced test flakiness, and support for specialized hardware."
And the trust signal it leads with (README.md):
"NativeLink is trusted in production environments to reduce costs and developer iteration times — handling over billions of requests per month for its customers, including large corporations such as Samsung."
Four architectural commitments follow, and they shape the whole system:
- The client owns the workspace; the server owns the work. NativeLink deliberately knows nothing about
WORKSPACE/MODULE.bazel,BUCKfiles, packages, or targets. Everything it sees is post-analysis: a stream ofActions addressed by digest. This is the inverse of Nx/Turborepo, which own a project graph and a task pipeline; NativeLink is the substrate under such an engine, reachable by any client that speaksREAPI. - Memory-safe, GC-free Rust as a correctness argument. The choice of Rust is not incidental marketing — the pitch is that a remote-execution server sitting on the critical path of every build in a safety-critical org should have no garbage-collector pauses, no data races, and deterministic resource behavior. This is the same value proposition Rust brings to the async-I/O runtimes in the sibling survey (see Glommio/Tokio), applied to build infrastructure instead of network servers.
- The backend is a composed store stack, not a fixed cache. Every storage concern — local disk, an S3/GCS/Redis backend, deduplication, compression, integrity verification, sharding, hot/cold tiering — is its own small
Storeimplementation, and a deployment assembles them by nesting store specs inside one another inJSON5. Afast_slowover adedupover acompressionover anexperimental_cloud_object_storeis a single declarative tree. - Hermeticity is enforced down to the toolchain via Nix. NativeLink's
LREframework generates the same compiler/toolchain from a Nix flake for both the developer's machine and the remote workers, so local and remote actions hash identically and the cache hits across laptops, CI, and the farm — addressing the "but it built on my machine" cache-miss problem at its root.
NativeLink sits in the same REAPI-backend family as BuildBuddy (Go, open-core, autoscaling cloud + web UI) and Buildbarn (Go, Kubernetes-native, highly decomposed); the shared wire protocol is the reason a Bazel or Buck2 client can swap one for another by changing a single --remote_executor URL. For how the client side drives this contract, see the Bazel and Buck2 deep-dives; for the D-language gap this whole class of tooling exposes, see the D landscape.
Core components and gRPC surface
NativeLink is a single binary (nativelink) that, depending on which services its config file enables, plays the role of cache server, scheduler, and/or worker. The same executable can run all roles in one process (the local quickstart) or be deployed as separate cache / scheduler / worker tiers at scale. It implements the standard REAPI services a Bazel client connects to, plus an internal WorkerApi for workers to register and stream work.
| Concept | Component / service | Role |
|---|---|---|
| Cache (read/write) | cas, ac, bytestream, capabilities services | Standard REAPI cache surface the client hits for blob digests and action results |
| Remote execution | execution service | Accepts Execute/WaitExecution; turns an action digest into a scheduled job |
| Scheduling | schedulers[] (simple, grpc, cache_lookup, property_modifier) | Matches a queued action to a worker whose platform_properties satisfy the action |
| Worker brokering | worker_api service (private listener) | Workers register, stream capabilities, lease jobs, and report results |
| Worker | workers[] (local) | Pulls leased actions, materializes the input tree on disk, runs the command, uploads outputs |
| Storage | stores[] (a composable stack — see How it works) | Every cache/CAS backend; nested store specs form the durable substrate |
| Toolchain hermeticity | local-remote-execution/ (LRE) | Nix-generated toolchains identical local↔remote for ~100% cache hit rate |
| Server / listeners | servers[] (listener.http, services) | One or more gRPC listeners; public cache/exec ports vs. private worker/admin ports |
The standard REAPI client contract
A Bazel (or Buck2 / Reclient / Goma) client never names NativeLink components directly; it points its remote flags at the public gRPC listener and NativeLink multiplexes the REAPI services behind it:
# .bazelrc — point Bazel at a NativeLink endpoint
build --remote_cache=grpc://localhost:50051 # REAPI CAS + ActionCache + ByteStream
build --remote_executor=grpc://localhost:50051 # REAPI Execution service (omit for cache-only)--remote_cache enables the cache services only; adding --remote_executor promotes the same endpoint to a full remote-execution backend (if unset, --remote_cache defaults to the executor's value). Because the contract is the wire protocol, the very same client config works against BuildBuddy or Buildbarn — the differentiation is operational, not protocol-level.
How it works
The composable store stack — the defining idea
NativeLink's signature design is that storage is built from small, nestable Store implementations rather than one monolithic cache. The stores[] array in the config is a list of named stores, and most store types take other stores as fields, so a deployment composes a tree. The variants (from nativelink-config/src/stores.rs) include:
| Store spec | Doc-comment role (quoted / paraphrased from source) |
|---|---|
memory | "Memory store will store all data in a hashmap in memory." |
filesystem | "Stores the data on the filesystem" (local persistent CAS; required as a worker's fast store) |
fast_slow | "will first try to fetch the data from the fast store and then if it does not exist try the slow store" |
dedup | rolling-hash (fastcdc) chunking + sha256 per slice; index_store (small/fast) + content_store |
compression | "will compress the data inbound and outbound" (LZ4: "extremely fast … does not perform very well in compression ratio") |
verify | "used to apply verifications to an underlying store implementation" (size + sha256 digest integrity) |
completeness_checking | "verifies if the output files & folders exist in the CAS before forwarding the request" |
existence_cache | "wrap around another store and cache calls to has so that subsequent calls will be faster" |
shard | "Shards the data to multiple stores" by digest hash for load distribution |
size_partitioning | routes small vs. large objects to different underlying stores by the digest's size field |
ref_store | "reference a store in the root store manager" — share one store instance across the tree |
grpc | "pass-through calls to another GRPC store" (proxy to an upstream CAS) |
redis | "Stores data in any stores compatible with Redis APIs." |
experimental_cloud_object_store | one blob backend over AWS S3, GCS, Azure Blob, NetApp ONTAP S3, Cloudflare R2 |
experimental_mongo | MongoDB backend for CAS and scheduler state (optional change streams) |
noop | "sends streams into the void and all data retrieval will return 404" |
cache_metrics | "wraps another store and emits low-cardinality OpenTelemetry cache operation metrics" |
The power is in the nesting. A realistic durable CAS might read: fast_slow { fast: filesystem, slow: dedup { index_store: redis, content_store: compression { experimental_cloud_object_store: s3 } } } — a local disk hot tier backed by content-defined-chunked, LZ4-compressed blobs in S3 with the chunk index in Redis. Each layer is independently testable and reusable. This is the same "shell-with-hooks / capability composition" instinct the Sparkles guidelines prize, applied to a storage substrate.
NOTE
The fast_slow store is a tiered mirror, not a write-through-to-slow cache, and the source warns about it explicitly: "this store will never check to see if the objects exist in the slow store if it exists in the fast store." For remote execution, where workers must reach the authoritative slow store, that subtlety is a real foot-gun — the basic_cas.json5 example even routes its slow to noop precisely because the CAS and worker share one filesystem locally.
Content-defined deduplication
The dedup store is where NativeLink earns its "content-addressed" credentials beyond whole-blob hashing. From stores.rs:
"A dedup store will take the inputs and run a rolling hash algorithm on them to slice the input into smaller parts then run a sha256 algorithm on the slice."
The chunk sizes are tunable (min_size default 64 KiB, normal_size 256 KiB, max_size 512 KiB), and slices are split via content-defined chunking (fastcdc) so that an edit to one region of a large file re-uploads only the affected chunks — the rest are already present by digest. The slice index lives in a small, fast index_store; the chunk bytes go to a large, slow content_store. This is the build-artifact analogue of a pnpm-style content-addressed package store, but at sub-file granularity.
A complete config, end to end
The canonical single-process example (nativelink-config/examples/basic_cas.json5) wires stores, scheduler, worker, and two listeners into one running server. Abridged:
{
stores: [
{
name: 'AC_MAIN_STORE',
filesystem: {
content_path: '…/content_path-ac',
temp_path: '…',
eviction_policy: { max_bytes: 1000000000 },
},
},
{
name: 'WORKER_FAST_SLOW_STORE',
fast_slow: {
// "fast" must be a "filesystem" store because the worker uses it to make
// hardlinks on disk to a directory where the jobs are running.
fast: {
filesystem: {
content_path: '…/content_path-cas',
eviction_policy: { max_bytes: 10000000000 },
},
},
slow: { noop: {} }, // CAS and worker share local storage → slow is a noop
},
},
],
schedulers: [
{
name: 'MAIN_SCHEDULER',
simple: {
supported_platform_properties: {
cpu_count: 'minimum',
memory_kb: 'minimum',
cpu_arch: 'exact',
OSFamily: 'priority',
'container-image': 'priority',
ISA: 'exact',
InputRootAbsolutePath: 'ignore',
},
},
},
],
workers: [
{
local: {
worker_api_endpoint: { uri: 'grpc://127.0.0.1:50061' },
cas_fast_slow_store: 'WORKER_FAST_SLOW_STORE',
upload_action_result: { ac_store: 'AC_MAIN_STORE' },
work_directory: '/tmp/nativelink/work',
platform_properties: {
cpu_count: { values: ['16'] },
cpu_arch: { values: ['x86_64'] },
ISA: { values: ['x86-64'] },
},
},
},
],
servers: [
{
name: 'public',
listener: { http: { socket_address: '0.0.0.0:50051' } },
services: {
cas: [{ instance_name: '', cas_store: 'WORKER_FAST_SLOW_STORE' }],
ac: [{ instance_name: '', ac_store: 'AC_MAIN_STORE' }],
execution: [
{
instance_name: '',
cas_store: 'WORKER_FAST_SLOW_STORE',
scheduler: 'MAIN_SCHEDULER',
},
],
capabilities: [
{
instance_name: '',
remote_execution: { scheduler: 'MAIN_SCHEDULER' },
},
],
bytestream: [
{ instance_name: '', cas_store: 'WORKER_FAST_SLOW_STORE' },
],
},
},
{
name: 'private_workers_servers',
listener: { http: { socket_address: '0.0.0.0:50061' } },
services: {
worker_api: { scheduler: 'MAIN_SCHEDULER' },
admin: {},
health: {},
},
},
],
global: { max_open_files: 24576 },
}Three structural facts are worth drawing out:
- Services are bound to stores by name.
cas,ac,execution, andbytestreameach reference a store by its stringname; the same store can back multiple services and multipleinstance_names (theREAPInamespace that scopes a cache, e.g.""vs"main"). - Public vs. private listeners are separate ports with different permission sets. The
publiclistener exposes the cache/exec frontend; theprivate_workers_serverslistener exposesworker_api/admin/health— a backend API that should not be internet-reachable. - A worker is just another store consumer. The
localworker references thefast_slowCAS store for inputs/outputs and theacstore for results, and declares theplatform_propertiesit can satisfy.
Scheduling: matching actions to workers
The simple scheduler is the default in-process matcher (nativelink-config/src/schedulers.rs). Its job is to pair a queued action against a worker whose advertised platform_properties satisfy the action's Platform requirements. Each property name carries a matching strategy in supported_platform_properties:
| Strategy | Meaning |
|---|---|
exact | the worker's value must equal the action's requested value (e.g. cpu_arch, ISA) |
minimum | numeric: the worker must advertise at least the requested amount (cpu_count, memory_kb) |
priority | the property steers placement preference (e.g. OSFamily, container-image, lre-rs) |
ignore | the property is dropped from matching (e.g. InputRootAbsolutePath) |
SimpleSpec also exposes operational knobs: allocation_strategy (least_recently_used — "Prefer workers that have been least recently used to run a job" — vs most_recently_used), worker_timeout_s ("Remove workers from pool once the worker has not responded in this amount of time"), max_job_retries ("If a job returns an internal error or times out this many times … the scheduler will return the last error to the client"), and client_action_timeout_s.
The other scheduler variants are wrappers, composed the same way stores are:
grpc— "A scheduler that simply forwards requests to an upstream scheduler … useful when doing some kind of local action cache or CAS away from the main cluster of workers." This is how a local proxy tier delegates execution to a central farm.cache_lookup— short-circuits execution by returning a cachedActionResult(anActionCachehit) instead of scheduling the action.property_modifier— rewrites an action's platform properties before handing it to a nested scheduler (e.g. inject a defaultcontainer-image).
A production scheduler is therefore often a stack: cache_lookup → property_modifier → simple, exactly mirroring the store-stack philosophy.
Local Remote Execution (LRE) — Nix-enforced toolchain parity
NativeLink's most distinctive feature beyond the store stack is LRE. The recurring pain of any remote-execution deployment is toolchain drift: a developer's locally-installed compiler differs from the remote worker's, so action digests differ and the cache never hits across the local/CI/farm boundary. LRE attacks this with Nix. From local-remote-execution/README.md, LRE is:
"a framework to build, distribute, and rapidly iterate on custom toolchain setups that are transparent, fully hermetic, and reproducible across machines of the same system architecture."
It "mirrors toolchains for remote execution in your local development environment," letting developers "reuse build artifacts with virtually perfect cache hit rate across different repositories, developers, and CI." Because the local toolchain and the remote worker image are derived from the same nixpkgs pin, they resolve to byte-identical Nix store paths, so an action built locally and the same action built remotely produce the same digest — and the cache hits. This is the deepest expression of point 4 above: hermeticity not just at the action level (declared inputs) but at the toolchain level (the compiler itself is a content-addressed input).
The five dimensions
1. Workspace declaration & topology
Not applicable in the usual sense — and that is the point. NativeLink has no workspace manifest, no members glob, no root config enumerating sub-packages. The "topology" it operates on is the action graph the client already computed: a stream of Actions, each carrying a Merkle tree of input digests. Discovery, globbing, and the dependency DAG all happen on the client (Bazel/Buck2) before the first byte reaches NativeLink.
What NativeLink does declare — and declares unusually richly — is server and fleet topology: the stores[] stack (storage topology), the schedulers[] (matching topology), the workers[] and their platform_properties (fleet topology), and the servers[]/listeners (network topology). Cross-action "namespacing" is done with the REAPI instance_name (the ""/"main" keys in the config), which scopes cache entries — a cache boundary, not a project one.
NOTE
For dub, the lesson is one of layering: a future dub [workspace] block (the client-side topology) is one concern; a REAPI backend like NativeLink would sit below it, caching whatever actions a workspace-aware dub emits. The two concerns are orthogonal and compose — NativeLink contributes nothing to workspace declaration and everything to the cache/exec substrate beneath it.
2. Dependency handling & isolation
NativeLink isolates at the action level, not the package level, and it does so with content addressing rather than symlink trees or hoisting. There is no node_modules, no virtual store, no workspace: protocol — those are client-side dependency models. What NativeLink guarantees is:
- Input isolation per job. Each action runs in a fresh worker
work_directorywhose input tree is materialized exactly from the action's declared Merkle tree (the worker hardlinks blobs from itsfastfilesystem store into the job dir). Nothing undeclared is visible — hermeticity enforced by the substrate, the same property Buck2 leans on. - Content-addressed deduplication of inputs. Shared inputs (a common header, a toolchain binary) live once in the
CAS; with adedupstore they are split into content-defined chunks so even partially-overlapping large inputs share storage. The worker'sfast_slowstore fetches each blob once and hardlinks it into every job that needs it. - Toolchain isolation via
LRE. Uniquely, the compiler itself is a content-addressed, Nix-pinned input, so "dependency isolation" extends past the source tree to the build environment — the cause of most spurious cache misses elsewhere.
3. Task orchestration & scheduling
The DAG lives on the client; NativeLink schedules the leaf actions that DAG emits. It is a genuine distributed scheduler, not a DAG engine:
- Property-based worker matching. The
simplescheduler pairs each queued action with a worker whoseplatform_propertiessatisfy the action'sPlatform, per theexact/minimum/priority/ignorestrategies above — so heterogeneous fleets (GPU workers, specificISAs, particular container images) route correctly. - Composable scheduler stack.
cache_lookup(short-circuit on anActionCachehit),property_modifier(rewrite properties), andgrpc(forward to an upstream farm) wrap asimplecore the same way stores nest. - Allocation strategy & resilience.
least_recently_used/most_recently_usedworker selection,worker_timeout_sto evict silent workers,max_job_retriesto bound retries before surfacing the error, andclient_action_timeout_sto reap abandoned operations.
Change detection is the cache itself: an action whose digest is unchanged is never scheduled at all — the ActionCache (optionally fronted by a cache_lookup scheduler) short-circuits it. This is the same input-hashing model as Nx/Turborepo, but at the granularity of a single compiler invocation rather than a package-level task — and made far more reliable by LRE's toolchain pinning, since drift is the usual reason a "should-hit" action misses.
4. Caching & remote execution
This is NativeLink's reason to exist, and the dimension where it is a primary implementation rather than a consumer. It is a full REAPI v2 server:
| Service / role | REAPI component | Client flag |
|---|---|---|
| Content-addressable store | cas + bytestream services | --remote_cache=grpc://… |
| Action results (digest → outputs) | ac (ActionCache) service | --remote_cache=grpc://… |
| Capability negotiation | capabilities service | (implicit on connect) |
| Remote execution of actions | execution service | --remote_executor=grpc://… |
| Worker brokering | worker_api service (private listener) | (internal; workers connect) |
Where BuildBuddy hard-codes a three-tier (disk → Redis → blob) cache, NativeLink makes the entire cache backend a user-assembled store stack — fast_slow, dedup, compression, verify, shard, size_partitioning, existence_cache, and the cloud/redis/mongo backends compose into whatever topology a deployment needs. The experimental_cloud_object_store spans S3, GCS, Azure Blob, ONTAP S3, and R2 from one spec. Integrity is opt-in via the verify store (size + sha256) and completeness_checking (outputs really exist before an ActionResult is served). Crucially, NativeLink speaks the same REAPI wire protocol as BuildBuddy and Buildbarn, so a Bazel/Buck2 client treats the three as interchangeable; the differentiation is the Rust single-binary deployment, the composable stores, and LRE, not the protocol.
5. CLI / UX ergonomics
NativeLink ships no build CLI of its own — its "command boundary" is the client's flags. The developer-facing surface is two parts: a handful of Bazel (or Buck2 / Reclient / Goma) remote flags, and the JSON5 config that operators write.
| Concern | Mechanism |
|---|---|
| Run the server | nativelink path/to/config.json5 (single binary; container image too) |
| Enable caching | --remote_cache=grpc://<endpoint>:50051 |
| Enable remote execution | --remote_executor=grpc://<endpoint>:50051 |
| Cache namespace | instance_name in config (the REAPI instance the client targets) |
| Worker selection | action Platform properties matched against worker platform_properties |
| Parallelism | the client's --jobs=N (Bazel/Buck2 fan-out), bounded by fleet capacity |
| Toolchain parity | LRE Nix flake (nix run/bazel configs generated from one nixpkgs pin) |
The target-selection ergonomics (//..., :target, --filter-equivalents, --since) belong entirely to the Bazel/Buck2 client, not to NativeLink. The operator's ergonomics, by contrast, are unusually expressive: the JSON5 config (comments, trailing commas, unquoted keys) is the whole control surface, and the nesting of stores and schedulers means most behavior changes are a config edit, not a recompile or a flag.
Strengths
- Single Rust binary, no GC. One statically-linkable executable runs cache, scheduler, and worker roles; no garbage-collector pauses on the build critical path; memory-safe by construction. Operationally far lighter than a multi-service, Redis-plus-blob-store deployment for small/medium fleets.
- Composable store stack. The standout feature —
fast_slow,dedup,compression,verify,shard,size_partitioning,existence_cache, and cloud/redis/mongobackends nest arbitrarily, so storage topology is a declarativeJSON5tree, independently testable layer by layer. LREtoolchain hermeticity. Nix-generated toolchains make local and remote actions hash identically, delivering near-100% cache hit rates across laptops, CI, and the farm — solving the toolchain-drift cache-miss problem at its root.- Drop-in
REAPIserver. AnyREAPIclient (Bazel, Buck2, Pants, Reclient, Goma, Soong) gets org-wide caching and farm-scale execution by changing one URL; no build rewrite. - Content-defined deduplication.
fastcdcchunking + per-slicesha256shares storage across partially-overlapping large inputs, not just whole-blob identity. - Production-proven at scale. The project reports billions of requests/month for customers including Samsung.
Weaknesses
- Bazel-shaped, not language-native. It only helps clients that already model builds as hermetic, content-addressed actions. A tool without that model (
dubtoday, Cargo, npm) cannot benefit until it emitsREAPIactions — a large client-side investment. - No workspace/topology features of its own. It contributes nothing to workspace declaration, dependency isolation at the package level, or task-DAG construction; those are entirely the client's job. In this survey it is a backend, not a workspace tool.
- Configuration is powerful but unguarded. The composable store stack is a sharp tool: the
fast_slow"never re-checks the slow store" subtlety, mismatchedinstance_names, or anoopslow store in the wrong place can silently break correctness or remote execution. Expressiveness shifts burden onto the operator. - Several backends marked
experimental. The cloud object store, MongoDB store, and parts of the cloud scheduler are explicitly experimental; the most battle-tested path is filesystem +fast_slow. LRE's parity guarantee is Nix-bound. The near-perfect cache-hit story assumes a Nix-based toolchain and the samenixpkgspin everywhere; teams not on Nix get theREAPIserver but not the headline hermeticity feature.FSLlicense, not pure OSS.FSL-1.1-Apache-2.0restricts competing-product use for two years before each release converts to Apache-2.0 — more permissive than many source-available licenses, but not OSI-approved-open on day one.- Niche audience. Only meaningful inside the Bazel/Buck2/Pants/Reclient ecosystem; the broader package-manager world (uv, pnpm,
dub) does not speakREAPIat all.
Key design decisions and trade-offs
| Decision | Rationale | Trade-off |
|---|---|---|
Implement REAPI/ByteStream, own no workspace model | One protocol serves every client; the build graph stays on the client where it belongs | Useless to clients that don't already emit content-addressed actions; no topology help |
| Rust core, garbage-collector-free | No GC pauses on the build critical path; memory safety for safety-critical orgs | Smaller contributor pool than Go's REAPI servers; Rust build/learning curve for hackers |
| Single binary playing all roles (cache/scheduler/worker) | Trivial local quickstart; light ops for small fleets; same image scales out by config | Less prescriptive than a decomposed (Buildbarn) design; large fleets must shard manually |
Composable store stack (nested Store specs in JSON5) | Storage topology is declarative, layered, reusable, and independently testable | Powerful but unguarded — wrong nesting (e.g. fast_slow/noop slow) can silently break correctness |
Content-defined dedup (fastcdc + per-slice sha256) | Shares storage across partially-overlapping large blobs, not just whole-file identity | Extra index store + chunking CPU; tuning min/normal/max chunk sizes is non-obvious |
Composable scheduler stack (cache_lookup/property_modifier/grpc) | Same nesting philosophy as stores; local proxy tiers and property rewriting are first-class | Behavior is spread across a stack; reasoning about a multi-layer scheduler takes care |
Property-based worker matching (exact/minimum/priority/ignore) | Heterogeneous fleets (GPU, specific ISA, container images) route correctly | Operators must keep worker platform_properties and action Platform requests in sync |
LRE: Nix-pinned toolchains identical local↔remote | Near-100% cache hits by eliminating toolchain drift — the usual cause of spurious misses | Requires committing to Nix and a shared nixpkgs pin; non-Nix teams lose the headline feature |
FSL-1.1-Apache-2.0 (delayed-open license) | Funds the project while still converting to Apache-2.0 after two years | Not OSI-open on day one; competing-product restriction during the embargo window |
Sources
- TraceMachina/nativelink — GitHub repository (source for the cited config, stores, and schedulers)
README.md— positioning, production usage (Samsung, billions of requests),FSLlicense- NativeLink documentation — concepts, on-prem deployment,
REAPIsetup nativelink-config/src/stores.rs— everyStoreSpecvariant + doc comments (fast_slow,dedup,compression,verify, …)nativelink-config/src/schedulers.rs—simple/grpc/cache_lookup/property_modifier, allocation strategy, timeoutsnativelink-config/examples/basic_cas.json5— the end-to-end single-process config quoted abovelocal-remote-execution/README.md—LRE: Nix-generated identical local↔remote toolchains- Functional Source License 1.1 (Apache-2.0 future grant)
- Bazel Remote Execution API (
REAPI) — the cross-vendor protocol NativeLink implements - Related deep-dives: BuildBuddy · Bazel · Buck2 · Pants · Please · Nx · Turborepo · Cargo · pnpm · the D landscape