Image stolen from:
bmcgee.ie
NixOS is Easy When...
Painless Software
Source: Nix: A Safe and Policy-Free System for Software Deployment (p. 81)
6 lambdas that form a snowflake
Source: NixOS Logo and Branding Update (2025)
A Linux distribution based on the Nix package manager
Install Nix on any Linux distro (or macOS)
NixOS Calamares installer
👆
# Edit this configuration file to define what should be installed
{ config, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
];
boot.loader.systemd-boot.enable = true;
networking.hostName = "nixos";
networking.networkmanager.enable = true;
time.timeZone = "Europe/Rome";
i18n.defaultLocale = "en_US.UTF-8";
programs.firefox.enable = true;
environment.systemPackages = with pkgs; [
vim
];
}# Do not modify this file! It was generated by ‘nixos-generate-config’
{ config, lib, pkgs, modulesPath, ... }:
{
boot.kernelModules = [ "kvm-intel" ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/5a42e090-37b7";
fsType = "ext4";
};
networking.useDHCP = lib.mkDefault true;
}NixOS Configuration
$ tree /etc/nixos
/etc/nixos
|-- configuration.nix
`-- hardware-configuration.nixHow Make NixOS Your Home
$ nixos-rebuild --sudo {switch|boot|test} [--file ...|--flake ...]
...
Done. The new configuration is /nix/store/j6a42...systemPackages
programs (system-wide)/etc/nixos/ (e.g. your home directory)Nix Store (read-only)
$ ls -lAF --color /nix/store
total 7355364
dr-xr-xr-x 1 root root 6 1 Jan 1970 000bmy9k246z4bdywjkdi6hbybah9z0j-user/
dr-xr-xr-x 1 root root 0 1 Jan 1970 000fdw5ijqc8nhgnnawfy11vxf1hi8rw-system-generators/
-r--r--r-- 2 root root 3599 1 Jan 1970 000hxqh70ck7sjhdvcn66zdsyp2yprpb-source.drv
-r--r--r-- 2 root root 2221 1 Jan 1970 000hy9cqahi1rwy2mx3yak8dq02pa5ih-gst-devtools-1.26.5-vendor.drv
-r--r--r-- 2 root root 3146 1 Jan 1970 000nzak0hrcsl7d2kf3l6d544p7kyrf2-crate-num_threads-0.1.7.tar.gz.drv
-r--r--r-- 2 root root 2223 1 Jan 1970 000skmmf43a4vnxnc775s78psjr96d54-001-fix-rpath.patch.drv
-r--r--r-- 2 root root 4493 1 Jan 1970 000x39hj0g61mkp15xgkz27j7fkzkh8h-CVE-2022-48174.patch.drv
-r--r--r-- 2 root root 3615 1 Jan 1970 0010xx2b5k2kxq0c5b3pwq3bby9rdk94-source.drv
-r--r--r-- 2 root root 3314 1 Jan 1970 0015pxy9jsaxgq729bfc5ikn9n1wssbi-gnome-font-viewer-48.0.drv
...FHS ?!?
All packages are uniquely kept in the Nix Store.
There are no
/bin,/sbin,/usr, etc. directories!
.drv = Derivation
Derive(
[
("debug", "/nix/store/hichgz37f1327dfi0saqll0lpbimvfw0-coreutils-9.7-debug", "", ""),
("info", "/nix/store/x13fzfc1lv1nbmszq25kajhsxzwzyd6r-coreutils-9.7-info", "", ""),
("out", "/nix/store/psy9v2asypgl9ylg8cnzkixc7fv0snj0-coreutils-9.7", "", ""),
],
[
("/nix/store/05q48dcd4lgk4vh7wyk330gr2fr082i2-bootstrap-tools.drv", ["out"]),
("/nix/store/6wnz883nsn7hz72yqbjzj0pg4zlqcfza-xz-5.8.1.drv", ["bin"]),
("/nix/store/ayrji7q39s4z0vnx6ksryxp2jvl5dl20-attr-2.5.2.drv", ["dev"]),
...
],
...
[
...
("strictDeps", ""),
("system", "x86_64-linux"),
("version", "9.7"),
],
)Package build action (generated)
Nix expression ➜ Derivation
technical recipe (build activities)
Nix Package
$ tree /nix/store/00zrahbb32nzawrmv9sjxn36h7qk9vrs-bash-5.2p37/
/nix/store/00zrahbb32nzawrmv9sjxn36h7qk9vrs-bash-5.2p37
├── bin
│ ├── bash
│ └── sh -> bash
└── lib
└── bash
├── accept
├── basename
├── csv
├── cut
├── dirname
...
├── sync
├── tee
├── truefalse
├── tty
├── uname
├── unlink
└── whoamiPackage (read-only file tree)
Symbolic Links All Over The Place
$ ls -l /run/current-system/sw
lrwxrwxrwx 2 root root 55 1 Jan 1970 /run/current-system/sw -> /nix/store/1n2l2vz3sdzgxjilhqzndff9lmyvdg8c-system-path
$ ls -l /run/current-system/sw/
bin/ etc/ lib/ sbin/ share/
$ ls -l /run/current-system/sw/bin
...
lrwxrwxrwx 8 root root 70 1. Jan 1970 zstdcat -> /nix/store/n0crcrrdb0jlfmwidnv150vm33611xs3-zstd-1.5.7-bin/bin/zstdcat
lrwxrwxrwx 8 root root 71 1. Jan 1970 zstdgrep -> /nix/store/n0crcrrdb0jlfmwidnv150vm33611xs3-zstd-1.5.7-bin/bin/zstdgrep
lrwxrwxrwx 8 root root 71 1. Jan 1970 zstdless -> /nix/store/n0crcrrdb0jlfmwidnv150vm33611xs3-zstd-1.5.7-bin/bin/zstdless
lrwxrwxrwx 8 root root 69 1. Jan 1970 zstdmt -> /nix/store/n0crcrrdb0jlfmwidnv150vm33611xs3-zstd-1.5.7-bin/bin/zstdmtSystem Packages
$ ls -l /etc/static/profiles/per-user/
lrwxrwxrwx 2 root root 60 1 Jan 1970 peter -> /nix/store/z8h5rbv29p6a7rxdjr31b3z6pwp0giv7-user-environment
$ tree /etc/static/profiles/per-user/peter/bin
/etc/static/profiles/per-user/peter/bin
├── accessdb -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/accessdb
├── apropos -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/apropos
├── catman -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/catman
├── codium -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/codium
├── ghostty -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/ghostty
├── git -> /nix/store/qyiyrbrh1wnpa43w4vqn9r89w4q8b4bb-home-manager-path/bin/git
...User Packages
$ nix store gc -v
finding garbage collector roots...
deleting garbage...
deleting '/nix/store/v5cqs341xjgpqsx5c8pc2h8hz9mvwv3b-source'
deleting '/nix/store/zmnlm7rglapnp1mm8mz25jlbjgsckniy-nixos-system-bittner-25.11.20250719.c87b95e.drv'
deleting '/nix/store/x302k0rbgrry7vm94mkzvz1pzj9b2ng2-etc-nix-registry.json'
deleting '/nix/store/i1l6nqcknyzgpg77h3ysf7axspzhqzsl-nixos-system-bittner-25.11.20250719.c87b95e.drv'
deleting '/nix/store/al0hg0j2x9ikrniqxfmrfc0vjg3zxda5-etc.drv'
deleting '/nix/store/2q5svikixkmx7mfh3l1xrx4bm99a8ksr-etc.drv'
deleting '/nix/store/9ndq6wxadmlp85y22pcw92bbmdkf594y-etc-nix-registry.json.drv'
deleting '/nix/store/182c2qj20sfspzxcshd6p051qdx3mx5j-source'
...
deleting '/nix/store/cdirisw2j68pmr3anwvzy43q3zyfks44-plymouth-initrd-themes'
deleting '/nix/store/i69ii1nris373wqnzbl7hgmbns9f3f55-x86_64-unknown-linux-musl-gcc-14.3.0-lib'
deleting '/nix/store/i8zh98ynh9bn3gz244pispp81dzmw8n6-activation-script'
deleting '/nix/store/8q3vz0vk5bllxrqa0v5s1fxp9a0jmdpy-boot.json'
deleting unused links...
note: currently hard linking saves 99903.89 MiB
73449 store paths deleted, 34381.53 MiB freedGarbage Collection
Cleanup of unreferenced packages (and derivations)
42number.nix
"foo"string.nix
trueboolean.nix
~/.local/binpath.nix
[ "foo" "bar" 42 false ]list.nix
{ target = /etc/nixos; foo = 42; }attribute-set.nix
x: x + 1function.nix
{
x = import ./number.nix;
}
imported-value.nix
Data Types, Operators, Built-ins
{
# negation
truth = !false;
# string and paths
a_str = "Dirs" + ./.config/user;
a_path = /etc + "/nixos";
# list concatenation
l = [ 1 2 3 ] ++ [ "four" "five"];
# logical and, logical or
m = true && false; n = true || false;
# update attribute set
s = { x = 1; } // { y = 1; x = 2; };
# has attribute (attribute set member)
h = { x = 42; } ? x;
}operators
{
# available = builtins;
yes = true; no = false; nothing = null;
system = builtins.currentSystem;
epoch = builtins.currentTime;
packages = builtins.storeDir;
paths = builtins.nixPath;
language = builtins.langVersion;
version = builtins.nixVersion;
}built-in constants
{
d = builtins.readDir;
f = builtins.readFile;
e = builtins.getEnv "HOME";
s = builtins.substring 0 3 "nixos";
...
}built-in functions
{
# numbers
a = 1; f = 3.14;
# strings
text = "Hello Pi! ${f}";
multi_line = ''
Very long text ... with ${a}
'';
# paths
x = /absolute/path/to
y = ./relative/path
z = ~/.config # path in home dir
}datatype features
{ lib, ... }:
{
imports = [
./file.nix
];
options = {
magicNumber = lib.mkOption {
type = lib.types.number;
default = 3.14;
};
};
config = {
boot.loader.grub.enable = true;
};
}module.nix
{ lib, ... }:
let
preference = "GNOME";
in
{
imports = [
./file.nix
];
options = {
desktop.style = lib.mkOption {
type = lib.types.enum [ "Windows" "macOS" "GNOME" ];
default = preference;
};
};
}variables.nix
Nix Module
{
console.keyMap = "it";
services.xserver.xkb = {
layout = "it";
variant = "";
};
}
implicit-config.nix
config
✨
Nix Flake
$ nix flake show templates
$ nix flake init -t 'templates#utils-generic'
$ nix flake update
$ nix flake check
$ nix flake showRecommendation
{
description = "Linux Day flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
nixos-hardware.url = "github:NixOS/nixos-hardware?ref=master";
};
outputs = { self, nixpkgs }: {
nixosConfigurations = nixpkgs.lib.mapAttrs mkSystem systems;
};
}flake.nix
I want a computer that works
$ nixos-rebuild --sudo --file ~/.config/nixos/default.nix
...
Done. The new configuration is /nix/store/j6a42...systemPackages, programs)I want to run any software, instantly
$ nix run nixpkgs#neofetch
...
$ nix shell nixpkgs#cowsay nixpkgs#lolcat
$ cowsay 'Hello Linuxday!' | lolcat
_________________
< Hello Linuxday! >
-----------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||I manage a fleet of computers
$ nixos-rebuild switch --sudo --flake gitlab:painless-software/nixos-config
...
Done. The new configuration is /nix/store/j6a42...
$ nixos-rebuild test --sudo --flake .
building the system configuration...
Place your finger on the fingerprint reader
activating the configuration...
$ nix flake check
$ nix flake show
$ nix flake updatesystem.autoUpgrade)I write software (incl. websites)
$ nix flake show templates
...
$ nix flake init -t templates#...
...
$ nix develop
...
$ go version
go version go1.25.1 linux/amd64nix develop to start a development shell{
description = "A simple Go package";
inputs.nixpkgs.url = "nixpkgs/nixos-25.11";
outputs = { self, nixpkgs }:
{
packages = forAllSystems (system:
let
pkgs = nixpkgsFor.${system};
in
...
);
};
devShells = forAllSystems (system:
default = pkgs.mkShell {
buildInputs = with pkgs; [ go gopls gotools go-tools ];
};
);
}flake.nix
I want a pythonic work environment
$ uv python install 3.14 --preview --default
$ which python
/home/peter/.local/bin/python
$ uv python upgrade --preview-features python-upgrade
All versions already on latest supported patch release
$ uv tool install pre-commit
$ uv tool install httpie
$ ls ~/.local/bin
http httpie https pre-commit python
$ uv tool list
...
$ uv tool upgrade --all
$ uvx pyclean
$ uvx copier updateuv (but not any Python!) via systemPackages
uv
nix-ld to fix the dynamic linking problem{
environment = {
localBinInPath = true; # ~/.local/bin in PATH
systemPackages = with pkgs; [
uv # install Pythons in user-space using uv
];
};
programs.nix-ld = {
enable = true;
libraries = with pkgs; [
libelf
libjpg
libpng
];
};
}configuration.nix
I create software for NixOS users
$ nix shell nixpkgs#nix-init
$ nix-init --url https://github.com/bittner/pyclean
...
$ nix build --file .
$ nix build nixpkgs#hello
$ ls -l
lrwxrwxrwx 1 peter users 56 25 Ott 15:42 result -> /nix/store/9mj...as2fis-hello-2.12.2
$ ./result/bin/hello
Hello, world!nix-init for "nixification"nix build to build and locally verify a package setuphosts/ – managed machinesroles/ – classes of machines (abstraction layer)system/ – system-global configurationHost-based Configuration
Manage user-specific software and settings ("dotfiles")
Home Manager
Declarative disk partitioning
disko-config.nix
$ sudo nix run github:nix-community/disko/latest -- \
--mode destroy,format,mount \
--flake gitlab:painless-software/nixos-config#example
Disko
Flake
Establish execution entrypoint (e.g. integrate hosts/).
Consider integrating QA tools, pre-commit and CI/CD early.
Disko
Integrate Disko configuration. Verify installation process end-to-end.
Consider setting up functional tests with VMs.
Refine
Delegate host setup to system/ configuration. Consider introducing roles/.
Consolitate home/ features in common user/ modules.
Home Manager
Allow configuring settings and installing software for individual users.
Consider using LDAP for a flexible, host-independent setup.
Iterative Development
Use Technology's QA Tools
Copier template
$ copier copy gl:painless-software/cicd/config/nixos nixos-config
...
$ pre-commit install
...
$ git init
$ git add -v .
...
$ pre-commit
$ nix flake check
....gitlab-ci.yml
---
deadnix:
extends: .nix
script: nix run nixpkgs#deadnix -- --fail
statix:
extends: .nix
script: nix run nixpkgs#statix -- check
flake:
extends: .nix
script:
- nix flake check
- nix flake show
disko:
extends: .nix
script: nix run github:nix-community/disko/latest --
--mode destroy,format,mount --dry-run --flake .#generic
tooling:
extends: .megalinter
variables:
FLAVOR: documentationLess pain, more fun.
Most static backgrounds from Unsplash (CC BY-SA)
Animated backgrounds from GIPHY (CC BY-SA)
Decorative icons are unicode (CC-0)
Wow!
This presentation
was made entirely
without any AI!