Workspaces in Bazel
This guide explains how to set up and manage Bazel workspaces effectively. While older Bazel projects used a WORKSPACE file for configuration, modern Bazel uses a more predictable MODULE.bazel file and the Bazel Central Registry for dependency management.
What is a Workspace?
A Bazel workspace is a directory that contains your project's source files and build configuration. At its core, it needs:
- Source files
- BUILD files (defining targets)
- A module definition file (MODULE.bazel)
- Configuration files (.bazelrc, .bazelignore)
Understanding Rulesets
Bazel's core functionality is intentionally minimal - it provides a framework for building and testing code, but doesn't know how to build specific languages or create specific outputs. This is where rulesets come in.
A ruleset is a collection of build rules that teach Bazel how to build certain types of targets. For example:
rules_pythonprovides rules likepy_binaryandpy_testfor Python coderules_goprovides rules likego_binaryandgo_libraryfor Go coderules_dockerprovides rules likecontainer_imagefor Docker containers
You'll need different rulesets depending on what you're building. Most modern rulesets are available through the Bazel Central Registry (BCR), which is Bazel's official ruleset registry. The BCR provides:
- Verified and tested versions of rulesets
- Automatic dependency management
- Compatibility information between rulesets
- Security through checksums and verification
The BCR is central to modern Bazel development - it's where you'll find most of the rules you need, and it makes dependency management much simpler than the legacy WORKSPACE approach. Learn more about how the BCR works and how to publish your own rulesets in our Bazel Central Registry guide.
Module Definition
Modern Approach (Recommended)
The MODULE.bazel file is a configuration file that Bazel evaluates to understand your project's structure. Like BUILD files, it contains declarations that Bazel reads and processes - it does not execute functions or run code. This makes it predictable and safe.
# MODULE.bazel is declarative - these are not function calls
module(
name = "my_project",
version = "1.0",
)
# Import rulesets from the Bazel Central Registry
bazel_dep(name = "rules_python", version = "0.27.1")
bazel_dep(name = "rules_go", version = "0.42.0")
# Even toolchain registration is declarative
register_toolchains("//toolchains:my_toolchain")The module system (Bzlmod) provides:
- Pure configuration without execution
- Automatic dependency resolution
- Central registry integration
- Predictable evaluation order
Working with Legacy Documentation
When searching for Bazel documentation or examples, you'll often find instructions like this:
# Add this to your WORKSPACE file
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_foo",
urls = ["https://github.com/example/rules_foo/archive/v1.0.0.zip"],
sha256 = "...",
)
# Then load and call the ruleset's setup function
load("@rules_foo//:deps.bzl", "rules_foo_dependencies")
rules_foo_dependencies()When you encounter such documentation:
Check the BCR First
- Visit registry.bazel.build and for more info, check our BCR guide
- Search for the ruleset by name
- Check if there's a newer version available
- Review the ruleset's documentation and compatibility information
If the Ruleset is in BCR
python# In MODULE.bazel - much simpler! bazel_dep(name = "rules_foo", version = "1.0.0")- No need for manual downloads
- No need for setup functions
- Dependencies are handled automatically
If the Ruleset is Not in BCR
- Check the ruleset's documentation for Bzlmod support
- Look for alternative rulesets in BCR
- Consider using multiple_version_override:python
# In MODULE.bazel bazel_dep(name = "rules_foo", version = "0.0.0") # Dummy version single_version_override( module_name = "rules_foo", patches = ["//patches:rules_foo.patch"], patch_strip = 1, ) - As a last resort, you might need a hybrid approach with both MODULE.bazel and WORKSPACE
Common Patterns in Legacy Docs
http_archivecalls indicate external dependenciesload()statements often need to be reordered in WORKSPACE- Setup functions (like
foo_dependencies()) are usually automatic with Bzlmod - SHA hashes in WORKSPACE become unnecessary with BCR
Legacy Approach (WORKSPACE)
The older WORKSPACE file is a Starlark script that actively executes code to set up your workspace. Each function call in a WORKSPACE file:
- Actually runs when the file is loaded
- Can download files and create directories
- Must be ordered correctly to work
- Can have complex side effects
Example of the legacy approach (not recommended for new projects):
# WORKSPACE functions are executed in order
workspace(name = "my_project") # This runs first
# This function runs and downloads files
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_cc",
urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.1/rules_cc-0.0.1.tar.gz"],
sha256 = "...",
)
# This must come after http_archive because it uses its results
load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies")
rules_cc_dependencies() # This function runs and sets up more repositoriesConfiguration Files
.bazelrc
The .bazelrc file contains default command-line options:
# Common settings
build --incompatible_enable_cc_toolchain_resolution
test --test_output=errors.bazelignore
The .bazelignore file specifies directories to exclude from the build:
# Generated or external directories
node_modules
dist
targetProject Structure
A modern Bazel workspace typically looks like this:
my_project/
├── MODULE.bazel # Module definition (replaces WORKSPACE)
├── .bazelrc # Build settings
├── .bazelversion # Pinned Bazel version
├── BUILD.bazel # Root package build targets
├── src/ # Source code
│ └── BUILD.bazel # Package build targets
└── toolchains/ # Custom toolchains (if needed)
└── BUILD.bazel # Toolchain definitionsBest Practices
Use MODULE.bazel
- Prefer the module system over WORKSPACE
- Use the Bazel Central Registry
- Keep dependencies minimal
Workspace Organization
- Use meaningful package names
- Keep the root clean
- Group related files
Version Control
- Version control .bazelrc
- Include .bazelversion
- Document setup requirements
Dependencies
- Use exact versions
- Keep dependencies up to date
- Document version requirements
