What is Nix?
Nix is a so-called functional package manager. Functional, as in functional programming. It introduces a new paradigm for managing software. Instead of merely downloading software onto your computer, it instead builds that software for you using a reproducible formula.
Each package you can install on your system is expressed as a piece of code, written in a functional programming language called nix.
Nix (the language) is declarative, and expressive. You need merely to declare what you would like the end state to be, as an expression, and nix (the package manager) takes care of building, installing, and configuring the end result for you.
What is a nix derivation?
In nix, all packages (or anything else nix can build) are actually called "derivations".
A nix derivation is an extension of a low-level build environment called the nix standard environment, or stdenv. All nix packages build on top of this environment to create their own, self contained, environment where the software has access to everything it needs to run.
The stdenv has access to some basic build tools like glibc, clang, coreutils, and make. It also has a pre-defined build workflow, expressed as a series of hooks that you can either extend, override, or leave alone, as the situation demands.
Packaging software in nix involves declaring your build-time and run-time configuration, environment variables, and dependencies for this environment. Package artifacts like binaries, config files, man pages, and readmes, are typically bundled alongside one another in the same location. External dependencies are built in separate derivations and symlinked into the target derivation at build time and run time.
Here is an example nix expression that builds the hello program from the GNU project.
Note
Notice how a single nix expression can include not only the location of the package source code, but also environment variables, build flags, automated tests, and metadata about the license model, and current maintainers of this tool.
A simple derivation can pack a lot of punch!
{
callPackage,
lib,
stdenv,
fetchurl,
nixos,
testers,
versionCheckHook,
hello,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "hello";
version = "2.12.2";
src = fetchurl {
url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz";
hash = "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm0x6nc4IGKs=";
};
# The GNU Hello `configure` script detects how to link libiconv but fails to actually make use of that.
# Unfortunately, this cannot be a patch to `Makefile.am` because `autoreconfHook` causes a gettext
# infrastructure mismatch error when trying to build `hello`.
env = lib.optionalAttrs stdenv.hostPlatform.isDarwin {
NIX_LDFLAGS = "-liconv";
};
doCheck = true;
doInstallCheck = true;
nativeInstallCheckInputs = [
versionCheckHook
];
# Give hello some install checks for testing purpose.
postInstallCheck = ''
stat "''${!outputBin}/bin/${finalAttrs.meta.mainProgram}"
'';
passthru.tests = {
version = testers.testVersion { package = hello; };
};
passthru.tests.run = callPackage ./test.nix { hello = finalAttrs.finalPackage; };
meta = {
description = "Program that produces a familiar, friendly greeting";
longDescription = ''
GNU Hello is a program that prints "Hello, world!" when you run it.
It is fully customizable.
'';
homepage = "https://www.gnu.org/software/hello/manual/";
changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${finalAttrs.version}";
license = lib.licenses.gpl3Plus;
maintainers = with lib.maintainers; [ stv0g ];
mainProgram = "hello";
platforms = lib.platforms.all;
identifiers.cpeParts.vendor = "gnu";
};
})
Reproducibility
Because nix is a functional language, all nix derivations have the following characteristics:
- They have a pre-determined set of inputs
- They have a deterministic output
- No dependencies may be undeclared
- Packages cannot reference or store any external state at build time
These rules allow the nix package manager to build fully reproducible packages, even at a bitwise level.
If the package builds successfully once, it will build successfully everywhere. No virtualization or containerization required. It all runs on your host system!
This makes it easy to define packages (or sets of packages) that are easy to share with your friends and colleagues.
The Nix Store
All nix packages are built in stdenv, which is hermetically sealed off from the rest of the host system. Afterward, all build artifacts are stored in a special location called the nix store under their own directory. Each directory is kept separate from one another by a unique hash of that package's source code and a list of its direct dependencies.
Here is a sample path in the nix store for the hello package:
Note
Notice how the package name and version are both a part of the directory name, just after the long hash prefix. If you decide to make your own version of the hello program by changing the source code, your new build won't override this already existing build. It will get stored under a different hash directory, but with the same package name and version tag.
This strict isolation makes it so installing software with nix will not collide with other packages in the nix store, or with those on your host system.
It also makes it possible to run multiple versions of the same program at the same time without suffering though dependency hell.
nixpkgs
Nix's built-in package definitions come from a tool called [nixpkgs](https://github.com/NixOS/nixpkgs/), which boasts a package set of over 100,000 actively maintained projects. When you use a vanilla nix installation, all of the packages that you install come from a package definition somewhere in nixpkgs.
We'll explore learning how to build our own packages using nix later on, but for now, we're going to focus on using some of the tools that come with nix and nixpkgs out of the box.
Installing Software
"Installing" a piece of software with nix is a kind of fuzzy idea.
Did you mean "download the software somewhere on my computer", or "let me use the software right now"? To nix, those are separate concepts. Nix's concept of "software that I can use right now" equates to "software that is in my $PATH".
Consider this command, which both installs the hello program and makes it usable immediately.
$ nix-shell -p hello
this path will be fetched (0.06 MiB download, 0.27 MiB unpacked):
/nix/store/10s5j3mfdg22k1597x580qrhprnzcjwb-hello-2.12.3
copying path '/nix/store/10s5j3mfdg22k1597x580qrhprnzcjwb-hello-2.12.3' from 'https://cache.nixos.org'...
$ hello
Hello, world!
In the above example, nix fetched a pre-built version of hello from a cache, resolved all of its dependencies, and then dropped the user into a temporary shell environment with the hello program available in the $PATH.
If you went a step further and called exit, you will drop back into your regular user shell. Calling the hello program will result in an error.
The hello program is still on your computer, stashed away in the nix store, but you can't call it because it isn't in your $PATH anymore.
This is just one way to install software on nix. As we'll learn, there are many more techniques you can use, most of which are configuration driven and rely on the nix language.
However, using the nix-shell command is perhaps the simplest and lowest level way to get used to the basic concept. And, it is also a great way to test out new software without disrupting your base system!
Installaing Nix
Nix is cross-platform, meaning you can use it on any host operating system.
There are a few different distributions of the nix package manager you can choose from, each with their own unique approach to community management, default configuration, and development processes.
All flavors of nix have a high degree interoperability. If in doubt, simply download the default distribution of nix.
Nix
The original nix package manager, created by Eelco Dolstra and maintained by the community.
- Installation instructions: https://nixos.org/download/
- Website: https://nixos.org
- Repository: https://github.com/nixos/nix
- Documentation: https://nix.dev/reference/nix-manual
Lix
A community fork of the nix package manager with a greater focus on language correctness, reducing regressions, and providing a better governance structure.
- Installation instructions: https://lix.systems/install/
- Website: https://lix.systems/
- Repository: https://git.lix.systems/lix-project/lix
- Documentation: https://docs.lix.systems/manual/lix/stable/
Determinate Nix
A fork of nix for use in enterprise systems. Distributed as an alternative installer for nix. Maintained by Determinate Systems, a company established by the original creator of nix. Has some features of the nix package manager enabled by default, such as flakes.
- Installation instructions and docs: https://docs.determinate.systems/
- Website: https://determinate.systems/nix/
- Repository: https://github.com/DeterminateSystems/nix-installer
Docker
You can optionally install the nix package manager as a Docker image for use in CI/CD pipelines. This website actually does that in order to build and deploy the docs you're reading right now. Neat!
- Installation instructions: https://nixos.org/download/#nix-install-docker
- Docker image: https://hub.docker.com/r/nixos/nix/
Further Reading
-
Official Nix Documentation
Read the official nix documentation in more detail and learn about the language internals with hands-on exercises.
-
Wombat's Book of Nix
Learn about some practical uses of nix for packaging software and sharing it with your colleagues. Great for learning about flakes!
-
Stdenv Docs
Learn about the nix standard environment, what it provides, how to extend it, and how it works.