Skip to main content

Migrate to bzlmod

Background: Bazel's dependency story

Historically, Bazel came from Blaze, the monorepo build tool developed within Google's monorepo, "google3". google3 is fully self-contained, even bootstrapping compilers from source. It has no "third-party" dependencies, aside from those that engineers lovingly vendor into the third_party folder. For this reason, Blaze has no affordances for fetching external packages. The google3/WORKSPACE file is short and meaningless to Googlers as there's very little repo-wide configuration.

However when releasing Bazel, it was clear that other users needed external packages. As much as Google is opinionated, asking everyone to vendor their dependencies in every language is a non-starter.

Thus, concepts such as "repository rules" were invented to give the beginning of Bazel's analysis phase the ability to fetch archives over HTTP, and perform post-install steps on the code that was fetched. As evidence of this lineage, note that Bazel's evaluation model doesn't mention the "fetch" phase; this is because Blaze has no such concept.

However, the implementation of the "fetch" phase in the WORKSPACE file was fatally flawed: it has no dependency resolution step. In fact it doesn't understand transitive dependencies at all. To workaround this omission, developers of Bazel rulesets created macros that wrap the repository_rules, such as my_rules_dependencies(). Calling this from the end-user's WORKSPACE file causes those dependencies to fetch. This doesn't work well because the first fetch wins. If my_rules_dependencies() brings you rules_python@0.1.0 then you get errors later in the build about rules_python not working the way you expect, and it's nearly impossible to discover where version 0.1.0 comes from.

The solution to these problems is to replace most or all of the content of your WORKSPACE file with a new file introduced for Bazel 6, MODULE.bazel. Starting with Bazel 7, the new file is enabled by default, and in Bazel 8 the team plans to disable reading WORKSPACE unless you supply an opt-in flag; however that flag will be removed in Bazel 9.

This means every Bazel user is forced to make this migration eventually.

more reading

For more background, see our blog post from before Bazel 6 was released.

Common migration path

  1. Upgrade to latest Bazel first. It's possible to do bzlmod on Bazel 6 and then upgrade Bazel after. However we recommend this order because bzlmod bugfixes have been landing, so the bzlmod-first migration might need extra workarounds.
  2. At first, disable new Bazel 7 flags. Makes the Bazel 7 upgrade less risky and "do one thing at a time". These include:
    • Add --noenable_bzlmod
    • Remove --noexperimental_check_external_repository_files which is broken with BwoB
    • Add --@io_bazel_rules_docker//transitions:enable=false if using rules_docker, issue
    • Add --noincompatible_sandbox_hermetic_tmp issue
  3. Land .bazelversion upgrade and wait a few days to "bake" since there may be developer machines or CD machines that have hard-coded Bazel version.
  4. Remove --noenable_bzlmod and try analyzing (bazel build --nobuild //...). Bazel will create a MODULE.bazel file. Follow guidance below to make the minimal changes to get this green.
  5. Rinse-and-repeat to burn down the content of WORKSPACE and replace with MODULE.bazel.

Detailed migration instructions and notes


This section is available to Aspect customers only, at

If you don't have a login, you can register for Aspect Pro. Email to learn more.