How does nix-env -u know when not to apply version updates?

I’ve noticed that nix-env -u will frequently avoid applying updates that nix-env -qc claims are available. For example, I have nodejs-8.15.1 installed, and nix-env -qc tells me that version 11.13.0 is available, but nix-env -u doesn’t apply that update. This is good, as there are multiple nodejs packages for different major versions, but how does nix-env -u know that it shouldn’t update (and why does nix-env -qc not use the same logic)?

What prompted this question is I’m interested in having a second package for cocoapods that provides access to the current beta, as cocoapods has a long beta cycle and really wants people to use them (and in fact you can’t even submit a package update with the stable right now because they updated the server to expect something the current beta does :woman_facepalming:t2:). My question is, if I create a e.g. nixpkgs.cocoapods-beta, with the same cocoapods package name but the beta version, will nix-env -u know not to upgrade from the stable version to the beta version? And then what happens when the beta is released and the stable is updated, will nix-env -u upgrade from the beta to the stable, or will the cocoapods-beta package have to adopt the stable version too? If the latter, what will nix-env -u do when cocoapods-beta once again has a new beta version?

Packages can specify a meta.prio field (which defaults to 0). nix-env will prefer derivations with a lower meta.prio (numerically speaking, to add extra confusion this is referred to as high prio in Nixpkgs), even if it has a lower version number. lib defines the functions lowPrio and highPrio that update the prio accordingly (for 10 and -10, respectively). This is what the Node 8.x package does.

As long as cocoapods-beta has a higher (numerical) prio, yes, it will prefer cocoapods. However, it also won’t upgrade cocoapods-beta installs to new beta releases, since the preferred release will be stable cocoapods (which it will refuse to install since it has a lower version number).

At that point cocoapods will have both a higher version number and a lower (numerical) prio, so nix-env -u will upgrade to it. However, nixos-rebuild and other tools that use Nixpkgs attribute names will not, so -beta packages should still point to the stable build if there is no current beta release.


In general nix-env (and especially upgrades) is a huge mess, and I’d strongly suggest avoiding using it to install individual packages, especially when you care about different package variants. Instead, use environment.systemPackages (for system packages) or define a meta-package and install that (for single-user packages and non-NixOS systems).

2 Likes

Huh, I was under the impression that the priority only affected what happens if you try and install 2 derivations that both have the same output path. If this affects updating too, surely nix-env won’t “update” to 8.x if I install a different version with a lower priority? Or does this only work if the highPrio package is the one with the lowest version number?

I’m on macOS so I would have to do the meta-package route, but it seems kinda crappy for me to have to edit an overlay on disk any time I want to install or uninstall a package. I don’t suppose there are any plans to overhaul nix-env upgrades?

nix-env -u won’t downgrade a package, even if the newer version package is no longer longer available (or is shadowed by prios).

I can’t remember the name right now, but IIRC that’s a similar but unrelated system.

It’s pretty much the same workflow as for NixOS, which works fine for me (though it did take some getting used to). I expect this will be fixed when/if nix-env is merged into nix (which has replaced package names with attributes, removing the need for the prio system at all).

There is also Declarative package management for normal users - #2 by Mic92 which manages the meta-package for you.

I’ve been looking more into this approach and I’m a bit confused. The linked gist just declares packages as direct children of userPackages (I assume, though haven’t verified, that if nix-env is given a set it installs all members of the set), but the documentation on building an environment demonstrates using buildEnv (with packageOverrides but it seems I can ignore that bit).

I can’t find any documentation on buildEnv itself. As best as I can figure from the example in building an environment, it can be used to change the paths to link from the whole package set without having to override each individual package, but if I don’t care about changing the paths to link, are there any pros or cons to using it? Would best practice be to take the aforelinked gist and tweak it to use buildEnv or is that a problem if I then want multiple overlays to contribute to the package set?

IIRC removing a package from a buildEnv will uninstall it on the next “rebuild”, while the lightweight meta-package can only ever add new packages, but don’t quote me on that.

I take it buildEnv presents itself as a single installed package, so e.g. nix-env -q won’t show me all the software it includes, it’ll just show me the name of the env?

Yeah: https://github.com/NixOS/nixpkgs/blob/36bb2e3beb675af028a48968fb5eeee48e231174/pkgs/build-support/buildenv/default.nix

Ok, thanks. So it sounds like buildEnv is the way to go if I want to be able to use nix-env -i to install individual packages in addition to the common collection, but avoiding buildEnv and doing what’s in this gist is better if I want my overlay to be the single source of truth for all packages and want to be able to use nix-env -q to query for what’s installed.

1 Like