Chapter 10 describes the directory structure of ~/.nix-profile, and explains how rollbacks work.

The chapter starts with a sort of complicated drawing of multi-user directories with like default profiles and custom ones and it’s way too much for me to follow and I don’t really care. I think it’s easier to just look at some examples on my current system, and imagine how I would generalize them to multiple users.

$ readlink ~/.nix-profile
/nix/var/nix/profiles/per-user/ian/profile

$ readlink /nix/var/nix/profiles/per-user/ian/profile
profile-18-link

$ readlink /nix/var/nix/profiles/per-user/ian/profile-18-link
/nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment

That number in profile-18-link seems to be the way that rollback works – if we want to roll back, we just point the profile symlink to profile-17-link. In fact we can see everything that I’ve done so far:

$ ls -l /nix/var/nix/profiles/per-user/ian
channels@ -> channels-1-link
channels-1-link@ -> /nix/store/98kbm9c2p7m8h1mjppvc0sam1vvkpz85-user-environment
profile@ -> profile-18-link
profile-10-link@ -> /nix/store/jql328aa79xk37fnj4jvikdsv1g04hp0-user-environment
profile-11-link@ -> /nix/store/4n1jlahyami9rzdfgc4x1w3i1niik3r7-user-environment
profile-12-link@ -> /nix/store/jql328aa79xk37fnj4jvikdsv1g04hp0-user-environment
profile-13-link@ -> /nix/store/4n1jlahyami9rzdfgc4x1w3i1niik3r7-user-environment
profile-14-link@ -> /nix/store/jql328aa79xk37fnj4jvikdsv1g04hp0-user-environment
profile-15-link@ -> /nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment
profile-16-link@ -> /nix/store/jql328aa79xk37fnj4jvikdsv1g04hp0-user-environment
profile-17-link@ -> /nix/store/4n1jlahyami9rzdfgc4x1w3i1niik3r7-user-environment
profile-18-link@ -> /nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment
profile-6-link@ -> /nix/store/r2rlv43ijmxh56ng799zqkm471jbvr7x-user-environment
profile-7-link@ -> /nix/store/2ia4kf2kbhp4888jm9sawk8gzg5b4v4r-user-environment
profile-8-link@ -> /nix/store/jql328aa79xk37fnj4jvikdsv1g04hp0-user-environment
profile-9-link@ -> /nix/store/4n1jlahyami9rzdfgc4x1w3i1niik3r7-user-environment

I don’t have any profiles before 6 because I did a garbage collection during the quick start guide, which cleaned them up.

This is very neat. We can see that not only are profiles “versioned” when we run nix-env commands, but channels appear to be versioned as well. We can also see that there are very few unique environments in this list – because I’ve mostly just been installing and uninstalling the hello package, which reverts me back to a version that I’ve had before. And by the virtue of content-addressed file paths, we don’t need to duplicate those user environments. Love it.

I had previously suspected that this involved something called the “Nix database,” but it seems like it’s just names of symlinks. Or maybe that’s what the manual means by “Nix database” – just the files and directories that are in /nix/var. I do not know yet.

If it is just stored on the file system, I have an unimportant question.

I learned previously that if I went from generation 10 to generation 1 (say), and then did something, I would create a generation 11. How does that work? Does Nix list all the files and find the biggest one? Doesn’t that mean that creating a new profile generation would use time and memory linear to the number of generations present? It could start from the current generation, and check forward until it found an empty space, since the common case is being very close to the top – that gives you constant memory, but now you’re performing a linear number of syscalls in the degenerate case, and if you ever had a sparse list you could end up with illogical numbering – so it could have a hybrid approach where– none of this matters. With frequent garbage collection, there is no universe in which this becomes a problem worth actually thinking about. But I’ve had to do this exact thing before in some hacky script and I always felt gross about it. Even though I know it doesn’t matter… it just feels like there should be some better way that I don’t know about.

Anyway.

I guess a better question is: if I switch to an earlier profile, then install something, then roll back, does it take me to the earlier profile? Or just n-1?

$ nix-env --switch-generation 10
switching from generation 18 to 10

$ nix-env -iA nixpkgs.hello
installing 'hello-2.10'

$ nix-env --rollback
switching from generation 19 to 18

Okay. So yeah, --rollback is just “do the obvious simple thing.” There is no separate history; we aren’t making a DAG where new generations know what they’re based on or something. Just file names. In fact…

$ nix-env --switch-generation 10
switching from generation 18 to 10

$ rm /nix/var/nix/profiles/per-user/ian/profile-15-link

$ nix-env -iA nixpkgs.hello
installing 'hello-2.10'

$ readlink $(readlink ~/.nix-profile)
profile-19-link

Huhhhh. That’s weird, right? I already had a generation 19. I rolled back to 18, then switched to ten, then made a new one… and got back to 19? Is it using like… some kind of start and length to decide what the next profile should be? I don’t know.

I can’t imagine this ever mattering. Let’s get back on track.

I look inside the profile, to see what I can see.

$ tree /nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment
/nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment
├── Library -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/Library
├── bin
│   ├── hello -> /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10/bin/hello
│   ├── nix -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix
│   ├── nix-build -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-build
│   ├── nix-channel -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-channel
│   ├── nix-collect-garbage -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-collect-garbage
│   ├── nix-copy-closure -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-copy-closure
│   ├── nix-daemon -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-daemon
│   ├── nix-env -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-env
│   ├── nix-hash -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-hash
│   ├── nix-instantiate -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-instantiate
│   ├── nix-prefetch-url -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-prefetch-url
│   ├── nix-shell -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-shell
│   └── nix-store -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/bin/nix-store
├── etc
│   ├── profile.d -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/etc/profile.d
│   └── ssl -> /nix/store/ki7gssifc0xracrah8ygm63xj23wkjdz-nss-cacert-3.60/etc/ssl
├── lib -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/lib
├── libexec -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/libexec
├── manifest.nix -> /nix/store/1d6n6cw1ab1qk0d4vq1hw37rlg14zpcy-env-manifest.nix
└── share
    ├── info -> /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10/share/info
    ├── man
    │   ├── man1
    │   │   ├── hello.1.gz -> /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10/share/man/man1/hello.1.gz
    │   │   ├── nix-build.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-build.1.gz
    │   │   ├── nix-channel.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-channel.1.gz
    │   │   ├── nix-collect-garbage.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-collect-garbage.1.gz
    │   │   ├── nix-copy-closure.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-copy-closure.1.gz
    │   │   ├── nix-env.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-env.1.gz
    │   │   ├── nix-hash.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-hash.1.gz
    │   │   ├── nix-instantiate.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-instantiate.1.gz
    │   │   ├── nix-prefetch-url.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-prefetch-url.1.gz
    │   │   ├── nix-shell.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-shell.1.gz
    │   │   └── nix-store.1.gz -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man1/nix-store.1.gz
    │   ├── man5 -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man5
    │   └── man8 -> /nix/store/1s7qdbi6fprms0hljsx9rq2fwr3fz1gi-nix-2.3.10-man/share/man/man8
    └── nix -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10/share/nix

Neat.

I can see bin – that’s the directory that ultimately winds up in my PATH, after following this somewhat tangled web of symlinks. As well as share/man in my MANPATH. etc/profile.d contains nix.sh, which is sourced in my .zshenv, and nix-daemon.sh, which I assume is only relevant for multi-user Nix installations.

I tried to look at manifest.nix but it was not pretty printed and I don’t know how to pretty print it yet so I just gave up. lib seems to be what you’d guess – I recognize the string “boost” in some of the .dylib files. Library is a macOS thing; it currently contains the equivalent of a systemd unit file for the Nix daemon – again, only relevant for multi-user Nix, I think. Don’t know what libexec is. It contains a binary called resolve-system-dependencies and a symlink back to bin/nix. Probably not going to ever think about it.

The only other interesting thing in here is share/nix.

# tree /nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment/share/nix
/nix/store/0hq28alc5ywci03nz2imv8rl0wv6nnyj-user-environment/share/nix
├── corepkgs
│   ├── buildenv.nix
│   ├── config.nix
│   ├── derivation.nix
│   ├── fetchurl.nix
│   ├── imported-drv-to-derivation.nix
│   └── unpack-channel.nix
└── sandbox
    ├── sandbox-defaults.sb
    ├── sandbox-minimal.sb
    └── sandbox-network.sb

I don’t know what all this is. Are these, like, built-in Nix functions? Or I suppose… semi-built-in? I don’t know. I don’t know what these are. I could read them. But I am tired, and I do not want to read them now.

Lastly, I learn that I can temporarily override my ~/.nix-profile by setting nix-env --profile my/custom/path. Unlike --file, this seems perfectly straightforward, and I will accept it without question.


  • How do I pretty print Nix files?
  • What are all those /nix/store/...user-environment/share/nix/*.nix files?