3.2.2 .bazelignore
.bazelignore is how a maintainer tells Bazel that some directories are part of the repository checkout but not part of the tree Bazel should walk while discovering packages. The file lives at the repository root from 0.1.2 Repository Root, entries are root-relative and written one directory per line, and broad target patterns like //... stop descending into those paths.1,2,3
It is a tree filter, not a flag layer
Like .bazelrc in 3.2.1 .bazelrc Hierarchy, .bazelignore changes repository-wide behavior. It does so at a different layer: it does not set flags, it narrows the part of the checkout Bazel should even consider during the loading phase from 2.2.1 Loading, Analysis & Execution. The syntax is intentionally simple: one directory per line, paths relative to the repository root, and no glob semantics.1
A minimal file can look like this:
node_modules
deps/
This works well when the problem is a concrete directory: node_modules, a nested test module, or a local dependency checkout. If the ignored set starts looking like a pattern instead of a short list of names, .bazelignore is probably becoming too blunt.1,4,2,3
When maintainers actually reach for it
.bazelignore is the place for directories from related projects or other build systems that happen to live in the same checkout but should not be treated as part of the Bazel tree.1 In practice, node_modules is the canonical example: in a rules_js setup it is listed in .bazelignore, and npm_translate_lock(...) also receives verify_node_modules_ignored = "//:.bazelignore". This enforces the expectation in repository configuration instead of leaving it as undocumented team knowledge.4
That lesson matters more than the specific node_modules example. .bazelignore is not cleanup for its own sake. It is a boundary declaration. If a directory lives in the checkout but Bazel should not build it or treat it as package-discovery input, say so explicitly in configuration instead of assuming everyone will remember the convention.1,4
Gazelle follows that same boundary. During its Load stage it lists files and subdirectories while excluding anything matched by # gazelle:exclude directives or .bazelignore files, so if a directory is ignored at the repository level, it is also out of scope for Gazelle's normal BUILD-file generation walk.5
Why it keeps coming up with Bzlmod
In migration material, .bazelignore shows up not as housekeeping but as a workaround for real compatibility issues. A common recommendation is to add paths used by local_repository() and local_path_override() to .bazelignore until bazelbuild/bazel#22208 is resolved, with the caveat that this workaround may hurt IDE autocomplete.6
The same family of issues appears with nested test modules. Each nested module directory is added to the parent .bazelignore so bazel build //... and bazel test //... do not descend into it under Bzlmod.2 In a later example, deps/ is ignored not only so top-level //... does not match files from the helper workspace, but also so relative paths used in local_path_override() do not break the main module build.3
That gives you a good maintainer rule: if the repository contains extra trees only to support migration, compatibility testing, or local overrides, decide first whether Bazel should treat them as part of the normal repository. If not, .bazelignore is often the simplest way to say so.6,2,3
Use .bazelignore when Bazel keeps walking into directories that exist in the checkout but do not belong to the Bazel package tree. Keep the file short and explicit. If you start thinking in patterns instead of concrete directory names, you are moving into the REPO.bazel territory introduced in 0.1.2 Repository Root and its ignore_directories() directive.
When REPO.bazel is the better fit
A newer path exists for the same problem: on Bazel 8+, you can use ignore_directories() in REPO.bazel.1,7 Unlike .bazelignore, that directive follows glob() semantics, so it fits cases where the ignored set is pattern-shaped rather than a short hand-maintained list.7
That does not make .bazelignore obsolete. The plain-text file is still a good fit for simple, stable entries. The difference is practical: .bazelignore is the minimal "do not descend here" mechanism, while ignore_directories() is better when the repo already relies on REPO.bazel and needs more structured ignore rules.1,7
Check your understanding · 3 questions
1.A repo has a node_modules directory that should not be part of the Bazel package tree. Where does the maintainer declare this, and what is the effect?
Select one answer
2.True or false about .bazelignore and related tools:
Choose True or False for each sentence
3.When would using ignore_directories() in REPO.bazel be preferable to .bazelignore?
Select one answer
Footnotes
-
Write bazelrc configuration files —
.bazelignoreat the repository root, one root-relative directory per line, with no glob semantics ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
Migrating to Bazel Modules (a.k.a. Bzlmod) - Maintaining Compatibility, Part 3 — parent
.bazelignoreexcludes nested modules frombazel build //...andbazel test //...↩1 ↩2 ↩3 ↩4 -
Migrating to Bazel Modules (a.k.a. Bzlmod) - Maintaining Compatibility, Part 4 —
deps/in.bazelignoreso top-level//...does not enter the helper workspace or breaklocal_path_override()↩1 ↩2 ↩3 ↩4 -
Coding in the Fast Lane with ibazel — practical
node_modulesexample plusverify_node_modules_ignored = "//:.bazelignore"in arules_jssetup ↩1 ↩2 ↩3 -
How Gazelle Works — Load stage excludes files and subdirectories matched by
# gazelle:excludedirectives or.bazelignorefiles ↩ -
Migrating to Bazel Modules (a.k.a. Bzlmod) - The Easy Parts — workaround of adding
local_repository()/local_path_override()paths to.bazelignore, with an IDE-autocomplete caveat ↩1 ↩2 -
REPO.bazel files —
ignore_directories()inREPO.bazel,glob()-style matching, and examples of non-Bazel directories worth excluding ↩1 ↩2 ↩3