I remember from using Nix long ago that when you “uninstall” a package, you don’t really “uninstall” it – you just remove the symlinks to it in your ~/.nix-profile/bin directory, so it no longer appears on your PATH.

Now I know that it doesn’t “remove symlinks” but rather “builds a new user environment and changes the ~/.nix-profile symlink to point to the new generation’s symlink to that user environment.” But you know, effectively the same thing. So there is a separate operation to actually delete those files, and remove the old generations.

Let’s find out more.

Chapter 11. Garbage Collection

In the quick start guide I learned that I could use:

$ nix-collect-garbage --delete-old

And now I know that --delete-old means “hey but before you collect garbage, also delete old generations of my profile.”

Now I’m learning that I can do the --delete-old step myself, if I want, by using nix-env --delete-generations, and I can trigger the garbage collection by using nix-store --gc. This is my first encounter with nix-store, but the separation makes some sense to me: nix-env deals with my environment – stuff in my ~/.nix-profile – but the store is bigger than just that. It will eventually have things from nix-shell files, other users, or whatever.

So, alright. I suppose that nix-collect-garbage is its own command because it does both environment things and store things and it would be weird to have a single command that does both. Aside from, you know, nix.

Anyway. This has an example of an interesting command:

$ nix-env --delete-generations 14d

To delete generations older than two weeks.

Apparently generations know how old they are? Which is strange: previously I was able to convince myself that these were just symlinks. I apparently forgot that when I ran nix-env --list-generations, it showed me a timestamp. Where is that timestamp stored? It’s not in the user-environment: if it were, I wouldn’t have seen the duplicated environments when I looked at my symlinks. This “Nix database” character is starting to look suspicious again.

Oh. Right. Duh.

It’s the creation date of the symlink, isn’t it?

Thanks, filesystems. Still no evidence of any database here.

See? Unedited stream of consciousness. No matter how dumb it makes me look.

Let’s move on from that. Quickly.

The behaviour of the gargage collector [sic] is affected by the keep-derivations (default: true) and keep-outputs (default: false) options in the Nix configuration file.

The Nix configuration file?? What’s that?

This is the first I’d heard of this. Where is this file? What else can I configure?

I investigate likely sources – it wouldn’t be in my home directory, since these decisions feel bigger than me. I try to see if it’s in /nix… which leads me only to /nix/var/nix. And here I see a folder called db!

Aha! The Nix database is real! What a rollercoaster this has been.

I go on a tangent. There is a file /nix/var/nix/db/schema – I open it excitedly. But it just turns out to be a version number. Boo. There is also a file called big-lock, which earns my wholehearted approval. That’s a good name.

My curiosity gets the better of me and I briefly forget about the Nix configuration file – I open up the Nix database.

And it’s just a bunch of ^@ signs! I can’t make sense of any of this. Yuk yuk yuk.

$ sqlite3 /nix/var/nix/db/db.sqlite '.schema'
    id               integer primary key autoincrement not null,
    path             text unique not null,
    hash             text not null,
    registrationTime integer not null,
    deriver          text,
    narSize          integer,
    ultimate         integer, -- null implies "false"
    sigs             text, -- space-separated
    ca               text -- if not null, an assertion that the path is content-addressed; see ValidPathInfo
CREATE TABLE sqlite_sequence(name,seq);
    referrer  integer not null,
    reference integer not null,
    primary key (referrer, reference),
    foreign key (referrer) references ValidPaths(id) on delete cascade,
    foreign key (reference) references ValidPaths(id) on delete restrict
CREATE INDEX IndexReferrer on Refs(referrer);
CREATE INDEX IndexReference on Refs(reference);
CREATE TRIGGER DeleteSelfRefs before delete on ValidPaths
    delete from Refs where referrer = old.id and reference = old.id;
CREATE TABLE DerivationOutputs (
    drv  integer not null,
    id   text not null, -- symbolic output id, usually "out"
    path text not null,
    primary key (drv, id),
    foreign key (drv) references ValidPaths(id) on delete cascade
CREATE INDEX IndexDerivationOutputs on DerivationOutputs(path);

No idea. Truly no idea. Definitely not ready for this. But it’s nice to see how small it is! And that there seems to be nothing user-specific – all store-wide things. So I didn’t learn… nothing, by doing that. But still: let’s abort; let’s get back on course.

I still haven’t found the configuration file. Maybe I have to create one? Maybe there is no configuration file by default.

I grep for likely strings in man nix-env – wouldn’t it be nice if there were just a man nix? That would tell me things like this? Anyway. I follow a pointer to man 5 nix.conf, which tells me it’s sysconfdir/nix/nix.conf. What’s sysconfdir? It offers an example – /etc/nix/nix.conf – but that’s not there. I google the term sysconfdir because I’ve never heard it before. That leads me to /usr/local/etc. But: no joy.

I feel like there should be a command to just print the path of the current configuration file, but no such command is listed in man 5 nix.conf.

The man page for nix-env tells me that I can use NIX_CONF_DIR to change the location, and that that defaults to prefix/etc/nix, contradicting what man 5 nix.conf said. prefix is not defined, but from reading the descriptions of the other environment variables I conclude that this means /nix.

And I have no /nix/etc, so I have now decided that I have no nix.conf file. I must just be using all defaults.

man 5 nix.conf gives an example of a config file that I could use:

keep-outputs = true       # Nice for developers
keep-derivations = true   # Idem

That comment is intriguing. I allow myself to be briefly amused by the idea of someone who is not a professional software developer using Nix, before returning to the manual:

The defaults will ensure that all derivations that are build-time dependencies of garbage collector roots will be kept and that all output paths that are runtime dependencies will be kept as well. All other derivations or paths will be collected. (This is usually what you want, but while you are developing it may make sense to keep outputs to ensure that rebuild times are quick.)

Okaaaay. I assume this is the “Nice for developers” comment above. But I don’t really understand what that means. Why does… keeping outputs mean that rebuild times will be quick? What are “outputs”?

I am also sort of surprised that it keeps build time dependencies around – I suspect this is useful mostly because so many packages share them? Or so that --upgrade is faster? If I got a package from a binary cache, do I still get the build-time dependencies?

This seems like a potential waste of space.

$ du -hcd 0 /nix
du: /nix/.Spotlight-V100: Operation not permitted
du: /nix/.Trashes: Operation not permitted
du: /nix/.fseventsd: Permission denied
1.1G    /nix

And so far the only package I’ve installed is hello!

I mean, I assume most of that is whatever nix-shell installs, which I hope to learn more about. I expect if I collected garbage right now, that would go down considerably, but then I would be annoyed the next time I ran nix-shell.

The chapter closes with an example of nix-collect-garbage -d, which it defines as “a convenient little utility.” And it is!

At the end of this I should make a bestiary of all the nix-* commands. Clearly nix-env is the main powerhouse. nix so far has a search function and nothing else. nix-store sounds like a low-level plumbing command – but there are a bunch more, and I don’t know which are “little utilities” and which are fundamental commands that the others wrap. But! By the time I finish reading this manual, hopefully I will.

Anyway. Next we learn about garbage collector roots, and I am delighted to learn that the way you register new GC roots is by using ln -s. Yep! Make a symlink! You’re an adult! What, you thought there would be a nix add-gc-root command? Please. The manual gives a weird example of how to add a new root:

$ ln -s /nix/store/d718ef...-foo /nix/var/nix/gcroots/bar

(It’s weird to me that the root is called bar but the store path is foo.)

So that’s… fine. That’s pretty much all there is in this section. There’s nothing about how to make the GC include the stuff that nix-shell needs. Or even… what those things are. Do not know. I still hope to find out, but I’m a little sad that I didn’t find out in this section.

  • Where is the Nix configuration file?
  • What are “outputs” (as in keep-outputs)?
  • What does keep-derivations do? I’m still not really clear on the term “derivations”.
  • When I get a binary cached package, do I still download and keep all the build-time dependencies?