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) andkeep-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'
CREATE TABLE ValidPaths (
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);
CREATE TABLE Refs (
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
begin
delete from Refs where referrer = old.id and reference = old.id;
end;
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?