nix
This notes assume to have the experimental-features = nix-command flakes enabled.
nix.conf (ref)
nix config show
# experimental-features
# enable additional experimental features
# flake-registery
# url of global registry
# tarball-ttl
# time the downloaded tarball is treated as valid
nix search (ref)
nix search <installable> <regex>
# Flakeref from registry.
nix search nixpkgs glibc
# Explicit flakeref.
nix search nixpkgs/nixos-25.05 glibc
nix run (ref)
nix run <installable> -- <args>
nix run nixpkgs#strace -- -o strace.txt /bin/ls
nix run nixpkgs/nixos-25.05#emacs
nix shell (ref)
# Enter interactive shell with pkgs in PATH.
nix shell <installable> [<installable>]
nix shell nixpkgs#qemu nixpkgs#zig nixpkgs/nixos-25.05#strace
nix profile (ref)
The history allows to install packages, update them and easily rollback.
# Install/remove/upgrade package(s) (creates a new history version).
nix profile install <installable>
nix profile remove
nix profile upgrade --all
# List installed packages.
nix profile list
# Show different hisitory versions.
nix profile history
# Rollback profile history.
nix profile rollback [--to N]
nix store (ref)
# Show all alive store paths (paths with active reference).
nix-store --gc --print-live
# Show all dead store paths (paths w/o active reference).
nix-store --gc --print-dead
# Run garbage collection on nix store. This deletes all paths which are no
# gcroots and any dependency of any gcroot.
nix store gc
# Delete single path from store.
nix store delete <path>
# gcroots are for example created automatically when installing packages into
# the profile or when building a flake locally.
# gcroots are located under /nix/var/nix/gcroots
#
# One can also create mamual gcroots (eg for tmp operation).
> nix-store --gc --print-dead | grep 'binutils-2.44$'
/nix/store/wcv8n2k53w8sbffpf716gxj6mjb5jf26-binutils-2.44
> mkdir -p /nix/var/nix/gcroots/tmp
> ln -s /nix/store/wcv8n2k53w8sbffpf716gxj6mjb5jf26-binutils-2.44 /nix/var/nix/gcroots/tmp/keep-binutils
> nix-store --gc --print-dead | grep 'binutils-2.44$'
> nix-store --gc --print-live | grep 'binutils-2.44$'
/nix/store/wcv8n2k53w8sbffpf716gxj6mjb5jf26-binutils-2.44
nix flake (ref)
Flakes provide a description for reproducible builds.
# Create a new flake in a folder with name.
nix flake new <name>
# Create a new flake from given template.
nix flake new -t <template> <name>
# Create a new flake with the c-hello output from the templates flake.
nix flake create -t templates#c-hello myflake
# Show the outputs of a flake
nix flake show <flake>
# Show outputs from the default "templates" flake.
nix flake show templates
# Assumes '.' as flake (which effectively means flake.nix in cwd).
nix flake show
# Show the derivation of an installable in pretty format.
nix derivation show <installable>
# Show the derivation of the glibc output from the nixpkg flake.
nix derivation show nixpkgs#glibc
# Assumes '.' as flake.
nix derivation show
# Show a flakes metadata (eg store path, inputs, description, ..)
nix flake metadata <flake>
# Update the versions in the flake.lock file.
nix flake update
Use
nix registry listto list available global flakes ornix registry pin <flake>, to pin a flake to a fixed version.Documentation for installable syntax.
a bare bones flake
The following shows a bare bones flake using the
builtins.derivation function to define an output called
moose. The builtin provides an empty environment.
In general, a derivation is a declarative description of how to run an executable (builder) on a set of inputs (input derivations aka dependencies) to produce a set of outputs (store paths).
# The flake is effectively an attribute set with a standard set of attributes.
{
description = "bare bones (flake)";
# Input(s) of the flake.
# Each input attribute is passed into the output function below.
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
# The 'outputs' attribute is a function, accepting an attribute set with a
# self reference and all the inputs specified above.
# The function shall return an attribute set with different outputs.
# There are standardized outputs for different commands such as:
# > nix build
# > nix develop
outputs = { self, nixpkgs }: let
# Define local variables with let .. in construct.
system = "x86_64-linux";
# Import another flake, passing an attribute set.
# inherit system, is effectively system = system;
pkgs = import nixpkgs { inherit system; };
in
# Return attribute set.
{
# Some output.
moose = builtins.derivation {
# -- Required attributes.
inherit system;
name = "bone";
builder = "${pkgs.bash}/bin/bash";
# -- Optional attributes.
args = [ "-c" "echo hello > $out" ];
};
};
}
Understanding this example is crucial as any abstractions such as the
nixpkgs.stdenv builds on top of this.
One can inspect and build the derivation as follows.
# Show the derivation for the output "moose".
> nix derivation show .#moose
{
"/nix/store/q2l7g7myy5gmks7jml6hz2jd73yybplq-bone.drv": {
...
"inputDrvs": {
"/nix/store/d2gv7y7i7bw79dpfskzzk2g4h6mc0677-bash-interactive-5.2p37.drv": {
}
# Build the output 'moose' for the flake.nix in the cwd ('.').
> nix build .#moose ; cat result
hello
A derivation also defines a set of
outputs, which by default is["out"]. Outputs are passed as environment variables to the builder, from where they can be used as install targets for example.
An interesting point to observe is that nix computed bash as input derivation (inputDrv) and hence as input dependency for this output.
a stdenv flake
The stdenv library from the nixpkgs flake provides the
mkDerivation function to simplify building packages (defining derivations).
Many tools commonly used to build software are available out of the box.
Specifying build and runtime dependencies can also be done easiy.
The default builder provided by the mkDerivation function, organizes the build
into multiple phases like config, build and install. When defining the
derivation these phases can be easily overwritten.
Alternatively there is stdenvNoCC.mkDerivation which provides an environment
without a C compiler.
The example show a simple build and installation of a C file.
{
description = "stdenv (flake)";
inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; };
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
# Enable support for 'nix fmt'.
formatter.${system} = pkgs.nixfmt;
# This is the default output that will be build & run if no output is
# specified, when running the following in the flakes directory.
# > nix build
# > nix run
# > nix log
packages.${system}.default = pkgs.stdenv.mkDerivation {
# Either specify 'name' or 'pname & version'.
# In the latter case, 'name' will be ${pname}-${version}.
pname = "test";
version = "0.1";
# There are different builtins to also unpack or fetch tarballs.
src = ./src;
# The default builder of the stdenv defines many different phases that
# can be overwritten.
buildPhase = ''
gcc -O2 -g -o test test.c
'';
installPhase = ''
mkdir -p $out/bin
cp test $out/bin
'';
};
};
}
Use
NIX_DEBUG=[0-7]to enablestdenvdebug logging. Usenix log [<installable>]to inspect the build log.
One can also define multiple output directories using the outputs attribute
in the derivation. Each output turns into an environment variable of the same
name. Use nix derivation show to inspect the outputs and the environment the
builder inherits.
For example the glibc package in the nixpkgs flake provides multiple outputs.
> nix derivation show nixpkgs#glibc
..
"outputs": {
..
"out": {
"path": "/nix/store/g3s0z9r7m1lsfxdk8bj88nw8k8q3dmmg-glibc-2.40-66"
},
"static": {
"path": "/nix/store/lfj0w1r0ppj2swb11pz8vkppmsy1rf6y-glibc-2.40-66-static"
}
},
..
a development shell flake
The nixpkgs flake also provides the mkShell derivation
function (specialization of mkDerivation), which can be used to easily define
a development environment.
Alternatively there is also mkShellNoCC which provides
an environment without a C compiler.
The example shows an environment with the zig compiler and zls, the zig lsp
server. Running nix develop will drop into a shell where these packages are
available.
{
description = "dev shell (flake)";
inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; };
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
devShells.${system}.default = pkgs.mkShell {
packages = [
pkgs.zig
pkgs.zls
];
# https://github.com/NixOS/nixpkgs/issues/270415
shellHook = ''
unset ZIG_GLOBAL_CACHE_DIR
'';
};
};
}
When launching a development shell, the environment of the current shell is
inherited. Using -i clears the environment when entering the dev shell, using
-k certain variables can be kept.
For example the following command will only inherit $USER, $HOME, $DISPLAY
into the development shell.
nix develop -i -k USER -k HOME -k DISPLAY
One can also just run a command in the development shell.
nix develop --command bash -c "mkdir build && cd build && cmake .. && make"
flake default targets
When called without an explicit output, certain nix commands have default outputs. The following gives the most relevant.
nix build
nix run
nix log
# uses -> packages.<SYSTEM>.default
nix develop
# uses -> devShells.<SYSTEM>.default
a flake for multiple systems
The nixpkgs flake also provides lots of useful library functions. One example
is the nixpkgs.lib.genAttrs function, which allows to
create an attribute set from a list of keys and a lambda function which computes
the values of the attribute set.
nix-repl> pkgs.lib.genAttrs [ "a" "b" ] (arg: "some-value-${arg}")
{
a = "some-value-a";
b = "some-value-b";
}
This can be used to build a flake to support multiple systems. The following shows a small example for two systems which defines a dev shell and a formatter.
{
description = "a basic flake for multiple systems";
inputs.nixpkgs.url = "nixpkgs";
outputs =
{ self, nixpkgs }:
let
# forSystems is a function which takes a lambda (String -> Any).
# genAttrs will call the lambda for each list element and finally return
# an attribute set where the keys are the values in the list and the
# values are the return values from the lambda invocation.
forSystems = nixpkgs.lib.genAttrs [
"x86_64-linux"
"aarch64-linux"
];
# pkgs is a function which takes a system as argument and imports the
# nixpkgs for the given system.
pkgs = system: import nixpkgs { inherit system; };
in
{
# Generate formatter.${system} attribute set for each supported system.
formatter = forSystems (system: (pkgs system).nixfmt);
# Generate devShells.${system}.default attribute set for each supported
# system.
devShells = forSystems (system: {
default =
# The with statement brings the result of calling (pkgs system) into
# scope for the next expression.
with (pkgs system);
mkShell {
packages = [
strace
]
# An example how to customize per system.
++ nixpkgs.lib.optionals (system == "x86_64-linux") [ gdb ]
++ nixpkgs.lib.optionals (system == "aarch64-linux") [ lldb ];
};
});
};
}
a flake for multiple systems from scratch
The following shows how to build the nixpkgs.lib.genAttrs utility from scratch
with nix builtins. This mainly serves as learning but may come in handy when
doing work without nixpkgs.
{
description = "a flake for multiple systems from scratch";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs =
{ self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
# "forSystems" is a function, which takes as argument a function.
# It then calls the function passed for each value in the list "systems",
# and passes the nixpkgs imported for the given system.
# Finally it returns an attribute set where each key is the system in the
# "systems" list and the value is what is returned from the invoked user
# function.
#
# The pseudo code below shows the output.
# {
# systems[0] = f pkgs;
# systems[1] = f pkgs;
# ...
# }
#
# The "listToAttrs" builtin turns a list of name / value pairs (attribute
# sets) into an attribute set.
# > listToAttrs [ { name = "A"; value = "a"; } ... ] -> { A = "a"; ... }
#
# The "map" builtin takes a function and a list, then calls the function
# on each list value and replaces the value with the result of the function.
# > map (v: "m${v}") [ "a" "b" ... ] -> [ "ma" "mb" ... ]
forSystems =
f:
builtins.listToAttrs (
builtins.map (system: {
name = system;
value = f (import nixpkgs { inherit system; });
}) systems
);
in
{
formatter = forSystems (pkgs: pkgs.nixfmt);
devShells = forSystems (pkgs: {
default = pkgs.mkShell {
packages = [
pkgs.strace
];
};
});
};
}
nix lang basics
Nix is a functional language, where everything is an expression. The following shows enough nix lang to come quite far.
$ nix repl
nix-repl> 1 + 2
3
# String interpolation.
# (Assignment operation special to repl).
nix-repl> foo = "bar"
nix-repl> "hello ${foo}"
"hello bar"
# Attribute sets (kv).
nix-repl> { a = 1; b = 2; }
{
a = 1;
b = 2;
}
# Joining attribute sets.
nix-repl> { a = 1; } // { b = 2; }
{
a = 1;
b = 2;
}
# List, elements are separated by space.
nix-repl> [ 1 2 ]
[
1
2
]
# Joining lists.
nix-repl> [ 1 ] ++ [ 2 ]
[
1
2
]
# Simple function with argument.
nix-repl> f = a: a + 2
nix-repl> f 4
6
# Function taking an attribute set (? for default args).
nix-repl> f = { a, b ? 2 }: a + b
nix-repl> f { a = 1; b = 2; }
3
nix-repl> f { b = 1; a = 2; }
3
nix-repl> f { a = 1; }
3
# Function taking an attribute set w/o explicitly naming each input.
# Here, inp will bind to the whole input attribute set.
nix-repl> f = { a, b, ... } @ inp : a + inp.b + inp.c
nix-repl> f { b = 1; a = 2; c = 3; }
6
# Defining local variables with "let .. in".
nix-repl> let x = 1; in x + 3
4
nix-repl> x
error: undefined variable 'x'
# Let .. in expression returning an attribute set (handy when defining derivations).
nix-repl> let x = "abc"; in { name = "bar ${x}"; }
{ name = "bar abc"; }
# With to bring names into scope for the next expression.
nix-repl> x = { a = 1; b = 2; }
nix-repl> with x; [ a b ]
[
1
2
]
# Show all builtin functions.
nix-repl> builtins
{
abort = «primop abort»;
add = «primop add»;
..
# Factoring out code into multiple files.
# default.nix
# { x, y }: {
# a = x + 10;
# b = y + 20;
# }
#
# Import nix file by name ..
nix-repl> myfn = import ./default.nix
# .. or use short form for default.nix.
nix-repl> myfn = import ./.
nix-repl> myfn { x = 1; y = 2; }
{ a = 11; b = 22; }
nix-repl> x = 10
# 'inherit x;' is equal to 'x = x;' in the following.
nix-repl> myfn { inherit x ; y = 2; }
{ a = 20; b = 22; }
The
importbuiltin reads a nix expression from a file and evaluates it to return its value. For exampleimport ./default.nix { x = 1; y = 2; }is equivalent to(import ./foo.nix) { x = 1; y = 2; }. Firstimportreads and evaluates the nix expression infoo.nix, which returns the function (the value), which is then called with the attribute set as input.
useful builtins
# The "map" builtin takes a function and a list, then calls the function on
# each list value and replaces the value with the result of the function.
nix-repl> builtins.map (val: "some val ${val}") ["a" "b"]
[
"some val a"
"some val b"
]
# The "listToAttrs" builtin takes a list of name / value pairs (attribute sets)
# and turns the list into a attribute set build from the name / value pairs.
nix-repl> builtins.listToAttrs [{ name = "A"; value = "a"; } { name = "B"; value = "b"; }]
{
A = "a";
B = "b";
}
access flakes in the repl
# Access a flake inside repl.
nix-repl> pkgs = builtins.getFlake "nixpgs"
nix-repl> pkgs.legacyPackages.x86_64-linux.stdenvNoCC.mkDerivation
nix-repl> pkgs.legacyPackages.x86_64-linux.llvm # tab
pkgs.legacyPackages.x86_64-linux.llvm pkgs.legacyPackages.x86_64-linux.llvmPackages_git
pkgs.legacyPackages.x86_64-linux.llvm-manpages pkgs.legacyPackages.x86_64-linux.llvmPackages_latest
pkgs.legacyPackages.x86_64-linux.llvmPackages pkgs.legacyPackages.x86_64-linux.llvm_12
# Alternatively flakes can be put into the lookup path.
# arg: nix repl -I nixpkgs=flake:nixpkgs
# env: NIX_PATH=nixpkgs=flake:nixpkgs
# conf: extra-nix-path = nixpkgs=flake:nixpkgs
# Print the current nix path.
nix-repl> :p builtins.nixPath
type example
The following gives an example how to read the type definitions commonly found
in the documentation. For that the type of the
nixpkgs.lib.genAttrs function is explored.
genAttrs :: [ String ] -> (String -> Any) -> AttrSet
# genAttrs is the name of the function.
# It takes as arguments
# - a list of strings - [ Strings ]
# - and a function which maps strings to anything - (String -> Any)
# The function then returns an attribute set.