keywords: bzlmod
title: ‘Bzlmod Migration Guide’
Due to the shortcomings of WORKSPACE, Bzlmod is replacing the legacy WORKSPACE system. The WORKSPACE file is already disabled in Bazel 8 (late 2024) and will be removed in Bazel 9 (late 2025). This guide helps you migrate your project to Bzlmod and drop WORKSPACE for managing external dependencies.Why migrate to Bzlmod?
- There are many advantages compared to the legacy WORKSPACE system, which helps to ensure a healthy growth of the Bazel ecosystem.
- If your project is a dependency of other projects, migrating to Bzlmod will unblock their migration and make it easier for them to depend on your project.
- Migration to Bzlmod is a necessary step in order to use future Bazel versions (mandatory in Bazel 9).
How to migrate to Bzlmod?
Recommended migration process:- Use migration tool as a helper tool to streamline the migration process as much as possible.
- If there are errors left after using the migration tool, resolve them
manually. For understanding the main differences between concepts inside
WORKSPACEandMODULE.bazelfiles, check WORKSPACE versus Bzlmod section.
WORKSPACE vs Bzlmod
Bazel’s WORKSPACE and Bzlmod offer similar features with different syntax. This section explains how to migrate from specific WORKSPACE functionalities to Bzlmod.Define the root of a Bazel workspace
The WORKSPACE file marks the source root of a Bazel project, this responsibility is replaced by MODULE.bazel in Bazel version 6.3 and later. With Bazel versions prior to 6.3, there should still be aWORKSPACE or WORKSPACE.bazel file at
your workspace root, maybe with comments like:
-
WORKSPACE
Enable Bzlmod in your bazelrc
.bazelrc lets you set flags that apply every time your run Bazel. To enable
Bzlmod, use the --enable_bzlmod flag, and apply it to the common command so
it applies to every command:
-
.bazelrc
Specify repository name for your workspace
-
WORKSPACE
The
workspacefunction is used to specify a repository name for your workspace. This allows a target//foo:barin the workspace to be referenced as@<workspace name>//foo:bar. If not specified, the default repository name for your workspace is__main__. -
Bzlmod
It’s recommended to reference targets in the same workspace with the
//foo:barsyntax without@<repo name>. But if you do need the old syntax , you can use the module name specified by themodulefunction as the repository name. If the module name is different from the needed repository name, you can userepo_nameattribute of themodulefunction to override the repository name.
Fetch external dependencies as Bazel modules
If your dependency is a Bazel project, you should be able to depend on it as a Bazel module when it also adopts Bzlmod.-
WORKSPACE
With WORKSPACE, it’s common to use the
http_archiveorgit_repositoryrepository rules to download the sources of the Bazel project.As you can see, it’s a common pattern that users need to load transitive dependencies from a macro of the dependency. Assume bothbazel_skylibandrules_javadepends onplatform, the exact version of theplatformdependency is determined by the order of the macros. -
Bzlmod
With Bzlmod, as long as your dependency is available in Bazel Central
Registry or your custom Bazel
registry, you can simply depend on it with a
bazel_depdirective.Bzlmod resolves Bazel module dependencies transitively using the MVS algorithm. Therefore, the maximal required version ofplatformis selected automatically.
Override a dependency as a Bazel module
As the root module, you can override Bazel module dependencies in different ways. Please read the overrides section for more information. You can find some example usages in the examples repository.Fetch external dependencies with module extensions
If your dependency is not a Bazel project or not yet available in any Bazel registry, you can introduce it usinguse_repo_rule or module
extensions.
-
WORKSPACE
Download a file using the
http_filerepository rule. -
Bzlmod
With Bzlmod, you can use the
use_repo_ruledirective in your MODULE.bazel file to directly instantiate repos:Under the hood, this is implemented using a module extension. If you need to perform more complex logic than simply invoking a repo rule, you could also implement a module extension yourself. You’ll need to move the definition into a.bzlfile, which also lets you share the definition between WORKSPACE and Bzlmod during the migration period.Implement a module extension to load the dependencies macro. You can define it in the same.bzlfile of the macro, but to keep compatibility with older Bazel versions, it’s better to define it in a separate.bzlfile.To make the repository visible to the root project, you should declare the usages of the module extension and the repository in the MODULE.bazel file.
Resolve conflict external dependencies with module extension
A project can provide a macro that introduces external repositories based on inputs from its callers. But what if there are multiple callers in the dependency graph and they cause a conflict? Assume the projectfoo provides the following macro which takes version as
an argument.
-
WORKSPACE
With WORKSPACE, you can load the macro from
@fooand specify the version of the data dependency you need. Assume you have another dependency@bar, which also depends on@foobut requires a different version of the data dependency.In this case, the end user must carefully adjust the order of macros in the WORKSPACE to get the version they need. This is one of the biggest pain points with WORKSPACE since it doesn’t really provide a sensible way to resolve dependencies. -
Bzlmod
With Bzlmod, the author of project
foocan use module extension to resolve conflicts. For example, let’s assume it makes sense to always select the maximal required version of the data dependency among all Bazel modules.In this case, the root module requires data version2.0, while its dependencybarrequires3.0. The module extension infoocan correctly resolve this conflict and automatically select version3.0for the data dependency.
Integrate third party package manager
Following the last section, since module extension provides a way to collect information from the dependency graph, perform custom logic to resolve dependencies and call repository rules to introduce external repositories, this provides a great way for rules authors to enhance the rulesets that integrate package managers for specific languages. Please read the module extensions page to learn more about how to use module extensions. Here is a list of the rulesets that already adopted Bzlmod to fetch dependencies from different package managers: A minimal example that integrates a pseudo package manager is available at the examples repository.Detect toolchains on the host machine
When Bazel build rules need to detect what toolchains are available on your host machine, they use repository rules to inspect the host machine and generate toolchain info as external repositories.-
WORKSPACE
Given the following repository rule to detect a shell toolchain.
You can load the repository rule in WORKSPACE.
-
Bzlmod
With Bzlmod, you can introduce the same repository using a module extension,
which is similar to introducing the
@data_filerepository in the last section.Then use the extension in the MODULE.bazel file.
Register toolchains & execution platforms
Following the last section, after introducing a repository hosting toolchain information (e.g.local_config_sh), you probably want to register the
toolchain.
-
WORKSPACE
With WORKSPACE, you can register the toolchain in the following ways.
-
You can register the toolchain the
.bzlfile and load the macro in the WORKSPACE file. -
Or register the toolchain in the WORKSPACE file directly.
-
You can register the toolchain the
-
Bzlmod
With Bzlmod, the
register_toolchainsandregister_execution_platformsAPIs are only available in the MODULE.bazel file. You cannot callnative.register_toolchainsin a module extension.
WORKSPACE,
WORKSPACE.bzlmod and each Bazel module’s MODULE.bazel file follow this
order of precedence during toolchain selection (from highest to lowest):
- toolchains and execution platforms registered in the root module’s
MODULE.bazelfile. - toolchains and execution platforms registered in the
WORKSPACEorWORKSPACE.bzlmodfile. - toolchains and execution platforms registered by modules that are (transitive) dependencies of the root module.
- when not using
WORKSPACE.bzlmod: toolchains registered in theWORKSPACEsuffix.
Introduce local repositories
You may need to introduce a dependency as a local repository when you need a local version of the dependency for debugging or you want to incorporate a directory in your workspace as external repository.-
WORKSPACE
With WORKSPACE, this is achieved by two native repository rules,
local_repositoryandnew_local_repository. -
Bzlmod
With Bzlmod, you can use
local_path_overrideto override a module with a local path.Note: Withlocal_path_override, you can only introduce a local directory as a Bazel module, which means it should have a MODULE.bazel file and its transitive dependencies are taken into consideration during dependency resolution. In addition, all module override directives can only be used by the root module. It is also possible to introduce a local repository with module extension. However, you cannot callnative.local_repositoryin module extension, there is ongoing effort on starlarkifying all native repository rules (check #18285 for progress). Then you can call the corresponding starlarklocal_repositoryin a module extension. It’s also trivial to implement a custom version oflocal_repositoryrepository rule if this is a blocking issue for you.
Bind targets
Thebind rule in WORKSPACE is deprecated and
not supported in Bzlmod. It was introduced to give a target an alias in the
special //external package. All users depending on this should migrate away.
For example, if you have
//external:openssl. You can migrate
away from this by:
-
Replace all usages of
//external:opensslwith@my-ssl//src:openssl-lib.- Tip: Use
bazel query --output=build --noenable_bzlmod --enable_workspace [target]command to find relevant info about the target.
- Tip: Use
-
Or use the
aliasbuild rule-
Define the following target in a package (e.g.
//third_party) -
Replace all usages of
//external:opensslwith//third_party:openssl.
-
Define the following target in a package (e.g.
Fetch versus Sync
Fetch and sync commands are used to download external repos locally and keep them updated. Sometimes also to allow building offline using the--nofetch
flag after fetching all repos needed for a build.
- WORKSPACE Sync performs a force fetch for all repositories, or for a specific configured set of repos, while fetch is only used to fetch for a specific target.
-
Bzlmod
The sync command is no longer applicable, but fetch offers
various options.
You can fetch a target, a repository, a set of configured repos or all
repositories involved in your dependency resolution and module extensions.
The fetch result is cached and to force a fetch you must include the
--forceoption during the fetch process.
Manual migration
This section provides useful information and guidance for your manual Bzlmod migration process. For more automatized migration process, check recommended migration process section.Know your dependencies in WORKSPACE
The first step of migration is to understand what dependencies you have. It could be hard to figure out what exact dependencies are introduced in the WORKSPACE file because transitive dependencies are often loaded with*_deps
macros.
Inspect external dependency with workspace resolved file
Fortunately, the flag--experimental_repository_resolved_file
can help. This flag essentially generates a “lock file” of all fetched external
dependencies in your last Bazel command. You can find more details in this blog
post.
It can be used in two ways:
-
To fetch info of external dependencies needed for building certain targets.
-
To fetch info of all external dependencies defined in the WORKSPACE file.
With the
bazel synccommand, you can fetch all dependencies defined in the WORKSPACE file, which include:bindusagesregister_toolchains®ister_execution_platformsusages
resolved.bzl file.
Inspect external dependency with bazel query
You may also know bazel query can be used for inspecting repository rules with
Built-in default dependencies
If you check the file generated by--experimental_repository_resolved_file,
you are going to find many dependencies that are not defined in your WORKSPACE.
This is because Bazel in fact adds prefixes and suffixes to the user’s WORKSPACE
file content to inject some default dependencies, which are usually required by
native rules (e.g. @bazel_tools, @platforms and @remote_java_tools). With
Bzlmod, those dependencies are introduced with a built-in module
bazel_tools , which is a default dependency for every other
Bazel module.
Hybrid mode for gradual migration
Bzlmod and WORKSPACE can work side by side, which allows migrating dependencies from the WORKSPACE file to Bzlmod to be a gradual process. Note: In practice, loading ”*_deps” macros in WORKSPACE often causes confusions with Bzlmod dependencies, therefore we recommend starting with a WORKSPACE.bzlmod file and avoid loading transitive dependencies with macros.WORKSPACE.bzlmod
During the migration, Bazel users may need to switch between builds with and without Bzlmod enabled. WORKSPACE.bzlmod support is implemented to make the process smoother. WORKSPACE.bzlmod has the exact same syntax as WORKSPACE. When Bzlmod is enabled, if a WORKSPACE.bzlmod file also exists at the workspace root:WORKSPACE.bzlmodtakes effect and the content ofWORKSPACEis ignored.- No prefixes or suffixes are added to the WORKSPACE.bzlmod file.
- When Bzlmod is disabled, you fall back to fetching dependencies from the original WORKSPACE file.
- When Bzlmod is enabled, you can better track what dependencies are left to migrate with WORKSPACE.bzlmod.
Repository visibility
Bzlmod is able to control which other repositories are visible from a given repository, check repository names and strict deps for more details. Here is a summary of repository visibilities from different types of repositories when also taking WORKSPACE into consideration.| From the main repo | From Bazel module repos | From module extension repos | From WORKSPACE repos | |
|---|---|---|---|---|
| The main repo | Visible | If the root module is a direct dependency | If the root module is a direct dependency of the module hosting the module extension | Visible |
| Bazel module repos | Direct deps | Direct deps | Direct deps of the module hosting the module extension | Direct deps of the root module |
| Module extension repos | Direct deps | Direct deps | Direct deps of the module hosting the module extension + all repos generated by the same module extension | Direct deps of the root module |
| WORKSPACE Repos | All visible | Not visible | Not visible | All visible |
@foo is defined in WORKSPACE and
@foo is also used as an apparent repository
name in MODULE.bazel, then @foo
refers to the one introduced in MODULE.bazel.
Note: For a module extension generated repository @bar, if @foo is used as
an apparent repository name of
another repository generated by the same module extension and direct
dependencies of the module hosting the module extension, then for repository
@bar, @foo refers to the latter.
Manual migration process
A typical Bzlmod migration process can look like this:- Understand what dependencies you have in WORKSPACE.
- Add an empty MODULE.bazel file at your project root.
- Add an empty WORKSPACE.bzlmod file to override the WORKSPACE file content.
- Build your targets with Bzlmod enabled and check which repository is missing.
- Check the definition of the missing repository in the resolved dependency file.
- Introduce the missing dependency as a Bazel module, through a module extension, or leave it in the WORKSPACE.bzlmod for later migration.
- Go back to 4 and repeat until all dependencies are available.
Publish Bazel modules
If your Bazel project is a dependency for other projects, you can publish your project in the Bazel Central Registry. To be able to check in your project in the BCR, you need a source archive URL of the project. Take note of a few things when creating the source archive:- Make sure the archive is pointing to a specific version. The BCR can only accept versioned source archives because Bzlmod needs to conduct version comparison during dependency resolution.
-
Make sure the archive URL is stable.
Bazel verifies the content of the archive by a hash value, so you should
make sure the checksum of the downloaded file never changes. If the URL is
from GitHub, please create and upload a release archive in the release page.
GitHub isn’t going to guarantee the checksum of source archives generated on
demand. In short, URLs in the form of
https://github.com/<org>/<repo>/releases/download/...is considered stable whilehttps://github.com/<org>/<repo>/archive/...is not. Check GitHub Archive Checksum Outage for more context. -
Make sure the source tree follows the layout of the original repository.
In case your repository is very large and you want to create a distribution
archive with reduced size by stripping out unnecessary sources, please make
sure the stripped source tree is a subset of the original source tree. This
makes it easier for end users to override the module to a non-release
version by
archive_overrideandgit_override. - Include a test module in a subdirectory that tests your most common APIs. A test module is a Bazel project with its own WORKSPACE and MODULE.bazel file located in a subdirectory of the source archive which depends on the actual module to be published. It should contain examples or some integration tests that cover your most common APIs. Check test module to learn how to set it up.
Best practices
This section documents a few best practices you should follow for better managing your external dependencies.Split targets into different packages to avoid fetching unnecessary dependencies.
Check #12835, where dev dependencies for tests are forced to be fetched unnecessarily for building targets that don’t need them. This is actually not Bzlmod specific, but following this practices makes it easier to specify dev dependencies correctly.Specify dev dependencies
You can set thedev_dependency attribute to true for
bazel_dep and
use_extension directives so that
they don’t propagate to dependent projects. As the root module, you can use the
--ignore_dev_dependency flag to verify if your targets
still build without dev dependencies and overrides.