Local Remote Execution
Hermetic, content-addressed builds on your machine. No Docker, no containers, no surprise dependencies.
Local Remote Execution (LRE) is NativeLink's answer to the question every team eventually asks: how do I get the hermeticity of remote execution without losing the iteration speed of building locally?
The short version: run a remote-execution worker on your laptop, pin the toolchain with Nix, route every action through the worker. Same guarantees as RBE; no network round-trip.
What LRE gives you
- Identical artifacts on every machine. The Nix-pinned toolchain produces bit-identical output across your laptop, your colleague's laptop, and CI.
- Real hermeticity, no Docker. No container runtime overhead, no image rebuild on every toolchain change.
- Cache sharing with remote workers. Because the action hashes match across local and remote execution, a cache hit one place is a cache hit everywhere.
- Works offline. Once the toolchain is fetched, LRE is a pure local computation.
How it works
Three pieces in collaboration:
- A NativeLink worker running on the developer's machine, bound to the loopback interface.
- A pinned toolchain, provided by a Nix flake. Every binary the
build invokes — compiler, linker, assembler, even
coreutils— is content-addressed in the Nix store. - A configured client (Bazel, Buck2, ...) that targets the local worker the same way it would target a remote cluster.
When the client issues an Execute call, the action's inputs are
fetched from the local CAS (also running on localhost), the worker
runs the command in a sandbox using the pinned toolchain, and the
outputs are stored back in the CAS — same protocol path as remote
execution, with the network replaced by a Unix socket.
When to use LRE
| Scenario | Use LRE? |
|---|---|
| Solo dev iterating on a personal monorepo | ✓ |
| Team of 5 sharing a cache without a server | ✓ |
| Reproducing a CI failure locally | ✓ |
| Fan-out across hundreds of cores | ✗ (use a remote worker fleet) |
| Building for a platform you can't host | ✗ |
The split is "is the bottleneck CPU or determinism?" — LRE gives you determinism. For raw throughput, you still want a remote worker pool.
Setup
The recommended flow:
Install Nix with flakes enabled. The next-gen installer is the easiest path.
Pull the NativeLink Bazel flake template.
nix flake init -t github:TraceMachina/nativelink#bazelEnter the dev shell. This downloads the Nix-pinned toolchain on first run and generates
lre.bazelrc, which the template's.bazelrcalreadytry-imports.nix developPoint
user.bazelrcat a NativeLink cache and executor. The template ships it with placeholders:build --remote_cache=grpcs://TODO build --bes_backend=grpcs://TODO build --remote_timeout=600 build --remote_executor=grpcs://TODOFill these in with either your dev.nativelink.com credentials or a self-hosted cluster with a worker capable of the platform the example needs. A plain local instance (drop the
singrpcs://, point at127.0.0.1) is enough to validateremote_cache, but the C++ example'slre-ccplatform still needs a real worker running the matching container image — see Local Remote Execution for the exact commands and what each one actually requires.Build the example.
bazel build hello-world
The first build will be the same wall-time as a normal local build. The second one will be near-instant — that's the cache doing its job.
Adding LRE to an existing flake-parts project
The template above is the fastest way to see LRE working, but it
starts a new project. To add the same Nix-pinned toolchains to a
project you already have, see the full flake-side and Bazel-side
wiring — the nativelink.flakeModule import, lre.installationScript,
and the local-remote-execution Bazel module override — in
local-remote-execution/README.md.
Why Nix, specifically?
LRE needs every input to a build action to be hashable. The compiler,
the linker, the standard library headers, the bash that runs the
wrapper script. Hashing a /nix/store/...-clang-18 path is trivial
because the path itself encodes a hash of every input that produced
it. Hashing a /usr/bin/clang is impossible without scraping its
filesystem state.
That's the entire reason. Nix gives us hash-pinned tools; everything else follows.
For background, see What is Nix? and How do I make my Bazel setup hermetic?.
What's next
- RBE → Local Remote Execution — the
Setup steps above, run for real, plus what's actually
local-vs-remote once you leave
x86_64-linux. - Local cache and executor — a cache and executor to point the setup above at, no Nix required for the server side.
- Architecture — the full RE-API picture LRE plugs into.
- Configuration → Basic — the JSON5 the local worker reads.
- RBE → Nix templates — the
bazelflake template this page walks through.