Repository Root
With Bazelisk and .bazelversion handling the build tool itself (0.1.1 Bazelisk & .bazelversion), the next file to look for is the one that marks where the project begins. Every Bazel project needs a root — a single directory that anchors the entire build. Bazel finds this root by looking for a boundary marker file1. Without one, Bazel doesn't know where your project begins, and no command will work.
The Boundary Marker Files
Three file types can serve as a repository boundary marker2:
| File | Purpose |
|---|---|
MODULE.bazel | The default since Bazel 7.0. Marks the root and declares dependencies. |
REPO.bazel | Also marks the root. Optionally sets repo-wide defaults. |
WORKSPACE / WORKSPACE.bazel | Legacy (pre-Bazel 9). Removed in Bazel 93. |
Multiple marker files can coexist in the same directory2. In practice, a modern project usually has a MODULE.bazel at the root and may also have a REPO.bazel beside it.
A typical project root looks like this:
my-project/
├── .bazelversion
├── .git/
├── MODULE.bazel
├── REPO.bazel (optional)
├── BUILD.bazel
├── app/
│ ├── BUILD.bazel
│ └── main.cc
└── lib/
├── BUILD.bazel
└── utils.cc
The .bazelversion file from the previous article, a boundary marker such as MODULE.bazel, and BUILD.bazel files in directories — which create packages, covered next in 0.1.3 Package.
MODULE.bazel
This file serves two purposes4:
- Root marker — tells Bazel where the repository begins.
- Dependency declaration — lists external modules the project depends on.
A minimal MODULE.bazel can be empty — just its presence is enough to mark the root. A typical one looks like this:
module(name = "my-project", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "platforms", version = "1.0.0")
How bazel_dep() works, version resolution, and the Bazel Central Registry are covered in 3.1 Dependency Management. At this stage, just know that MODULE.bazel sits at the root and anchors everything else.
REPO.bazel
REPO.bazel is also a repository boundary file2. Like MODULE.bazel, it can be empty and still mark the root. Its extra job is to set repo-wide defaults for all build targets. For example, applying a common license to all targets:
repo(
default_package_metadata = ["//:my_license"],
)
It also provides ignore_directories() to exclude directories from Bazel's view (e.g., node_modules)5 — the modern replacement for .bazelignore, covered in 3.2.2 .bazelignore.
In modern Bzlmod-based projects, REPO.bazel usually complements MODULE.bazel rather than replacing it. MODULE.bazel carries module and dependency declarations; REPO.bazel carries repo-wide defaults. The important mental model is simply that either file counts as a boundary marker.
WORKSPACE (Removed)
Before Bzlmod, the WORKSPACE file was Bazel's only root marker and dependency management mechanism. It required all transitive dependencies to be declared manually, leading to fragile and verbose configurations2. It was replaced by MODULE.bazel, disabled by default in Bazel 8, and fully removed in Bazel 93. A variant named WORKSPACE.bazel also existed, for the same case-sensitivity reasons that BUILD.bazel is preferred over BUILD (covered in 0.1.3 Package).
If you encounter a WORKSPACE file in an existing project, it's a sign the project hasn't migrated to Bzlmod yet — see M1 WORKSPACE → Bzlmod.
One Bazel Repo ≈ One Git Repo
A Bazel repository and a Git repository are typically one-to-one — the MODULE.bazel sits at the same level as .git/5. Nesting multiple Bazel repositories inside one Git repository, or placing a Bazel repository in a subdirectory, is possible but unusual and generally discouraged4. Bazel calls this directory tree a repository. The related term workspace, common in older documentation, has a slightly different meaning — the distinction becomes relevant when working with external dependencies and labels (0.2.1 Label Anatomy).
In modern projects, MODULE.bazel is usually the first root file you look for. A REPO.bazel may sit beside it and also counts as a boundary marker. Once Bazel finds that root, all packages, labels, and build paths are resolved relative to it.
1.Which file is the default repository boundary marker in modern Bazel projects?
2.What is the extra purpose of REPO.bazel beyond marking the root?
3.Can a minimal MODULE.bazel file be completely empty?
Footnotes
-
Repositories, workspaces, packages, and targets — Authoritative definitions of repositories, workspaces, and their relationship ↩
-
External dependencies overview — Boundary marker files (MODULE.bazel, REPO.bazel, WORKSPACE), REPO.bazel syntax, and legacy WORKSPACE shortcomings ↩1 ↩2 ↩3 ↩4
-
Bzlmod Migration Guide — WORKSPACE removal timeline: MODULE.bazel default since 7.0, WORKSPACE disabled in 8, removed in 9 ↩1 ↩2
-
Bazel Training 101 (Part 7): Bazel Modules (MODULE.bazel) — MODULE.bazel as root marker and dependency description, one module per repository ↩1 ↩2
-
Bazel 101 Training (Part 6): Create a repository — Repository structure, REPO.bazel for ignoring directories, Bazel-Git repo correspondence ↩1 ↩2