3.2.1 .bazelrc Hierarchy
.bazelrc is where a maintainer turns ad hoc CLI flags into repeatable project policy. The important skill is not memorizing one more flag, but deciding which layer should own it: checked-in workspace defaults for the team, personal overrides for one machine, and command-line flags for one invocation. Bazel reads rc files in a defined order and then applies command-specific blocks by specificity, so placement determines both behavior and who gets to override it.1,2
More-specific blocks override looser ones. Line order still matters when specificity ties. test inherits build, then may override it.
There are two hierarchies
The first hierarchy is across files. Bazel looks for a system rc, the workspace .bazelrc next to the repository root marker from 0.1.2 Repository Root, the home rc, any paths injected through BAZELRC, and any files passed with repeated --bazelrc= startup flags. Later files override earlier ones, and explicit command-line flags override everything loaded from rc files.1,3,4
For maintainers, the workspace rc is the shared contract. That is where the repo records defaults every developer and CI job should inherit. Home or user-specific rc files are for local preferences and machine-specific settings, not for policy the whole repo depends on.2,3
Inside one file, specificity still matters
The second hierarchy is inside a single rc file. Each line starts with a scope such as startup, common, build, or test. common applies to every command that understands the flag. Command-specific lines are more specific, and inheritance matters: test inherits from build, so a test line can override the same flag from a build line.1
That is why "last line wins" is only half the rule. Order matters among equal-specificity lines, but Bazel still applies more-specific command blocks over less-specific ones. A good maintainer pattern is: put genuinely global defaults in common, then narrow only where behavior should really differ between build, test, query, or named configs.1,5
Named configs are opt-in overlays
A stanza like the following defines a named config:
build:memcheck --strip=never --test_timeout=3600
Nothing in that line applies until --config=memcheck is in effect for the current command, whether that --config came from the command line or from another rc entry.1,4 This is the clean way to separate always-on policy from situation-specific behavior. Shared defaults stay in plain common or build lines. Release-only or CI-only knobs live behind named configs, such as putting --workspace_status_command under build:release instead of paying the stamping cost on every local build.6 The metadata injected by that release config belongs to 3.2.3 Stamping & Build Metadata. The maintainer-workspace .bazelrc is a minimal runnable version: build:ci / test:ci for CI defaults, build:release for release-only knobs, and build:linux / build:macos as platform-aliasing overlays activated by --config=linux or --config=macos. For just the rc-precedence slice of the same idea, the bazelrc-layering snippet's .bazelrc keeps a single build:ci / test:ci block (no platform or release overlays), so it is small enough to read alongside the bazel build --announce_rc --config=ci //app:app output the snippet pins.
Platform-specific rc blocks look similar but are a separate mechanism. Build/test/query blocks such as build:linux become automatic host-OS overlays when platform-specific config is enabled. Startup options have their own always-on form: startup:linux, startup:macos, startup:windows, startup:freebsd, and startup:openbsd apply on the matching host without needing --enable_platform_specific_config.1
A repo pattern that scales
One maintainable pattern is:
import %workspace%/tools/preset.bazelrc
try-import %workspace%/user.bazelrc
That short snippet encodes a lot of policy. Import curated or generated presets near the top of the workspace rc, keep project-specific overrides below them, and end with try-import %workspace%/user.bazelrc so each developer can add repo-local preferences without committing them.2,5,7 Because imported files take effect at the point where they are imported, later lines in the main .bazelrc can intentionally override the preset, and the final try-import gives the local user the last word.1,7 The maintainer-workspace .bazelrc ends with exactly that try-import line, paired with a checked-in user.bazelrc.example developers can copy without committing the result.
Shared hermeticity and reproducibility defaults belong in the checked-in workspace rc, which is where teams usually enforce the flags collected in 3.2.6 Hermeticity Settings. Bazel credential helpers are another example of settings Bazel expects as rc or CLI flags rather than in BUILD logic.8 If you need to intercept the Bazel invocation before normal flag processing, that is wrapper territory and belongs in 3.2.8 tools/bazel Wrapper.
Prefer common over copy-pasted command blocks
One recurring failure mode is setting the same repo-wide flag under build, test, and query separately, then forgetting one command or giving two commands conflicting values. This drift is a practical source of analysis-cache discards in scripted workflows. Lean on common for flags that should follow every applicable command.5
A good habit is to keep presets imported untouched near the top and put project-specific overrides below them. That way preset updates arrive as reviewable diffs, while the repo still owns its own policy decisions.5,7
Treat .bazelrc as layered policy, not as a dump of random flags. Put shared defaults in the workspace file, keep opt-in behavior behind named configs, reserve personal overrides for a git-ignored imported file, and debug surprises by asking which layer set the flag before you change the flag itself. Once the source of a flag is clear, the next question is the flag surface itself, which continues in 3.2.4 Command Line Flags.
The last two layers are usually tooling-owned
BAZELRC can inject additional rc paths from the environment, and repeated --bazelrc= startup flags can add still more files.1 These layers are useful in CI launchers, wrappers, or clean-room release scripts. For ordinary project defaults, the workspace .bazelrc is still the right home.1,2
When the active configuration feels mysterious, --announce_rc is the fast way to see which rc files contributed options. If you need a clean baseline, --ignore_all_rc_files bypasses the normal search chain. After that, add only the --bazelrc= files you intentionally want, or disable individual default layers with --nosystem_rc, --noworkspace_rc, and --nohome_rc while keeping the rest.1,4,9 The bazelrc-layering snippet pins both flags as runnable commands and ships an assertion script that captures the --announce_rc output and verifies the workspace .bazelrc is one of the files Bazel reports loading.
For the narrower question of how named configs appear in normalized flag output, the canonicalize-flags snippet keeps a tiny .bazelrc around build:ci so 3.2.4 Command Line Flags can show that --config=ci is preserved rather than expanded.
Check your understanding · 3 questions
1.A flag appears in both a build line and a test line in .bazelrc. Which value wins when running bazel test?
Select one answer
2.True or false about .bazelrc layering:
Choose True or False for each sentence
3.Match each .bazelrc concern to the correct layer:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
Footnotes
-
Write bazelrc configuration files — file-location hierarchy, imports, option specificity, named configs, platform-specific blocks including always-on
startup:<os>options, and--ignore_all_rc_files↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
Best Practices — workspace
.bazelrcas project policy andtry-import %workspace%/user.bazelrcfor git-ignored per-user overrides ↩1 ↩2 ↩3 ↩4 -
Bazel Training 101 (Part 8): Flags — practical rc-file layering, repository vs home rc roles, and
common/ command scoping ↩1 ↩2 -
Commands and Options —
--[no]announce_rcand--configcommand semantics ↩1 ↩2 ↩3 -
Better Bazel Flag Defaults — why
commonreduces config drift, how presets layer into a repo.bazelrc, and theuser.bazelrcconvention ↩1 ↩2 ↩3 ↩4 -
.bazelrc flags you should enable — keep release-only stamping behind
build:releaseinstead of making it a default for every build ↩ -
bazelrc-preset.bzl — Generated .bazelrc Presets for Bazel — import presets at the top, override locally below them, and
try-import %workspace%/user.bazelrclast ↩1 ↩2 ↩3 -
Configuring Bazel's Credential Helper — credential-helper flags belong in
.bazelrcor on the Bazel command line ↩ -
Bazel flag cheat sheet — practical use of
--announce_rc,--config, and--bazelrcwhile debugging active defaults ↩