TL;DR — Quick Summary
Nix is a purely functional package manager enabling reproducible builds and system configurations. Learn installation, flakes, dev shells, and NixOS basics.
Nix is a purely functional package manager that takes a fundamentally different approach to package management. Instead of modifying global system state, every package is built in complete isolation with its exact dependencies specified. This makes builds reproducible, rollbacks trivial, and multiple versions of the same software coexistable. This guide covers Nix from installation through real-world workflows.
Prerequisites
- Linux (any distribution) or macOS
- ~10 GB free disk space (Nix store grows over time)
curlfor installation- Basic command-line familiarity
Why Nix?
Traditional package managers like apt, dnf, and brew modify global system state. Installing a package changes shared libraries, updates global paths, and can break other software. Nix solves this fundamentally:
- Reproducibility: The same package definition always produces the exact same output
- Isolation: Every package has its own dependency tree — no DLL/shared library conflicts
- Atomic upgrades: Upgrades either complete fully or not at all — no broken intermediate states
- Rollback: Instantly revert to any previous system generation
- Multi-version: Run Python 3.10 and 3.12 simultaneously without containerization
Installation
Recommended: Determinate Systems Installer
curl --proto '=https' --tlsv1.2 -sSf -L \
https://install.determinate.systems/nix | sh -s -- install
This enables flakes and the new CLI by default, uses systemd for the daemon, and provides an uninstaller.
Official Installer
sh <(curl -L https://nixos.org/nix/install) --daemon
After installation, open a new terminal or source the profile:
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
Core Concepts
The Nix Store
Every package is stored at /nix/store/<hash>-<name>-<version>/. The hash incorporates all inputs (source, dependencies, build flags), ensuring that any change produces a different path:
/nix/store/a1b2c3d4...-nodejs-22.0.0/
/nix/store/e5f6g7h8...-nodejs-20.0.0/
Both versions coexist without conflict.
Searching and Installing Packages
# Search for packages (90,000+ available)
nix search nixpkgs nodejs
nix search nixpkgs ripgrep
# Install to user profile
nix profile install nixpkgs#nodejs_22
nix profile install nixpkgs#ripgrep
# Temporary shell with a package (no install)
nix shell nixpkgs#python312 nixpkgs#poetry
Nix Flakes for Projects
Flakes are the modern way to use Nix. Create flake.nix in your project root:
{
description = "My web application";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs_22
nodePackages.pnpm
postgresql_16
redis
just
];
shellHook = ''
echo "Development environment ready!"
echo "Node: $(node --version)"
'';
};
});
}
Enter the development shell:
nix develop # Enter the shell with all tools
nix develop -c just build # Run a single command in the shell
The flake.lock file pins exact versions. Commit it for team-wide reproducibility.
Comparison
| Feature | Nix | apt/dnf | Homebrew | Docker | Conda |
|---|---|---|---|---|---|
| Reproducibility | Excellent | Poor | Fair | Good | Fair |
| Multi-version | Native | Difficult | Difficult | Containers | Environments |
| Rollback | Native | Manual | Manual | Rebuild | Manual |
| Cross-platform | Linux + macOS | Linux only | macOS + Linux | Anywhere | Anywhere |
| Learning curve | Steep | Easy | Easy | Medium | Medium |
| Package count | 90,000+ | 60,000+ | 7,000+ | N/A | 20,000+ |
Real-World Scenario
Your CI pipeline uses Ubuntu 22.04 with Node 18, but a developer on macOS has Node 20, and another uses Fedora with Node 22. Tests pass locally but fail in CI. With a flake.nix, all three developers and CI use the exact same Node version, same native dependencies, same tool versions. Running nix develop on any machine produces an identical environment — no Dockerfile needed, near-zero startup time.
Gotchas
- Disk usage: The Nix store grows over time. Run
nix store gcperiodically to clean unused packages - Learning curve: The Nix language is functional and unfamiliar to most developers. Start with
nix shellandnix developbefore diving into the language - Binary caches: Nix downloads pre-built binaries from
cache.nixos.orgby default. Without cache hits, packages build from source (slow). Use Cachix for custom binary caches - macOS quirks: Some packages need Xcode command line tools. Run
xcode-select --installbefore installing Nix on macOS - Flakes are experimental: While widely used, flakes remain behind an experimental flag in the official installer. The Determinate Systems installer enables them by default
Troubleshooting
- Command not found after install: Open a new terminal or run
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh - Permission denied in /nix/store: The Nix daemon must be running. Check with
systemctl status nix-daemonand start withsudo systemctl start nix-daemon - Hash mismatch errors: Usually means a dependency’s source has changed. Run
nix flake updateto refresh the lock file - Build runs out of disk: Set
keep-derivations = falseandkeep-outputs = falsein/etc/nix/nix.conf, then runnix store gc
Summary
- Nix is a purely functional package manager providing reproducible, isolated package builds
- It installs alongside existing package managers without conflicts — packages live in
/nix/store - Flakes provide hermetic project definitions with lock files for team-wide reproducibility
nix developcreates instant development shells with precise dependency versions- 90,000+ packages are available, covering most development needs
- The learning curve is steep but the reproducibility guarantees save significant debugging time
- Use
nix store gcregularly to manage disk usage