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
registry into the default profile. If you followed the installer you should
now be able to run the
rg binary from the
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
> 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
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
- 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 '.*'.
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
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
fzf as you're done
with them now after the demo. In this case, you can run the
nix profile rollback
> nix profile rollback switching profile from version 2 to 1
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
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
> nix profile rollback --to 2 switching profile from version 1 to 2
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
> 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.
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.