The foundational part of Nix is the Nix package manager. This is a tool that allows you to install packages. The "first layer" of Nix usage is to use this tool to install packages. The Nix package manager, similar to projects like os-rpmtree (used in Fedora Silverblue) or snapper (used in snap based Linux distros), will allow you to easily revert package installs or updates if something goes wrong.
This post covers the basics of using the nix package manager at the command line - while most users use nix through more advanced workflows, the basics explained here will be useful in understanding those workflows, as well as for users who want to dip their toes into Nix by treating it like a more traditional package manager to start out.
To install your first package, run the nix profile install
command. The
package used in this demonstation is ripgrep
, which is a rust based grep
alternative that has a nicer out of the box experince.
This command tells nix
to install the ripgrep
package from the nixpkgs
registry into the default profile. If you followed the installer you should
now be able to run the rg
binary from the ripgrep
package.
Let's install a few more packages, like jq
the CLI tool for JSON and
fzf
a fuzzy finder that lets you search a list of CLI arguments and select one.
Now you can observe what it set up. By default, nix profile
will set itself up
in your ~/.nix-profile
directory.
> ls -l ~/.nix-profile/
total 4
dr-xr-xr-x 1 root root 44 Jan 1 1970 bin/
-r--r--r-- 1 root root 500 Jan 1 1970 manifest.json
dr-xr-xr-x 1 root root 78 Jan 1 1970 share/
> ls -l ~/.nix-profile/bin/
total 20
lrwxrwxrwx 1 root root 62 Jan 1 1970 fzf -> /nix/store/bds5wjz9js59y9ccwmryav4klc7ir8wg-fzf-0.34.0/bin/fzf*
lrwxrwxrwx 1 root root 68 Jan 1 1970 fzf-share -> /nix/store/bds5wjz9js59y9ccwmryav4klc7ir8wg-fzf-0.34.0/bin/fzf-share*
lrwxrwxrwx 1 root root 67 Jan 1 1970 fzf-tmux -> /nix/store/bds5wjz9js59y9ccwmryav4klc7ir8wg-fzf-0.34.0/bin/fzf-tmux*
lrwxrwxrwx 1 root root 61 Jan 1 1970 jq -> /nix/store/348rj7g1gg2fnz91qilrnd0sc8nchls1-jq-1.6-bin/bin/jq*
lrwxrwxrwx 1 root root 65 Jan 1 1970 rg -> /nix/store/cmzib5qfpqsc9fq1h0i8hawdzn612jw4-ripgrep-13.0.0/bin/rg*
> ls -l ~/.nix-profile/share/
total 20
lrwxrwxrwx 1 root root 80 Jan 1 1970 bash-completion -> /nix/store/cmzib5qfpqsc9fq1h0i8hawdzn612jw4-ripgrep-13.0.0/share/bash-completion/
dr-xr-xr-x 1 root root 102 Jan 1 1970 fish/
lrwxrwxrwx 1 root root 64 Jan 1 1970 fzf -> /nix/store/bds5wjz9js59y9ccwmryav4klc7ir8wg-fzf-0.34.0/share/fzf/
lrwxrwxrwx 1 root root 68 Jan 1 1970 man -> /nix/store/cmzib5qfpqsc9fq1h0i8hawdzn612jw4-ripgrep-13.0.0/share/man/
lrwxrwxrwx 1 root root 72 Jan 1 1970 vim-plugins -> /nix/store/bds5wjz9js59y9ccwmryav4klc7ir8wg-fzf-0.34.0/share/vim-plugins/
lrwxrwxrwx 1 root root 68 Jan 1 1970 zsh -> /nix/store/cmzib5qfpqsc9fq1h0i8hawdzn612jw4-ripgrep-13.0.0/share/zsh/
First, note that ~/.nix-profile
is the location of your user's default profile. Every
user can have their own independent profile with their own set of installed packages.
This doesn't involve duplicating packages if multiple users have them installed, as the
multi-user installation will have these all symlink back to your global /nix
directory.
If you have performed the default installation and you're using a supported shell, then
~/.nix-profile/bin
should already be in your path. If not, you can add it if you want
to have your commands from your nix profile available at all times.
Once that's done, let's try and use some of those commands.
|
You should get a window which lists the three installed packages according to the manifest json file and if you pick an item with arrow keys or typing, it should show you the object from your nix profile that describes the installed package.
Of course, while rooting around in the manifest.json
files works nicely as
a demonstration of the newly installed jq
package, you don't need to do this
to get a package listing. Instead you can use the nix profile list
command,
which will produce output like the following:
> nix profile list
0 flake:nixpkgs#legacyPackages.x86_64-linux.ripgrep github:NixOS/nixpkgs/b7a47253e0c8cb04c0a3f8ed3149e90229e62884#legacyPackages.x86_64-linux.ripgrep /nix/store/k86k8szjhii474hp7f7xqvvdf7fnfigw-ripgrep-13.0.0
1 flake:nixpkgs#legacyPackages.x86_64-linux.fzf github:NixOS/nixpkgs/b7a47253e0c8cb04c0a3f8ed3149e90229e62884#legacyPackages.x86_64-linux.fzf /nix/store/d6hh0i8fkgqinq7f8hdfvy8r0qk35412-fzf-0.34.0-man /nix/store/v5aza5gg981bzmqw1z0f6lddvnqi9ab4-fzf-0.34.0
2 flake:nixpkgs#legacyPackages.x86_64-linux.jq github:NixOS/nixpkgs/b7a47253e0c8cb04c0a3f8ed3149e90229e62884#legacyPackages.x86_64-linux.jq /nix/store/5x1jpz4grhzl1psmrxj42wzmvllqgbhm-jq-1.6-bin /nix/store/6pnapmlp83dvi974s86lvbmwn5lhdv5b-jq-1.6-man
The fields here are:
- A number referencing the installation order. This can be used as shorthand when installing or uninstalling the package, similar to a short hash in git.
- The expanded version of the reference used to install the package. This is used by Nix when it needs to upgrade the package, or by users if they want to have something that's independent of installation order. This is also known as the mutable flake reference.
- The full version of the reference when it is resolved. This means that not
only will it refer to conceptually the same package, but also the same version
of the same package. As a result, it's known as the immutable flake reference,
as it will always get the same result, while the mutable flake reference can
change as you upgrade your
nixpkgs
version. - The location on disk in the nix store the package was stored in.
Finally, you can update a specific package or all packages.
To update a specific package, you can use nix profile upgrade <pkg reference>
. The
package reference can be one of the numbers from nix profile list
, or the mutable flake reference
in either the short version used when installing it or the expanded version in
nix profile list
. To upgrade all packages, you can use nix profile upgrade '.*'
.
Generations
So far, I've just covered how to do the things with nix that you can do with
any package manager, and they seem a little more convoluted too, so now it's
time to get into what you can do with Nix that you can't with apt
, brew
or pacman
.
The first is that Nix keeps multiple versions of installed packages, and a record of the changes made. This allows you to easily rollback package management commands. Each version of your profile after you perform a package manager operation is known as a generation.
You can observe this log of history with the nix profile history
command:
> nix profile history
Version 1 (2022-10-05):
flake:nixpkgs#legacyPackages.x86_64-linux.ripgrep: ∅ -> 13.0.0
Version 2 (2022-10-05) <- 1:
flake:nixpkgs#legacyPackages.x86_64-linux.fzf: ∅ -> 0.34.0, 0.34.0-man
flake:nixpkgs#legacyPackages.x86_64-linux.jq: ∅ -> 1.6-bin, 1.6-man
When you run this in your terminal, you will likely see the number 2 highlighted in green to indicate it is the current version.
Now, let's say you didn't want to keep around jq
and fzf
as you're done
with them now after the demo. In this case, you can run the nix profile rollback
command.
> nix profile rollback
switching profile from version 2 to 1
Run nix profile history
again and you'll now see version 1 highlighed
as the active version.
Change your mind and want to go back to version 2? The --to
parameter allows you to set a specific version as the target, letting
you go back multiple versions, or, as in this case, forward versions after
rollbacks.
> nix profile rollback --to 2
switching profile from version 1 to 2
Profiles
The second uncommon feature in Nix is the ability to manage multiple
sets of installed packages. While in part 7 I'll get onto flakes and
a neater way to manage them on a per-project basis, the nix profile
command
is capable of doing it on its own by passing the --profile
argumemt to a
command.
> nix profile install --profile ~/py39-profile nixpkgs#python39
> nix profile install --profile ~/py310-profile nixpkgs#python310
> ~/py39-profile/bin/python --version
Python 3.9.14
> ~/py310-profile/bin/python --version
Python 3.10.7
Each of these profiles has the full suite of nix commands available to it, including upgrades, rollbacks, etc.
Garbage Collection
Some of you might have heard the mention of Nix keeping all versions of packages around and worried about disk space. There are two commands to keep in mind to resolve this.
The first of these is the nix store gc
command. This will delete packages
that are only used by profiles or generations that have been deleted. This will
detect if you just delete a profile with rm /path/to/profile
, allowing
you to delete and free up a profile entirely.
The second is the nix profile wipe-history
command. This will remove
past versions of a profile. By default it will delete all non-current
versions of a profile. But if you want to keep some history, you can
also use the nix profile wipe-history --older-than 30d
to delete
generations older than 30 days, for example.
With the usage of the package manager now covered, next time I'm going to discuss Nix's programming language.