Nix is a purely functional package manager that installs every package into its own immutable directory under /nix/store, named by a cryptographic hash of all its build inputs. In Linux package management it stands apart from APT, DNF or Pacman: instead of overwriting shared files in /usr, the Nix package manager lets many versions of the same software coexist, makes upgrades atomic and allows a return to any previous state with a single command.
Nix runs on most Linux distributions and on macOS, and it is the foundation of NixOS, a distribution whose entire system configuration is built the same way. The project also maintains Nixpkgs, a very large collection of package definitions that is among the biggest software repositories available for Linux.
How Nix builds and stores packages
Packages in Nix are described in the Nix language, a lazy, purely functional language. A package description evaluates to a derivation: a precise recipe listing source code, compiler, dependencies, build script and environment variables. The hash of that recipe determines the output path, for example /nix/store/ followed by a 32-character hash and the name and version of the package.
Because the path depends on every input, changing any dependency produces a new path rather than modifying an existing one. Builds run in a sandbox without network access and with only declared dependencies visible, which prevents hidden dependencies on whatever happens to be installed on the build machine.
| Concept | What it is | Why it matters |
|---|---|---|
| Nix store | Read-only directory /nix/store holding all packages | Packages never overwrite each other |
| Derivation | Complete build recipe with all inputs | Determines the hash and output path |
| Binary cache | Server with prebuilt store paths | Avoids compiling locally when the hash matches |
| Profile and generations | Symlink trees pointing to chosen store paths | Atomic switching and rollback |
| Garbage collector | Removes store paths no longer referenced | Reclaims disk space safely |
When a matching path exists in a binary cache, Nix downloads it instead of building it, so everyday installation is usually as fast as with conventional package managers. Packages are only compiled locally when they have been modified or are not cached.
Profiles, generations and rollbacks
A user environment in Nix is a profile: a tree of symbolic links into the store. Installing, removing or upgrading software creates a new generation of that profile and then switches one symlink to it. The switch is atomic, so an interrupted upgrade never leaves a half-updated system.
Older generations remain until they are deleted, which makes rollback trivial. On NixOS the same mechanism applies to the whole operating system, and earlier system generations appear as entries in the boot menu, so even a broken kernel or driver update can be undone by choosing the previous entry at startup.
Nix compared with other package managers
Nix shares goals with several tools in the Linux ecosystem but reaches them differently. The table compares it with traditional and container-style approaches.
| Approach | Installs into | Multiple versions | Rollback | Reproducibility |
|---|---|---|---|---|
| Nix | Hash-named paths in /nix/store | Yes, side by side | Built in, atomic | Strong by design |
| APT or DNF | Shared FHS paths such as /usr | Rarely | Limited, often by snapshots | Depends on repository state |
| Flatpak | Separate application bundles with runtimes | Partly, by branch | Supported for apps | Good for desktop apps |
| Docker images | Layered container filesystems | Yes, per image | By image tag | Depends on how images are built |
The decisive difference is that Nix treats a package as a pure function of its inputs. Conventional package managers describe the desired package names and versions, while Nix pins the entire dependency graph down to compilers and libraries.
Everyday use and development environments
Beyond installing software, Nix is widely used to create per-project development environments. A file in the repository lists required compilers, libraries and tools, and one command opens a shell where exactly those versions are available, without touching the rest of the system.
| Command | Purpose |
|---|---|
nix-shell or nix develop | Enter a project development environment |
nix profile install | Add a package to the user profile |
nix-env --rollback | Return the profile to the previous generation |
nixos-rebuild switch | Build and activate a new NixOS configuration |
nix-collect-garbage -d | Delete old generations and unreferenced store paths |
Flakes add a standard project structure with a lock file that pins exact input revisions, similar to lock files in language package managers. They are widely adopted, although they have long been marked experimental, so teams should agree on whether to use them before building workflows around them.
Limitations and common pitfalls
The main obstacle is the learning curve: the Nix language, derivations and the store model differ from everything most administrators know. Error messages from evaluation can be long and hard to interpret, and documentation is spread across several manuals and community resources.
Precompiled binaries from outside Nix often fail to run because they expect libraries and a dynamic loader at standard FHS paths that do not exist in the same form on NixOS. Workarounds include patching binaries, compatibility layers and FHS-style environments. Disk usage also grows because old generations keep their packages alive, so regular garbage collection is part of normal maintenance.
Understanding it simply
Nix is a way of installing software that never overwrites anything. Each program goes into its own sealed box with a unique label. That label is based on exactly how the program was made.
Because old boxes are kept, you can always go back to how things were yesterday. It is a bit like a word processor with a perfect undo button, but for your whole set of installed programs. Two projects can even use different versions of the same tool without getting in each other’s way.
Applied in practice
A team of six developers maintains a data processing service that needs a specific Python version, a pinned PostgreSQL client library and a particular build of GDAL. New team members used to spend most of their first day setting up their laptops, and the results still differed slightly between machines.
The team adds a Nix flake to the repository that declares all tools and libraries with exact versions. Now a new developer installs Nix, clones the repository and runs nix develop; within about ten minutes, mostly spent downloading from the binary cache, the full environment is ready. The continuous integration server uses the same definition, so the familiar works on my machine problem largely disappears, and a later upgrade is tested on a branch and rolled back with one command when a library change breaks a test.