Bazel keeps a full output tree per workspace, and it is enormous
The output base holds every build artefact and external dependency for each workspace. Where it is, what bazel clean does, and the flag that matters.
Bazel is fast because it keeps everything: every action output, every external dependency, every intermediate artefact, in an output base per workspace stored outside the workspace itself. That is why builds are quick and why the disk fills.
Find the output bases
bazel info output_base
bazel info repository_cache
du -sh ~/.cache/bazel/* 2>/dev/null | sort -h | tailOn macOS the output base usually lives under a cache directory keyed by a hash of the workspace path, which is why moving or renaming a project silently creates a second one and leaves the first behind forever.
The commands, and what each removes
| Command | Removes | Cost |
|---|---|---|
bazel clean | Build outputs for this workspace | Full rebuild |
bazel clean --expunge | Output base including external repos | Rebuild plus redownload |
bazel shutdown | Stops the server holding the tree | None |
| Deleting an orphaned output base | Everything for a workspace that no longer exists | None |
Orphans are the real win
Because the output base is keyed to the workspace path, every repository you have deleted, renamed or moved has left a complete output tree behind, and nothing will ever reclaim it. Those are free gigabytes:
for d in ~/.cache/bazel/*/*/; do
ws=$(cat "$d/DO_NOT_BUILD_HERE" 2>/dev/null)
[ -n "$ws" ] && [ ! -d "$ws" ] && du -sh "$d"
doneEach line printed is an output base whose workspace no longer exists on disk. Removing those costs nothing at all, since there is no project left to rebuild.
The repository cache is shared, and worth keeping
External dependencies downloaded by Bazel go to a shared repository cache, which is the one part you want to keep: it is what makes a fresh clone build quickly. Expunging removes the workspace's copies but the shared cache remains, so it is cheaper than it looks.
The same pattern of a build tool keeping a per project tree shows up in Gradle's caches and Rust target folders, and the orphan problem is the same one behind caches for IDE versions you no longer have.
Common questions
Where is Bazel's output base on a Mac?
Under a cache directory keyed by a hash of the workspace path, which bazel info output_base prints. It is outside your project, which is why deleting the project does not free the space.
What is the difference between bazel clean and clean --expunge?
clean removes build outputs and keeps external repositories, so a rebuild is faster. clean --expunge removes the whole output base including external repos, which also means redownloading them.
Why does Bazel use so much disk space?
Because it keeps every action output and every external dependency per workspace, and moving or renaming a project creates a new output base while leaving the old one behind permanently.
Can I delete old Bazel output bases?
Yes. Any output base whose workspace no longer exists on disk is dead weight and can be removed with no cost, since there is nothing left to rebuild.