Nix is a functional package manager for Linux and macOS which helps build reproducable setups.
Instead of installing packages globally, it stores each package in the Nix Store. In there, each package is placed in a directory named with a cryptographic hash of the package and all of its dependencies. This hash changes whenever a package is updated, so packages are never overwritten.
For example, installing the fish shell through Nix adds fish the the load path from Nix’s store directory:
/nix/store/dg6z33cy6jqqmclyg8wxx91lkc324lsj-fish-3.6.0/bin/fish
The packages are symlinked to by updating the shell’s load path, which allows for running shells with different versions of a program, or even spinning up a shell with a previously uninstalled program available.
After following the install instructions, use the nix run
command to run a program with Nix.
For example, to try the fish shell:1
nix run nixpkgs#fish\ --extra-experimental-features nix-command \ --extra-experimental-features flakes
This fetches the fish package from the nixpkgs package registry, downloads it to the Nix Store and starts it:
Welcome to fish, the friendly interactive shell Type help for instructions on how to use fish ~>
After closing the shell, fish is no longer in the load path:
which fish
fish not found
System configuration with Home Manager
Home Manager is a system for configuring user environments, built on Nix.
Set up a flake with the Home Manager template by running flake new
:
nix flake new ~/.config/nixpkgs -t github:nix-community/home-manager
{ description = "Home Manager configuration of Jane Doe"; inputs = { # Specify the source of Home Manager and Nixpkgs. nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs, home-manager, ... }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { homeConfigurations.jdoe = home-manager.lib.homeManagerConfiguration { inherit pkgs; # Specify your home configuration modules here, for example, # the path to your home.nix. modules = [ ./home.nix ]; # Optionally use extraSpecialArgs # to pass through arguments to home.nix }; }; }
Then, update the output system and username and remove the boilerplate:
{ description = "Home Manager configuration"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs, home-manager, ... }: let system = "x86_64-darwin"; pkgs = nixpkgs.legacyPackages.${system}; in { homeConfigurations.jeffkreeftmeijer = home-manager.lib.homeManagerConfiguration { inherit pkgs; modules = [ ./home.nix ]; }; }; }
For Home Manager’s configuration, create home.nix
.
It lists your use name and home directory, but also the stateVersion
, which determines the Home Manager release the configuration is compatible with.
The home.nix
file also sets up Home Manager to install and manage itself:
{ config, pkgs, ... }: { home = { username = "jeffkreeftmeijer"; homeDirectory = "/Users/jeffkreeftmeijer"; stateVersion = "22.11"; }; programs.home-manager = { enable = true; }; }
Finally, install Home Manager and apply the configuration:
nix run ~/.config/nixpkgs#homeConfigurations.jeffkreeftmeijer.activationPackage
Running the activationPackage
generates a flake.lock
file, which locks all packages to their currently installed versions for reproducability.
It only lists Home Manager now, but installed packages will be added to the list when they’re added.
Installing packages
To install a package, add it to home.packages
in home.nix
:
diff --git a/home.nix b/home.nix index 6f6f86d..12f9efe 100644 --- a/home.nix +++ b/home.nix @@ -5,6 +5,7 @@ username = "jeffkreeftmeijer"; homeDirectory = "/Users/jeffkreeftmeijer"; stateVersion = "22.11"; + packages = [ pkgs.git ]; }; programs.home-manager = {
Then, update the environment by running home-manager switch
:
home-manager switch
Starting Home Manager activation Activating checkFilesChanged Activating checkLaunchAgents Activating checkLinkTargets Activating writeBoundary Activating copyFonts Activating installPackages replacing old 'home-manager-path' installing 'home-manager-path' Activating linkGeneration Cleaning up orphan links from /Users/jeffkreeftmeijer Creating profile generation 2 Creating home file links in /Users/jeffkreeftmeijer Activating onFilesChange Activating setupLaunchAgents
After switching, the newly-installed package is available and symlinked to in the ~/.nix-profile
directory:
which git
/Users/jeffkreeftmeijer/.nix-profile/bin/git
The
nix run
command relies on thenix-command
andflakes
features. Both of these are currently experimental and disabled by default, but they’re enabled using the--extra-experimental-features
flag. To enable these features globally, setexperimental-featurs
innix.conf
:mkdir -p ~/.config/nix echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
experimental-features = nix-command flakes