with apologies

Back to my Roots

· 2 min read · November 05, 2025 · #nixos #tech

This is another one of those that’s largely documenting something for myself, but I’m sharing it in case others find it useful. And because self-aggrandisement is what it’s all about these days amirite?

I recently had issues because my NixOS installation ran out of space in the root partition wherein lies /nix/store/, the read-only filesystem containing the Nix store. It’s a pain when this runs out of space because then upgrading gets tricky—due to rollback support, there’s a general tendency to need enough space for at least all the new stuff in the new version in addition to the old versions.

I’d done all the usual garbage collection to try to free up as much space as possible—but still not enough free. What to do? This had used to work so I figured it was probably something to do with some content that should be being garbage collected but wasn’t—I’ve had that problem before when playing around with flakes. Some searching around showed that the solution was actually relatively straightforward.

First, find the roots – these are linked in /nix/var/nix/gcroots – then resolve them and use nix path-info --closure-size to list all the closures with their real sizes. Something like:

#!/usr/bin/env bash

for link in $(find /nix/var/nix/gcroots -type l -readable); do
  res="${res}
$(readlink "${link}") $(nix path-info --closure-size -sh "${link}")"
done
echo "$res" | sort -h -k3,3

This gave me the following (line numbers added after the fact):

1/nix/var/nix/profiles/default-1-link /nix/store/57904rflsfh46y7fxi62rzc7sidc9vmq-user-environment 0.3 KiB 0.5 KiB
2/nix/var/nix/profiles/per-user/mort/channels-2-link /nix/store/57904rflsfh46y7fxi62rzc7sidc9vmq-user-environment 0.3 KiB 0.5 KiB
3/nix/var/nix/profiles/per-user/root/channels-117-link /nix/store/fnlgy7im4rcd4ssh8xxy0kcr3aac7paf-user-environment 0.8 KiB 394.1 MiB
4/home/mort/.cache/nix/flake-registry.json /nix/store/pc6y12ccrlvmgfbp0rs34cbj8mbsyyvk-flake-registry.json 7.3 KiB 7.3 KiB
5/home/mort/.local/state/nix/profiles/home-manager-425-link /nix/store/p8y1zr968ajjnva0whznygvqv09fvdd2-home-manager-generation 17.5 KiB 14.1 GiB
6/home/mort/.local/state/home-manager/gcroots/current-home /nix/store/kmb5w2qymnnhqv3wz3k1nw0psp2qqgmx-home-manager-generation 17.6 KiB 3.0 GiB
7/nix/var/nix/profiles/system-931-link /nix/store/sinifrppd8m6kah30wywc27adyzqhgzy-nixos-system-greyjay-25.11.20251028.08dacfc 18.5 KiB 8.4 GiB
8/run/booted-system /nix/store/sinifrppd8m6kah30wywc27adyzqhgzy-nixos-system-greyjay-25.11.20251028.08dacfc 18.5 KiB 8.4 GiB
9/run/current-system /nix/store/sinifrppd8m6kah30wywc27adyzqhgzy-nixos-system-greyjay-25.11.20251028.08dacfc 18.5 KiB 8.4 GiB
10/nix/var/nix/profiles/per-user/mort/profile-1167-link /nix/store/6fc897c236g7yjpz467plxmzp5ycx87w-profile 66.0 KiB 3.0 GiB

The first three lines under /nix/var/nix/profiles are small and channel-state related; the fourth is also tiny and something flake-related. Lines 8 and 9 beginning /run/ are the currently running system so leave them alone; the match of the underlying path with that of line 7 shows that 931 is the currently running generation.

However, lines 5 and 6 are suspicious:

5/home/mort/.local/state/nix/profiles/home-manager-425-link /nix/store/p8y1zr968ajjnva0whznygvqv09fvdd2-home-manager-generation 17.5 KiB 14.1 GiB
6/home/mort/.local/state/home-manager/gcroots/current-home /nix/store/kmb5w2qymnnhqv3wz3k1nw0psp2qqgmx-home-manager-generation 17.6 KiB 3.0 GiB

These look like dead roots from random rebuilds, tryouts, whatever. And the first one is big at over 14GiB. So just delete it and re-run garbage collection:

rm ~/.local/state/nix/profiles/home-manager-425-link
sudo nix-store --gc && nix-store --gc
nixos-rebuild --sudo switch

Lo and behold! suddenly I have about 15GiB extra free. Happy days :)

In the end, I tweaked that script to the following (logically) one-liner; recorded here so I can copy’n’paste it again in future:

fd -t l . /nix/var/nix/gcroots | xargs readlink | while read link; do
  printf "%s " $link
  test -r $link && nix path-info -Ssh $link
done | tr -d " " | tr "\t" " " | tr -s " " | sort -h -t" " -k3 | uniq | tr " " "\t"