How do I make my Bazel setup hermetic?
Eliminate every "it works on my machine" by declaring every input.
A hermetic build is one where every input is declared, every output is reproducible, and the result depends only on the declared inputs. Same recipe, same ingredients, same dish, every time.
Bazel can produce hermetic builds. Out of the box, it doesn't — most Bazel projects depend implicitly on system tooling, environment variables, and the host OS. Reaching real hermeticity takes deliberate work.
The hermeticity checklist
Pin the toolchain. Don't rely on
/usr/bin/cc. Userules_cc's toolchain abstraction or a Nix-provided toolchain; register the platform inMODULE.bazel.Lock dependency sources. Every
http_archiveshould pin both the URL and the sha256. Module dependencies should pin a version, not a range.Forbid
$PATHleaks. Addbuild --incompatible_strict_action_envto your.bazelrc. Bazel will now only forward an explicitly-allowed set of env vars.Use the sandbox you trust. On Linux:
linux-sandbox. On macOS:darwin-sandbox. For maximum isolation, run actions in a container or under LRE.Declare every header. Strict include-checking (
--features=layering_check) catches the headers yourcc_librarysilently transitively depends on.Compare action hashes across machines. If the same target produces different hashes on two laptops, something is leaking.
bazel aquery 'mnemonic("CppCompile", //target:label)' --output=textshows you the action's full input set.
How NativeLink helps
A hermetic build maps directly to a content-addressed cache: same inputs = same hash = same cached result. NativeLink doesn't make your build hermetic, but it surfaces failures fast — non-hermetic actions show up as cache misses where there should be hits, or, worse, as nondeterministic action results.
The fastest way to find leaks: enable LRE, build the same target twice on the same machine, and compare outputs. Anything that differs is a leak.
Further reading
- What are toolchains?
- What is Nix?
- LRE — the easiest way to get there.