Is there any way to find out how much data can be cleared by executing 'nix-collect-garbage -d' without actually executing it?

I need to find out how much garbage can be cleared when ‘nix-collect-garbage -d’ is run for a personal project but I haven’t been able to find any answer about this on documentations and DDG.

I’ve tried ‘du -sh /nix/store’ but it would print the size everything in /nix/store however ‘nix-collect-garbage -d’ does not remove everything when it’s executed so that’s not gonna work. I’ve also tried executing “nix-store --gc --print-dead | grep ‘^/nix*’” (‘–print-dead’ produces output that which contains store paths that are not live and will not be used, with respect to roots) and then executing “stat -c ‘%s’” on each path (‘stat -c “%s” $file_name’ produces output containing the bytes of the concerned file in bytes) however that doesn’t seem to work as well for some unknown reason as they all seem to add up to a few kilobytes (despite the actual amount that ‘nix-collect-garbage -d’ deletes being about 400MB).

I know that the code below is extremely shit but that’s because that’s not my main project but a separate script solely for trying to find out what I am doing wrong and how I can properly implement this in my main project.

Thanks for any help!

#!/usr/bin/env bash

`tmp() {
declare -a str byt
str=( $(nix-store --gc --print-dead 2>/dev/null | grep ‘/nix*’) )
for garbage in “${str[@]}”; do
byt+=( $( stat -c ‘%s’ $garbage ) )
done
total=0
for filesize in “${byt[@]}”; do
total=$(( $total + $filesize ))
done
convertedtotal=“$(( ( $total / 1024 ) /1024  ))MB”
echo “$total”
echo “$convertedtotal”
}

tmp

Some responses from people on other platforms that did not work perfectly:

“I had the same issue a couple months back, i asked claude back then and got: sudo nix-store --gc --print-dead 2>/dev/null | xargs -I{} du -s {} 2>/dev/null | awk ‘{sum += $1} END {printf “%.2f GB\n”, sum/1024/1024}’”

“I had the same issue a couple months back, i asked claude back then and got: sudo nix-store --gc --print-dead 2>/dev/null | xargs -I{} du -s {} 2>/dev/null | awk ‘{sum += $1} END {printf “%.2f GB\n”, sum/1024/1024}’"

And

bytes=$(

nix-store --gc --print-dead |

xargs du -sb |

awk ‘{s+=$1} END {print s}’

)

numfmt --to=iec “$bytes”

some like this should work for you"

Both of them had one issue:

`$ nix-store --gc --print-dead | grep ‘^/nix*’ | xargs du -sh | awk ‘{ print $1 }’

finding garbage collector roots…

determining live/dead paths…

8.0K

8.0K

8.0K

4.0K

1.6M

1.9M

8.0K

2.3M

8.0K

45M

4.0K

4.0K

8.0K

4.0K

4.0K

4.0K

684K

8.0K

8.0K

4.0K

8.0K

8.0K

7.7M

692K

4.0K

728K

8.0K

8.8M

344K

492K

4.0K

572K

8.0K

1.1M

4.0K

4.0K

8.0K

4.0K

8.0K

36K

8.0K

8.0K

8.0K

4.0K

4.0K

8.0K

All of that is about 72MB.

$ nix-collect-garbage -d

removing old generations of profile /home/north/.local/state/nix/profiles/home-manager

removing old generations of profile /home/north/.local/state/nix/profiles/profile

finding garbage collector roots…

deleting garbage…

deleting ‘/nix/store/vdixm6r047a3rw1wx8pqsjh40nl886jj-kate-26.04.2.drv’

deleting ‘/nix/store/s5i0c135x2x5dz9ah01zxvl3prywqb30-kate-26.04.2.tar.xz.drv’

deleting ‘/nix/store/rivv4fh68mkqnc4v225mflmi3a9damcj-xfconf-4.20.0’

deleting ‘/nix/store/9nj1k063j54qfxzcm49dspwwl1sxhqs0-libxfce4util-4.20.1’

deleting unused links…

note: hard linking is currently saving 0.0 KiB

46 store paths deleted, 64.8 MiB freed`

Now what’s strange is that ‘nix-store --gc --print-dead’ outputs these 46 paths (which add up to ~72MB) and ‘nix-collect-garbage -d’ deletes 46 paths but somehow, 64.8 MiB, which is about 68MB, is freed instead of 72MB. I tried to replicate this again and it worked, there’s always some amount of inaccuracy. Of course, a few megabytes don’t matter but this can create significant inaccuracies on systems with more cache, but this definitely is a lot better.

You can get an upper limit of what will be freed using this “one-liner”:

nix-collect-garbage --dry-run -d 2>&1 | \
sed -ne "s+would remove profile version \(.*\)+/nix/var/nix/profiles/system-\1-link+p" | \
xargs -n 1 nix-store --query --requisites | sort -u | \
xargs -n 1 du -sb | awk '{sum+=$1;} END{print sum/(1024*1024) "MB";}'

Note that this assumes that the analyzed store entries are not part of any closure that will be kept (other generations, direnv gc roots,..) and counts hardlinked store paths multiple times.

That’s probably the issue with the original as well

A correct logic would need to resolve everything which is actually linked to something which is not going to be deleted

1 Like

This is even worse than the other two suggestions; it prints that I have a few kilobytes of garbage even though executing ‘nix-collect-garbage -d’ actually deletes ~81 MB of garbage.

The -d makes this a hard one.

You have to simulate it’s effect of removing all generations first, and then figure the newly added collectables.

I doubt you can achieve that without reimplementing half of nix’ GC logic.

4 Likes

It seems like this feature used to exist, got clobbered by a larger PR at some point, and no one has had the energy to diagnose and fix it.

1 Like

The reason why this is hard to do is because of the hard linking optimization in the nix store. Garbage collecting a store path is no guarantee that any disk space will actually be freed, so computing how much would be freed in a dry run is really hard.