How are you keeping devshell dependencies live in store?

I have the automatic nix store gc service running, nightly.

Every day when I cd somewhere with dotenv and flake devshells, a bunch of stuff gets re-downloaded, because it’s not linked via any of the profiles gc sees.

I could think of a few hacks:

  • run the gc service much less often, or not at all
  • add the packages to a profile, just because
  • add something to the direnv hook that somehow adds all the packages to a dummy profile, and then add another layer of gc logic to that

Are there any better suggestions / options / patterns / existing solutions?

5 Likes

I use nix-direnv plugin for direnv and it manages GC roots for the shells entered through it.

About once a month though I have to go through GC roots and delete the shell profiles I won’t need in the near future.

2 Likes

Oh, that looks like pretty much exactly the thing. Awesome.

ie in /nix/var/nix/gcroots/auto/ or similar?

1 Like

I use nix-store --gc --print-roots.

4 Likes

You can use nix develop --profile ./profile. This makes it save the devshell as a version of a profile, which prevents it from being garbage-collected. You can enter any version of the profile by doing nix develop ./profile-<version>-link.

3 Likes

This isn’t quite working.

I have some projects. I ran nix develop --profile ./profile, and profile links were created. They show up in nix-store --gc --print-roots, and seemingly all is good.

Some time passes. Some project aren’t touched for a while. Eventually when I get back to them, starting a devshell (via direnv, but I assume that doesn’t matter) ends up re-downloading a bunch of dependencies again, that I would not have expected to get gc’d.

Looking again at the roots, those profile links are no longer listed (even if they point to valid store locations). They reappear in the gc roots when I update the project deps (e.g. nix flake update) and recreate a new profile version in the workspace.

I’m not yet sure when they disappear: after a reboot? once they’re older than the auto-gc threshold? some other expiry? I will have to keep an eye on this and try to work out what’s going on - but it’s not what I expected to happen and may be a bug?

I usually have this:

# reload when these files change
watch_file flake.nix
watch_file flake.lock

{
  # shell gc root dir
  mkdir -p "$(direnv_layout_dir)"

  eval "$(nix print-dev-env --profile $(direnv_layout_dir)/flake-profile)"
} || use nix

Well it’s not as simple as them just going away after a reboot. I was wondering if the state was stored somewhere on a tmpfs or similarly obvious problem.

I went looking. It seems they get registered in /nix/var/nix/gcroots/auto, which has a set of hash/key files as links to the profile links. This looks like something maintained by the nix daemon.

Hypothesis: could the contents of this be getting validated on nix daemon startup, perhaps (sometimes) too early before the filesystems they’re pointing to are mounted, and thus there’s a race where they sometimes get removed incorrectly?

Update, because I was just reminded of this.

I don’t know what the final issue above actually was, but it went away entirely by switching to using nix-direnv together with direnv.