Bazelisk & .bazelversion
When you open a Bazel project for the first time, you won't install or manage the Bazel binary yourself. Instead, you'll use Bazelisk — a version-aware wrapper that reads a .bazelversion file, downloads the correct Bazel binary, and runs it1. This mechanism ensures every developer on the team builds with the exact same Bazel version, without manual coordination.
Why Not Install Bazel Directly?
Bazel releases new major versions roughly annually (Bazel 7, 8, 9...), and each project pins to a specific version. If you download a Bazel binary from the GitHub releases page, you might end up with a different version than your team — and version mismatches cause subtle, hard-to-diagnose build failures1. Bazelisk eliminates this problem entirely.
How It Works
Bazelisk is typically installed on your PATH as bazel. When you type bazel, you're actually running Bazelisk, which:
- Looks for a
.bazelversionfile in the current directory or any parent directory. - Downloads the specified Bazel version (if not already cached locally).
- Delegates the command to that version.
This is transparent — you use bazel build, bazel test, and other commands as if Bazel were installed directly.
The .bazelversion File
This is one of the first files you'll see at the root of a Bazel project2. It contains a single line: the Bazel version number.
9.0.0
That's it. When Bazelisk reads this file, it knows exactly which binary to fetch. The file is checked into version control, so cloning the repository is enough to guarantee the correct Bazel version.
Installing Bazelisk
On macOS, the most common method is Homebrew1:
brew install bazelisk
On other platforms (Linux, Windows), you can download a binary from the Bazelisk releases page: pick the artifact for your OS and architecture, rename it to bazel, and put it in a directory on your PATH (or symlink it there under that name). Alternatively, install via NPM:
npm install -g @bazel/bazelisk
After installation, the bazel command on your system is Bazelisk. You can verify with bazel version, which shows both the wrapper version and the underlying Bazel version1.
.bazeliskrc
Some teams also use a .bazeliskrc file to configure Bazelisk behavior — for example, pointing it at the Aspect CLI, an extended Bazel wrapper with additional developer tooling (covered in 3.5.4 Aspect CLI Extensibility). Like .bazelversion, this file is checked into the repository, so team-wide configuration is automatic2.
You never install a specific Bazel version manually. You install Bazelisk once, and .bazelversion in each project handles the rest. This version-pinning mechanism is the foundation of reproducible builds — before you even write a BUILD file, the build tool version itself is locked down.
How Bazelisk Resolves the Version
When Bazelisk starts, it walks a priority chain to decide which Bazel version to use3:
- The
USE_BAZEL_VERSIONenvironment variable — highest priority. Useful for one-off testing (USE_BAZEL_VERSION=8.1.0 bazel build //...) without modifying checked-in files. - A
USE_BAZEL_VERSIONentry in.bazeliskrcat the workspace root. - A
.bazelversionfile in the current directory or any parent directory. - The
USE_BAZEL_FALLBACK_VERSIONenvironment variable — a safety net with configurable behavior (error:,warn:, orsilent:prefix). - If nothing is set, Bazelisk falls back to the latest stable release — but this is essentially random drift and defeats the purpose of version pinning. Always pin explicitly.
Beyond exact version numbers like 9.0.0, Bazelisk understands several other formats3:
8.x— floating version, resolves to the latest release in the 8.x LTS series.8.1.0rc2— release candidates for testing upcoming releases.- Git commit hashes — for testing specific Bazel commits.
last_green/last_rc— latest CI-passing commit or most recent release candidate, used by contributors working on Bazel itself.
For day-to-day work, always use an exact pinned version — the other formats are for debugging and migration scenarios covered later.
Bazelisk also supports tools/bazel wrappers and custom Bazel forks — these are maintainer and infrastructure concerns covered in later levels.
1.What does Bazelisk do when you run `bazel build`?
2.Why is installing Bazel directly (without Bazelisk) discouraged?
3.Where does .bazelversion typically live in a project?
Footnotes
-
Bazel Training 101 (Part 5): Installing Bazel — Bazelisk as recommended installation method, .bazelversion behavior, available via Homebrew and NPM ↩1 ↩2 ↩3 ↩4
-
Bazel 101 Training (Part 6): Create a repository — .bazelversion and .bazeliskrc as key repository files ↩1 ↩2
-
Bazelisk README — version resolution algorithm, version formats, and full configuration reference ↩1 ↩2