A virtual environment for Bazel-managed tools and toolchains.
Starlark
99
72 commits
updated Sep 20, 2026
The bazel_env rule creates a "virtual environment" for Bazel-managed tools and toolchains by making them available under stable, platform-independent paths as well as on PATH.
This allows all developers to share the same tool versions as used in the Bazel build for IDEs and local usage.
bazel_env relies on the direnv tool to automatically set up PATH when entering the project directory.
When you run the bazel_env target, it will print instructions on how to set up direnv and its .envrc file.
🎙️ This rule was featured on the Aspect Insights podcast:
The example includes some commonly used tools and toolchains.
bazel_env.bzl to your MODULE.bazel file:bazel_dep(name = "bazel_env.bzl", version = "...", dev_dependency = True)
bazel_env target to a BUILD.bazel file (e.g. top-level or in a tools directory):load("@bazel_env.bzl", "bazel_env")
bazel_env(
name = "bazel_env",
toolchains = {
"jdk": "@rules_java//toolchains:current_host_java_runtime",
},
tools = {
# Tools can be specified as labels.
"buildifier": "@buildifier_prebuilt//:buildifier",
"go": "@rules_go//go",
# Tool paths can also reference the Make variables provided by toolchains.
"jar": "$(JAVABASE)/bin/jar",
"java": "$(JAVA)",
},
)
bazel_env target and follow the instructions to install direnv and set up the .envrc file, which should be committed to version control:$ bazel run //:bazel_env
====== bazel_env ======
✅ direnv is installed
❌ bazel_env's bin directory is not in PATH. Please follow these steps:
1. Enable direnv's shell hook as described in https://direnv.net/docs/hook.html.
2. Add the following snippet to a .envrc file next to your MODULE.bazel file:
watch_file .bazel_env/bin
PATH_add .bazel_env/bin
if [[ ! -d .bazel_env/bin ]]; then
log_error "ERROR[bazel_env.bzl]: Run 'bazel run //:bazel_env' to regenerate .bazel_env/bin"
fi
3. Run 'direnv allow' to allowlist your .envrc file.
bazel run //:bazel_env maintains a symlink named after the target with a leading dot (here: .bazel_env) in the target's package that points into Bazel's output tree.
All paths in the instructions go through this symlink, so they work with any --symlink_prefix setting, including --symlink_prefix=/, which suppresses the bazel-* convenience symlinks in the workspace root.
The symlink should be added to .gitignore.
Multiple bazel_env targets can be added per project.
Note that each target will eagerly fetch and build all tools and toolchains when built, so consider splitting them up into workflow-specific targets if necessary.
bazel_env target and follow the instructions to install direnv and allowlist the .envrc file:$ bazel run //:bazel_env
====== bazel_env ======
✅ direnv is installed
❌ bazel_env's bin directory is not in PATH. Please follow these steps:
1. Enable direnv's shell hook as described in https://direnv.net/docs/hook.html.
2. Run 'direnv allow' to allowlist your .envrc file.
====== bazel_env ======
✅ direnv is installed
✅ direnv added ./.bazel_env/bin to PATH
Tools available in PATH:
* buildifier: @buildifier_prebuilt//:buildifier
* go: @rules_go//go
* jar: $(JAVABASE)/bin/jar
* java: $(JAVA)
ℹ️ The bin directory is also reachable at bazel-out/bazel_env-opt/bin/bazel_env/bin relative to the workspace root.
Toolchains available at stable relative paths:
* jdk: .bazel_env/toolchains/jdk
direnv (e.g., in CI)Run the print-path subcommand of the bazel_env target and manually add its output to your PATH.
The printed path goes through the .bazel_env symlink, which the subcommand creates if needed.
For GitHub Actions, this can be done as follows:
$ bazel run //:bazel_env print-path >> $GITHUB_PATH
rules_multitool makes it easy to fetch tool binaries that match the host machine's architecture and OS and conveniently integrates with your bazel_env targets.
If you define a multitool hub called multitool, you can load the TOOLS dictionary from @multitool//:tools.bzl and use its values in the tools attribute:
load("@bazel_env.bzl", "bazel_env")
load("@multitool//:tools.bzl", "TOOLS")
bazel_env(
name = "bazel_env",
...
tools = {
...
"ibazel": TOOLS["ibazel"],
},
)
The example demonstrates this use of rules_multitool to fetch tools, and selects ibazel and terraform to add to the PATH.
Build the bazel_env target to keep the tools and toolchains up-to-date with the Bazel build.
Tools removed from the tools attribute also disappear from PATH with the next build.
The target can also be executed with bazel run to print the list of tools and toolchains.
[!IMPORTANT] Shells such as
bashandzshwill not automatically pick up changes to directories inPATH. You may need to runhash -rorrehashto clear the shell's command cache.bazel run //:bazel_envwill print the command to run.
You can reduce the verbosity of what direnv prints when you enter a folder, by adjusting the log_filter option in ~/.config/direnv/direnv.toml.
See https://github.com/direnv/direnv/issues/68#issuecomment-2812015043.
See the generated documentation for more information on the bazel_env rule.
Starlark
74.5%
Shell
25.5%
A virtual environment for Bazel-managed tools and toolchains.
Starlark
99
72 commits
updated Sep 20, 2026
The bazel_env rule creates a "virtual environment" for Bazel-managed tools and toolchains by making them available under stable, platform-independent paths as well as on PATH.
This allows all developers to share the same tool versions as used in the Bazel build for IDEs and local usage.
bazel_env relies on the direnv tool to automatically set up PATH when entering the project directory.
When you run the bazel_env target, it will print instructions on how to set up direnv and its .envrc file.
🎙️ This rule was featured on the Aspect Insights podcast:
The example includes some commonly used tools and toolchains.
bazel_env.bzl to your MODULE.bazel file:bazel_dep(name = "bazel_env.bzl", version = "...", dev_dependency = True)
bazel_env target to a BUILD.bazel file (e.g. top-level or in a tools directory):load("@bazel_env.bzl", "bazel_env")
bazel_env(
name = "bazel_env",
toolchains = {
"jdk": "@rules_java//toolchains:current_host_java_runtime",
},
tools = {
# Tools can be specified as labels.
"buildifier": "@buildifier_prebuilt//:buildifier",
"go": "@rules_go//go",
# Tool paths can also reference the Make variables provided by toolchains.
"jar": "$(JAVABASE)/bin/jar",
"java": "$(JAVA)",
},
)
bazel_env target and follow the instructions to install direnv and set up the .envrc file, which should be committed to version control:$ bazel run //:bazel_env
====== bazel_env ======
✅ direnv is installed
❌ bazel_env's bin directory is not in PATH. Please follow these steps:
1. Enable direnv's shell hook as described in https://direnv.net/docs/hook.html.
2. Add the following snippet to a .envrc file next to your MODULE.bazel file:
watch_file .bazel_env/bin
PATH_add .bazel_env/bin
if [[ ! -d .bazel_env/bin ]]; then
log_error "ERROR[bazel_env.bzl]: Run 'bazel run //:bazel_env' to regenerate .bazel_env/bin"
fi
3. Run 'direnv allow' to allowlist your .envrc file.
bazel run //:bazel_env maintains a symlink named after the target with a leading dot (here: .bazel_env) in the target's package that points into Bazel's output tree.
All paths in the instructions go through this symlink, so they work with any --symlink_prefix setting, including --symlink_prefix=/, which suppresses the bazel-* convenience symlinks in the workspace root.
The symlink should be added to .gitignore.
Multiple bazel_env targets can be added per project.
Note that each target will eagerly fetch and build all tools and toolchains when built, so consider splitting them up into workflow-specific targets if necessary.
bazel_env target and follow the instructions to install direnv and allowlist the .envrc file:$ bazel run //:bazel_env
====== bazel_env ======
✅ direnv is installed
❌ bazel_env's bin directory is not in PATH. Please follow these steps:
1. Enable direnv's shell hook as described in https://direnv.net/docs/hook.html.
2. Run 'direnv allow' to allowlist your .envrc file.
====== bazel_env ======
✅ direnv is installed
✅ direnv added ./.bazel_env/bin to PATH
Tools available in PATH:
* buildifier: @buildifier_prebuilt//:buildifier
* go: @rules_go//go
* jar: $(JAVABASE)/bin/jar
* java: $(JAVA)
ℹ️ The bin directory is also reachable at bazel-out/bazel_env-opt/bin/bazel_env/bin relative to the workspace root.
Toolchains available at stable relative paths:
* jdk: .bazel_env/toolchains/jdk
direnv (e.g., in CI)Run the print-path subcommand of the bazel_env target and manually add its output to your PATH.
The printed path goes through the .bazel_env symlink, which the subcommand creates if needed.
For GitHub Actions, this can be done as follows:
$ bazel run //:bazel_env print-path >> $GITHUB_PATH
rules_multitool makes it easy to fetch tool binaries that match the host machine's architecture and OS and conveniently integrates with your bazel_env targets.
If you define a multitool hub called multitool, you can load the TOOLS dictionary from @multitool//:tools.bzl and use its values in the tools attribute:
load("@bazel_env.bzl", "bazel_env")
load("@multitool//:tools.bzl", "TOOLS")
bazel_env(
name = "bazel_env",
...
tools = {
...
"ibazel": TOOLS["ibazel"],
},
)
The example demonstrates this use of rules_multitool to fetch tools, and selects ibazel and terraform to add to the PATH.
Build the bazel_env target to keep the tools and toolchains up-to-date with the Bazel build.
Tools removed from the tools attribute also disappear from PATH with the next build.
The target can also be executed with bazel run to print the list of tools and toolchains.
[!IMPORTANT] Shells such as
bashandzshwill not automatically pick up changes to directories inPATH. You may need to runhash -rorrehashto clear the shell's command cache.bazel run //:bazel_envwill print the command to run.
You can reduce the verbosity of what direnv prints when you enter a folder, by adjusting the log_filter option in ~/.config/direnv/direnv.toml.
See https://github.com/direnv/direnv/issues/68#issuecomment-2812015043.
See the generated documentation for more information on the bazel_env rule.
Starlark
74.5%
Shell
25.5%