Back to Linux guides

Getting Started with Nix

Stanley Ulili
Updated on December 13, 2025

Nix is a powerful open-source package manager that works across Linux, macOS, and Windows via WSL. It provides declarative package management with reproducible builds and atomic upgrades that keep your development environment consistent across machines.

In this guide, you'll learn how Nix approaches package management differently, how to use it effectively, and how it solves problems that traditional package managers can't handle.

Prerequisites

Before diving into Nix, make sure your system meets these requirements:

  • macOS: macOS 10.15 (Catalina) or later with at least 2GB of free disk space
  • Linux: Any modern distribution with kernel 2.6 or later and sudo access
  • Windows (WSL): Windows 10/11 with WSL2 and a Linux distribution installed (Ubuntu works well)

Nix runs in user space and doesn't require special permissions beyond what you'd give any package manager during installation.

What is Nix and why use it?

Screenshot of Nix website homepage

Before exploring Nix's technical capabilities, understanding its philosophy reveals why it represents such a fundamental shift in package management—especially for macOS users.

The declarative approach

Nix distinguishes itself from traditional package managers through its purely functional and declarative design.

Traditional package managers like Homebrew use an imperative approach. You run sequential commands that modify your system's state: brew install wget, then later brew uninstall wget. Each command tells the system how to change. If you want to track what you've installed, you need to remember or document every command you've run.

Nix takes a declarative approach instead. You write a configuration file describing the final state you want: "my system should have wget, neovim, and zellij installed." Run a single command, and Nix calculates the necessary steps to match that declaration. Remove wget from the file and rebuild—Nix automatically uninstalls it without explicit removal commands.

This distinction matters because your entire system configuration becomes a versioned text file. Store it in a Git repository, and you gain version control over your machine's setup. Getting a new Mac? Clone your repository, run one command, and Nix replicates your entire environment—every package, every setting—identically.

Reliability through immutability

Nix installs packages into unique directories within the Nix store at /nix/store. Each package is immutable—it never changes after installation.

When you update a package, Nix builds the new version in a separate directory, then atomically switches a symlink to point to it. Updates can never produce broken, half-configured states. If an upgrade fails, your system remains untouched because nothing was modified in place.

This architecture enables you to have multiple versions of the same package installed simultaneously without conflicts. You can also roll back to any previous configuration instantly—not just the last one, but any historical state you've created.

From NixOS to nix-darwin

Nix originated in 2003 from Dutch computer scientist Eelco Dolstra's PhD research. His work produced the Nix package manager, the Nix programming language (a functional language for writing configurations), and NixOS—a Linux distribution built entirely on Nix principles. On NixOS, everything from the kernel to user applications is managed declaratively.

For macOS users, the nix-darwin project brings this ecosystem to your Mac. It extends Nix's declarative configuration model to manage not just packages, but macOS system settings, services, and environment variables. By hooking into Darwin—the Unix-like foundation of macOS—nix-darwin provides unified control over your entire machine configuration.

Throughout this guide, you'll see how Nix's unique approach translates into practical benefits for your daily development work.

Installing Nix

Getting Nix running on your machine requires a single installation command that sets up everything automatically.

The official installer configures Nix for your user account without affecting system-wide software. This means you can experiment safely—if you decide Nix isn't for you, uninstalling leaves your system unchanged.

Open your terminal and run:

 
sh <(curl -L https://nixos.org/nix/install)

The installer explains each step as it progresses. It creates the /nix directory where all packages live and configures your shell to recognize Nix commands. You'll need to enter your password when prompted.

Installation typically takes 5-10 minutes depending on your connection speed. The script downloads the Nix binaries and sets up the package database.

After installation completes, you need to reload your shell configuration. The installer shows the exact command, but it's usually:

 
source ~/.nix-profile/etc/profile.d/nix.sh

For permanent setup, the installer adds this to your shell's configuration file (.bashrc, .zshrc, or similar) so Nix works in future terminal sessions automatically.

Verify the installation by checking the version:

 
nix --version
Output
nix (Nix) 2.33.0

Perfect! Nix is ready to manage packages on your system.

Getting started with Nix

Let's explore how Nix handles package installation and management. The commands differ from traditional package managers, but the underlying concepts become clear quickly.

Installing software with Nix uses profiles—think of these as named collections of packages. Install a package into your user profile like this:

 
nix-env -iA nixpkgs.ripgrep

The -iA flag means "install by attribute"—you're referencing the package by its exact name in the Nix package repository. The installation output shows what's happening:

Output
installing 'ripgrep-15.1.0'
these 8 paths will be fetched (10.9 MiB download, 49.4 MiB unpacked):
  /nix/store/xm08aqdd7pxcdhm0ak6aqb1v7hw5q6ri-gcc-14.3.0-lib
  /nix/store/shs2lyjm5yv7fmg4pwyvrlv9zr4macmv-gcc-14.3.0-libgcc
  /nix/store/xx7cm72qy2c0643cm1ipngd87aqwkcdp-glibc-2.40-66
  /nix/store/hxcmad417fd8ql9ylx96xpak7da06yiv-libidn2-2.3.8
  /nix/store/3rkccxj7vi0p2a0d48c4a4z2vv2cni88-libunistring-1.4.1

Each package gets installed to a unique path in /nix/store identified by a cryptographic hash. Nix then creates symlinks in your profile so you can run the commands.

Test that it works:

 
rg --version
Output
ripgrep 15.1.0

features:+pcre2
simd(compile):+SSE2,-SSSE3,-AVX2
simd(runtime):+SSE2,+SSSE3,+AVX2

PCRE2 10.45 is available (JIT is available)

You can install multiple packages at once:

 
nix-env -iA nixpkgs.git nixpkgs.nodejs nixpkgs.python312

Want to see what you've installed?

 
nix-env -q
Output
git-2.47.1
nodejs-22.12.0
python3-3.12.8
ripgrep-14.1.1

Searching for available packages works through the command line or the Nix package search website. From the terminal:

 
nix-env -qaP | grep postgresql
Output
nixpkgs.postgresql_14  postgresql-14.15
nixpkgs.postgresql_15  postgresql-15.10
nixpkgs.postgresql_16  postgresql-16.6
nixpkgs.postgresql     postgresql-17.2

The first column shows the attribute path (what you use with -iA), and the second shows the actual package name and version.

Get detailed information about a package before installing:

 
nix-env -qa --description ripgrep

This shows the version, description, and where it's available in the package repository.

Nix maintains a clear separation between packages in the store and packages in your profile. This distinction enables the rollback and garbage collection features you'll use later.

Managing packages with Nix

Nix's approach to package maintenance differs significantly from traditional package managers. Instead of modifying packages in place, it creates new generations of your profile.

Updating your package list fetches the latest package definitions:

 
nix-channel --update
Output
unpacking channels...

This updates the nixpkgs channel—Nix's equivalent of a package repository. Think of channels as streams of package versions that you subscribe to.

Upgrade all installed packages to their latest versions:

 
nix-env -u
Output
upgrading 'git-2.47.1' to 'git-2.48.0'
upgrading 'nodejs-22.12.0' to 'nodejs-22.13.1'

Nix downloads new versions without touching the old ones. Both versions exist in /nix/store simultaneously.

Upgrade a specific package only:

 
nix-env -u nodejs

Remove packages you no longer need:

 
nix-env -e nodejs

This removes the package from your profile but leaves it in the store. The old package versions remain available if you need to roll back.

Here's where Nix's generation system becomes powerful. Every time you install, upgrade, or remove packages, Nix creates a new generation of your profile. List all generations:

 
nix-env --list-generations
Output
   1   2024-11-15 10:30:22
   2   2024-11-20 14:15:33
   3   2024-12-01 09:45:12
   4   2024-12-10 16:22:45   (current)

If an upgrade breaks something, roll back to the previous generation instantly:

 
nix-env --rollback
Output
switching from generation 4 to 3

Your environment reverts to exactly how it was before the upgrade. No reinstallation, no configuration changes—just an atomic switch to the previous state.

You can also switch to any specific generation:

 
nix-env --switch-generation 2

Over time, the Nix store accumulates packages from old generations. Reclaim disk space by removing packages nothing references anymore:

 
nix-collect-garbage

This removes unreferenced packages but preserves all generations. To delete old generations and their packages:

 
nix-collect-garbage -d
Output
removing old generations of profile /nix/var/nix/profiles/per-user/stanley/profile
finding garbage collector roots...
deleting garbage...
deleting '/nix/store/abc123...-nodejs-22.12.0'
deleting '/nix/store/def456...-git-2.47.1'
freed 543.2 MiB

Check which packages depend on a specific package:

 
nix-store -q --referrers /nix/store/xyz789...-openssl-3.0.15

This shows everything that needs that particular package to function.

See what a package depends on:

 
nix-store -q --references $(which ripgrep)
Output
/nix/store/abc123...-glibc-2.39-52
/nix/store/def456...-gcc-13.2.0-lib
/nix/store/ghi789...-ripgrep-14.1.1

If something seems broken, Nix can verify package integrity:

 
nix-store --verify --check-contents

This confirms that packages in the store match their expected cryptographic hashes.

The generation system and garbage collection work together to give you both safety and space efficiency. You can experiment freely knowing you can always roll back, then clean up old versions when you're confident in your current setup.

Development environments with Nix

Beyond managing system packages, Nix excels at creating isolated development environments. This feature addresses one of the most common developer frustrations—dependency conflicts between projects.

A Nix shell creates a temporary environment with specific packages available. You can enter a shell with any tool without installing it permanently:

 
nix-shell -p python312 python312Packages.requests
Output
these 15 paths will be fetched (45.3 MB download)...
downloading...

[nix-shell:~]$

Inside this shell, Python 3.12 and the requests library are available:

 
python --version
Output
Python 3.12.8
 
python -c "import requests; print(requests.__version__)"
Output
2.31.0

Exit the shell with exit or Ctrl+D. The packages disappear from your environment immediately—they were only available inside that shell session.

This temporary environment approach works perfectly for quick experiments or running unfamiliar tools. Want to try a different Node.js version without affecting your installed version?

 
nix-shell -p nodejs-18_x
 
node --version
Output
v18.20.5

For projects requiring specific dependencies, create a shell.nix file in your project directory. This defines the environment declaratively:

shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.python312
    pkgs.python312Packages.django
    pkgs.python312Packages.psycopg2
    pkgs.postgresql_16
  ];

  shellHook = ''
    echo "Django development environment loaded"
    export DATABASE_URL="postgresql://localhost/mydb"
  '';
}

Navigate to your project directory and run:

 
nix-shell
Output
Django development environment loaded

[nix-shell:~/myproject]$

Now you have Python 3.12, Django, PostgreSQL client libraries, and PostgreSQL 16 available. The shellHook runs automatically when entering the shell, setting up environment variables or starting services.

Every developer on your team who runs nix-shell in this project gets the identical environment. No more "works on my machine" problems caused by different installed versions.

For even more reproducibility, use nix-shell --pure. This strips away all your existing environment variables and packages, giving you only what's declared in shell.nix:

 
nix-shell --pure

This mode reveals hidden dependencies—if your project works in a pure shell, it will work on any machine with Nix.

You can also run single commands in a Nix environment without entering the shell interactively:

 
nix-shell -p nodejs --run "node script.js"

This runs the command with Node.js available, then exits. Perfect for scripts or CI/CD pipelines.

For more complex projects, create multiple shell environments in separate files:

shell-dev.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.nodejs
    pkgs.nodePackages.typescript
    pkgs.nodePackages.eslint
  ];
}
shell-prod.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.nodejs
  ];
}

Enter different environments with:

 
nix-shell shell-dev.nix
 
nix-shell shell-prod.nix

The development shell includes linting and TypeScript compilation tools, while the production shell has only the Node.js runtime.

Teams can commit shell.nix files to version control. This documents dependencies explicitly and ensures consistent environments across local development, testing, and CI systems.

Nix shells transform dependency management from implicit (hoping everyone has the right versions installed) to explicit (declaring exactly what your project needs).

Final thoughts

This article covered Nix's fundamental features to help you manage software more reliably. With Nix, you get reproducible package installations, atomic rollbacks, and isolated development environments that solve real problems traditional package managers can't address.

To explore advanced features like building custom packages, writing more complex expressions, and using NixOS, visit the official Nix documentation.

Thanks for reading!

Got an article suggestion? Let us know
Next article
Ansible
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.