Hello, this is a record of my journey learning Nix.
I'm writing this mainly as a quickstart and reference for my future self, but I hope others find it helpful.
This is not comprehensive. It will just go over the basics and some of the more useful features.
Note: This doc will be using the newer nix
cli interface and flakes.
Nix is a purely functional package manager. It helps to avoid the installation and versioning pain points of software development.
That doesn't sound like much. However...
Suppose you need golang, node, and python. You can create a shell environment that has those tools with a single command.
$ go; node; python
zsh: command not found: go
zsh: command not found: node
zsh: command not found: python
$ nix shell nixpkgs#go nixpkgs#nodejs nixpkgs#python3
$ go version
go version go1.23.3 darwin/arm64
$ node --version
v20.18.0
$ python --version
Python 3.12.7
$ exit
$ go; node; python
zsh: command not found: go
zsh: command not found: node
zsh: command not found: python
We were able to get access to golang, node, and python instantly. Actually, the initial run takes a few moments, but subsequent runs are fast!
We skipped the setup docs, we didn't edit our shell profile, and we didn't have to worry about PATH
or other environment variables.
Best of all, we didn't pollute the user environment. Everything is gone once you exit
or Ctrl-D
the shell.
Other benefits?
Nix builds packages in isolation from each other. This ensures that they are reproducible and don't have undeclared dependencies, so if a package works on one machine, it will also work on another.
Nix makes it trivial to share development and build environments for your projects, regardless of what programming languages and tools you're using.
The easiest way to install Nix (Linux, macOS, WSL2) is to use The Determinate Nix Installer.
It installs Nix with flake support and the unified CLI feature already enabled. It also stores a receipt for the install process to allow for a clean uninstall.
Note: Directions to upgrade or uninstall can be found in their GitHub repo.
We've already seen how a shell environment can be created with nix shell
.
With nix run
we can skip creating the shell and run programs directly.
$ nix run nixpkgs#cowsay Hello, Nix!
$ nix run nixpkgs#lolcat -- --help
$ nix run nixpkgs#fortune | nix run nixpkgs#lolcat
A trivial script with non-trivial dependencies.
#! /bin/bash
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
This script fetches XML content from a URL, converts it to JSON, and formats it for better readability.
It requires curl, xml2json, jq, and bash. If any of these dependencies are not present on the system running the script, it will fail partially or altogether.
With Nix, we can declare all dependencies explicitly, and produce a script that will always run on any machine that has Nix.
#! /usr/bin/env nix
#! nix shell nixpkgs#bash nixpkgs#curl nixpkgs#jq nixpkgs#python312Packages.xmljson
#! nix --ignore-environment --command bash
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
- The
--ignore-environment
option prevents the script from implicitly using programs that may already exist on the system. - The
--command
option specifies the interpreter that will be invoked by nix shell after it has obtained the dependencies and initialized the environment.
Notice how nix shell
was only specified once when using multiple nix shell shebangs.
As long as the system has Nix, scripts can be written in any language without worrying about dependencies.
#! /usr/bin/env nix
#! nix shell nixpkgs/nixos-24.11#python3
#! nix --ignore-environment --command python
print("n", "n^2")
for n in range(1, 10):
print(n, n * n)
In this example we've specified the nixos-24.11
stable channel. This helps reproducibility. You can even use a git commit hash of the Nixpkgs repository for further granularity.
We can create a file that defines an environment. This can be shared with anyone to recreate the same environment on a different machine.
Create a flake.nix
file:
{
description = "A fun shell environment";
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 = import nixpkgs { inherit system; config = {}; overlays = []; };
in {
devShells.default = pkgs.mkShellNoCC {
# Packages to include
packages = with pkgs; [
cowsay
lolcat
python3Full
];
# Define environment variables
env = {
GREETING = "Hello, Nix!";
};
# Run a shell script on environment startup
shellHook = ''
echo $GREETING | cowsay | lolcat
'';
};
}
);
}
Enter the environment by running nix develop
in the same directory as flake.nix
.
Note: If the directory you're working in is a git repository you may get an error indicating flake.nix
doesn't exist. Stage the file with git add
to get nix to recognize it. I don't know. It's weird like that.
If you make changes to flake.nix
just exit
or Ctrl-D
to exit the environment and restart it with nix develop
.
Without pinning or locking your tools and dependencies to specific versions you'll eventually hit the point of development where a bug is affecting others but somehow it "works on my machine". It's often very unpleasant and very difficult to debug.
We've seen from an earlier example how to pin to a specific Nix channel. The channels can be found at status.nixos.org.
Tip: Which channel branch should I use?
Go to https://search.nixos.org/packages or use the command line.
$ nix search nixpkgs neovim
The key difference between wrapped and unwrapped packages in NixOS is that wrapped packages are configured to work seamlessly within the NixOS environment, while unwrapped packages are the raw, unmodified versions.
In most cases, users should install the wrapped version of a package, as it is preconfigured to work correctly on NixOS. The unwrapped version is primarily used when further customization or overriding of the package is required, as it serves as the base for creating a new wrapped derivation with additional modifications.
tbd
Below are some of the main sources I've used.
- GitHub: DeterminateSystems/nix-installer
- GitHub: NixOS/nix
- GitHub: NixOS/nixpkgs
- Nix Reference Manual: nix run
- Nix Reference Manual: nix search
- Nix Reference Manual: nix shell
- nix.dev
- nix.dev: Best Practices
- Zero to Nix
Additional resources: