NativeLink
Testing Remote Execution

Local Remote Execution

The Nix-pinned toolchain workflow behind LRE, walked through and tested end to end — including exactly what runs fully local and what still needs a real worker.

Explanations → LRE covers what Local Remote Execution is and why it exists. This page is the hands-on version: the exact commands, run for real, plus two gotchas the flake alone doesn't mention.

1. Install Nix

Flakes enabled. The next-gen installer is the easiest path.

2. Pull the template

mkdir my-lre-test && cd my-lre-test
nix flake init -t github:TraceMachina/nativelink#bazel

This writes a flake.nix that imports nativelink.flakeModules.lre and pins lre = { inherit (pkgs.lre.lre-cc.meta) Env; };, plus the hello-world.cpp example, its BUILD.bazel, and platforms/BUILD.bazel.

3. Init git — this step is not optional

git init && git add -A

4. Enter the dev shell

nix develop

This fetches the Nix-pinned Clang and writes lre.bazelrc. On aarch64-darwin, the generated file looked like this (paths and hashes will differ on your machine):

# These flags are dynamically generated by the lre flake module.
#
# PATH=/nix/store/.../llvm-binutils-wrapper-20.1.1/bin:/nix/store/.../customClang/bin:...
# CC=/nix/store/.../customClang/bin/customClang

# Bazel-side configuration for LRE.
build --define=EXECUTOR=remote
build --extra_execution_platforms=@local-remote-execution//rust/platforms:aarch64-apple-darwin
build --extra_toolchains=@local-remote-execution//rust:rust-aarch64-darwin
build --extra_toolchains=@local-remote-execution//rust:rustfmt-aarch64-darwin
build --platforms=@local-remote-execution//rust/platforms:aarch64-apple-darwin

Notice this registers the Rust platform even though the flake requested the lre-cc Env — there's no local aarch64-darwin config under generated-cc, so the module falls back to the toolchain that does have one. The template's .bazelrc separately adds --extra_execution_platforms=@//platforms:lre-cc for the C++ example, which is the container-based platform from the callout above.

5. Point user.bazelrc at a real cache and executor

The template's own generator writes user.bazelrc with the literal word TODO as every value — this isn't a placeholder left in this page, it's what nix flake init puts on disk for you to edit:

build --remote_cache=grpcs://TODO
build --bes_backend=grpcs://TODO
build --remote_timeout=600
build --remote_executor=grpcs://TODO

Replace all three TODOs with either your dev.nativelink.com credentials or your own cluster's endpoint. Concretely, filled in for a plain local cluster (no TLS, so grpc:// instead of grpcs://, and no BES endpoint to point at) — this is the exact user.bazelrc used to produce the verified output in step 4 above:

build --remote_cache=grpc://127.0.0.1:50051
build --remote_timeout=600
build --remote_executor=grpc://127.0.0.1:50051

A dev.nativelink.com or self-hosted cluster looks the same shape, just with grpcs:// and your cluster's real hostnames in place of 127.0.0.1:50051, plus a bes_backend line if you want build results streamed there.

If you build before editing user.bazelrc at all, Bazel fails fast on the literal hostname TODO — here's the exact error that produces, captured from a real run, so you recognize it as "I forgot to edit user.bazelrc" rather than something else being wrong:

[8 / 10] Compiling src/hello-world.cpp; 0s remote, remote-cache
ERROR: BUILD.bazel:3:10: Compiling src/hello-world.cpp failed:
  Failed to query remote execution capabilities: UNAVAILABLE: Unable to resolve host TODO

Everything before that DNS failure worked correctly — the Nix toolchain, the Bazel platform wiring, and the lre-cc execution platform selection (0s remote, remote-cache means Bazel committed to executing it remotely). Once real values replace TODO, this step just works.

6. Build

bazel build hello-world

With a real endpoint in user.bazelrc and a worker running the nativelink-worker-lre-cc image available to it, this compiles src/hello-world.cpp on that worker and returns the binary through the same CAS your local Bazel reads from. Re-run and it's a cache hit.

No container-capable worker handy?

You don't need a full cluster to see the cache side of this working. Local cache and executor starts a plain NativeLink instance on localhost — enough to validate remote_cache against real credentials before you also need a worker that matches lre-cc's platform requirements.

Troubleshooting

What's next

On this page