First steps with Nix

You have installed Nix. Now let’s play with the nix command but without bothering to write any Nix expressions yet (we reserve that for the next tutorial). In particular, we will learn how to use packages from the nixpkgs repository and elsewhere.


Running a package

As of this writing, nixpkgs has over 80,000 packages. You can search them here. Search for “cowsay” and you will find that it is available in Nixpkgs. We can download and run the cowsay package as follows:

$ nix run nixpkgs#cowsay "G'day $USER"
< G'day srid >
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     || 
nix run

nix run command will run the specified package from the flake. Here nixpkgs is the flake, followed by the letter #, which is followed by the package (Derivation) name cowsay that is outputted by that flake. See Flake URL for details on the syntax.

Looking inside a package

What is a Nix “package”? Technically, a Nix package is a special Store path built using instructions from a Derivation, both of which reside in the Nix Store. To see what is contained by the cowsay package, look inside its Store path. To get the store path for a package (here, cowsay), run nix build as follows:

$ nix build nixpkgs#cowsay --no-link --print-out-paths

The cowsay Derivation produces two output paths, the second of which is the cowsay binary package (the first one is the separate documentation path), and if you inspect that 1 you will see the contents of it:

$ nix run nixpkgs#tree /nix/store/n1lnrvgl43k6zln1s5wxcp2zh9bm0z63-cowsay-3.7.0
├── bin
│   ├── cowsay
│   └── cowthink -> cowsay
└── share
    └── cowsay
        ├── cows
        │   ├──
        │   ├──
        │   ├──
        │   ├── ...
Nix Store & Store Paths

/nix/store is a special directory representing the Nix Store. The paths inside /nix/store are known as Store path. Nix fundamentally is, in large part, about manipulating these store paths in a pure and reproducible fashion; Derivation are “recipes” that does this manipulation, and they too live in the Nix Store.

Shell environment

One of the powers of Nix is that it enables us to create isolated shell environments containing just the packages we need. For eg., here’s how we create a transient shell containing the “cowsay” and “fortune” packages:

$ nix shell nixpkgs#cowsay nixpkgs#fortune 

From here, you can verify that both the programs are indeed in $PATH as indicated by the “bin” directory in their respective store paths:

$ nix shell nixpkgs#cowsay nixpkgs#fortune 
❯ which cowsay
❯ which fortune
❯ fortune | cowsay
/ The longer the title, the less \
\ important the job.             /
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
One-off command

Instead of creating a shell environment, you can also run commands one-off using the -c option. The above session can equally be achieved using:

nix shell nixpkgs#cowsay nixpkgs#fortune -c sh -c 'fortune | cowsay'

Installing a package

Declarative package management

This section explains how to install a package imperatively. For a better way of installing packages (declaratively), see home-manager.

Neither nix run nor nix shell will mutate your system environment, aside from changing the Nix Store. If you would like to permanently install a package somewhere under your $HOME directory, you can do so using nix profile install:

$ nix profile install nixpkgs#cowsay nixpkgs#fortune
$ which cowsay
$ which fortune

nix profile install installs symlinks under the $HOME/.nix-profile directory, which the Nix installer already added to your $PATH. For details, see the Nix manual.

These symlinks, ultimately, point to the package Store path outputs under the Nix Store, viz.:

$ readlink $(which fortune)

Note that this is the same path used by both nix build and nix shell. Each specific package is uniquely identified by their Store path; changing any part of its build recipe (including dependencies), changes that path. Hence, nix is reproducible.

How is nixpkgs fetched

So far we have been retrieving and installing software from the nixpkgs flake, which is defined in the GitHub repository: This information comes from the Flake registry:

$ nix registry list | grep nixpkgs
global flake:nixpkgs github:NixOS/nixpkgs/nixpkgs-unstable

A registry is simply a mapping of flake alias to Flake URL.

Adding to registry

You can add your own flakes to this registry as well. See the manual

We can find the current Git revision of nixpkgs used by our registry as follows:

❯ nix flake metadata nixpkgs --json | nix run nixpkgs#jq -- -r .locked.rev

From here, you can see the revision on GitHub.

The discerning readers may have noticed that the registry specifies only the branch (nixpkgs-unstable), but not the specific revision. Nix registry internally caches flakes locally and updates them automatically, thus the specific Git revision of nixpkgs used may change over time!

Pinning nixpkgs

To avoid the aforementioned automatic update, you can manually pin the registry entry for nixpkgs. In home-manager, we will see an automatic and declarative way of doing this (through flake inputs).

You are not required to use a registry. Without a registry, getting a package off nixpkgs would instead involve its fully qualified URL:

$ nix run github:NixOS/nixpkgs/nixpkgs-unstable#cowsay

Using software outside of nixpkgs

nixpkgs is not the only way to get software packaged by Nix. As you have seen immediately above, you can install programs from any flake by specifying its flake URL to the nix ? commands. For example, Emanote (which is used to build this very website) can be executed or installed directly off its flake on GitHub:

$ nix run github:srid/emanote

You can of course also install it to your home directory:

$ nix profile install github:srid/emanote

What’s next

Incidentally, we use the tree command, rather than ls, to look at the directory tree, using the package from nixpkgs of course (since it may not already be installed).
Links to this page