Find the GC roots quietly pinning gigabytes of your Nix store, and clear them out safely.
Developed and maintained by Applicative Systems
nix-collect-garbage only frees what nothing points to. The trouble is that a
lot does point to things — every nix build result, every direnv
environment, every old profile generation is a GC root that quietly pins its
whole dependency closure on disk. Months later you run garbage collection,
it frees almost nothing, and you have no idea why.
gcan answers that question. It looks at all your GC roots and shows you, sorted
biggest-first, how much disk each one is really keeping alive, where it lives,
and how long it's been there — so you can see what to let go of and clear it out
safely.
SIZE AGE ROOTS LOCATION
4.8GB 6d 7 /home/you/src/old-project/.direnv/ (direnv)
3.7GB 14d 30 /home/you/src/training/.direnv/ (direnv)
1.2GB 90d 1 /home/you/src/experiment/result-1
741MB 21d 1 ~/.local/state/nix/profiles/profile-51-link
----
26GB TOTAL reclaimable
- Real closure sizes: roots ranked by the disk they're keeping alive, not their on-disk size.
- direnv projects grouped: one line per project with the true cost, not 30 cryptic hashes.
- Interactive TUI: browse, sort, and delete roots without leaving the keyboard.
- Filter by size and age:
--min-size 1G --min-age 30dto focus on what's worth clearing. - Safe by default: never offers system, booted, or current generations for deletion.
- Scriptable: JSON output, paths-only output, and exit codes that play well with shell pipelines.
- "
nix-collect-garbagebarely freed anything." Something is pinning the store.gcanshows you exactly what, ranked by size. - direnv pack-rats. Every project with a
.direnvkeeps a full copy of its flake inputs alive. A repo you haven't opened in three months can still be holding gigabytes.gcangroups all of a project's direnv roots into one line, so you see the true cost per project — not 30 cryptic hashes. - Forgotten
resultlinks. Thatresultsymlink from anix buildyou ran once is pinning its entire closure forever.gcanlists them with their age. - Old generations. Stale profile and home-manager generations add up;
gcansurfaces the big, old ones. - Spring cleaning. Sort by age, find what you haven't touched in months, and reclaim it in one pass.
gcan has four subcommands:
list: show rootsdelete: remove themtui: do it interactivelysummary: store-wide totals
List the biggest reclaimable roots:
gcan listNarrow it down to the things actually worth clearing — say, anything over 1 GB that you haven't touched in a month, oldest first:
gcan list --min-size 1G --min-age 30d --sort ageBy default list only shows roots you can safely delete. Add --all to see
the full picture, including the live system and other protected roots (these are
marked and can never be deleted). You can also sort by size, name, or age
(--sort) and flip the order with -r.
gcan tuiThis opens a full-screen view you can drive with the keyboard:
↑/↓ (or j/k) move s sort by size t show all / only deletable
Home/End jump n sort by name r reverse the order
D delete a sort by age q / Esc quit
Press D on a root to delete it — gcan asks for confirmation, removes it, and
refreshes the list on the spot. You can start it narrowed down too, e.g.
gcan tui --min-size 1G.
# Delete everything ≥2 GB and older than 30 days, after a confirmation prompt:
gcan delete --min-size 2G --min-age 30d
# Delete and reclaim the space in one go (runs nix-collect-garbage for you):
gcan delete --min-size 2G --min-age 30d --gc
# Or pipe the exact symlinks somewhere and remove them yourself:
gcan list --min-size 1G --format paths | xargs rmWhen you don't want the per-root table, just the store-wide totals, use
summary:
$ gcan summary
Collectable now (dead paths): (skipped; pass --collectable)
Reclaimable (prune safe roots): 79GB
Pinned — system: 33GB
Pinned — user (beyond system): 16GB- Reclaimable: what pruning every safe root would let GC free (the same
total as
list). - Pinned — system: the closure pinned by system roots (the running system, profile generations). Only the root user can free it.
- Pinned — user: what your roots pin beyond the system closure, so the shared base (glibc, etc.) is never double-counted.
These all read from gcan's cache, so a warm summary returns in well under a
second. The collectable-now figure — what a plain nix-collect-garbage
would free this instant — is off by default, because it needs a full-store
dead-path scan that can't be cached and takes tens of seconds. Ask for it with
--collectable:
gcan summary --collectableAdd --format json for a machine-readable snapshot to feed dashboards or a daemon.
Deleting a root just removes the link that was pinning the data — it tells Nix the data is no longer wanted. To actually give the disk space back, run garbage collection afterwards:
nix-collect-garbage(or pass --gc to gcan delete to have it run for you).
gcan will never offer to delete something that would break your system:
- It only lists roots you own and can remove — never root-owned system roots.
- The live system, the booted system, and your current home-manager generation
(and anything else marked
current-*/booted-*) are always protected. - Deletions in the TUI and via
gcan deleteask for confirmation first.
You can also export the full inventory as JSON (gcan list --all --format json)
to feed into your own scripts or dashboards.
We offer commercial support to help you get the most out of Nix and your infrastructure:
- Nix Infrastructure Consulting: audit, design, and tune your build and deployment pipelines.
- Custom Development: tailored tooling for your stack.
- Training: hands-on Nix and NixOS for your team.
- Integration Support: help wiring Nix and friends into CI and developer workflows.
Contact us:
- Join our Matrix channel
- Report issues on GitHub
- Contribute via Pull Requests
