Bootcamp

What if you could reinstall
your laptop in 30 minutes?

Šta ako bi mogao reinstalirati
laptop za 30 minuta?

NixOS — how I use itNixOS — kako ga ja koristim
Stefan Matić

DisclaimerDisclaimer

Before we startPrije nego počnemo

Built with Claude Code.Napravljeno uz Claude Code.

The problemProblem

Setting up a new machine, usuallyKako obično izgleda postavljanje novog računara

Nothing keeps a record of what you did to the system.Nigdje ne ostaje zapisano šta si uradio sa sistemom.

Names firstPrvo nazivi

Nix ≠ NixOS

Nix

package managerpackage manager

Installs packages into /nix/store, each version side by side. Runs on any Linux distro and on macOS.Instalira pakete u /nix/store, svaku verziju odvojeno. Radi na bilo kojem Linux distrou i na macOS-u.

Nix

languagejezik

The language you write packages and configs in. Same name, which doesn't help.Jezik u kojem se pišu paketi i konfiguracija. Isto ime, što ne pomaže.

NixOS

Linux distroLinux distro

A distro built on Nix. Kernel, services, users and settings are all defined in Nix files.Distro napravljen na Nixu. Kernel, servisi, korisnici i postavke su definisani u Nix fajlovima.

You can use Nix without NixOS. Most of this talk is about NixOS.Nix možeš koristiti i bez NixOS-a. Ovo predavanje je uglavnom o NixOS-u.

What is NixOS?Šta je NixOS?

What NixOS does differentlyŠta NixOS radi drugačije

Usual distroObični distro

$ sudo apt install docker.io
$ sudo systemctl enable --now docker
$ sudo usermod -aG docker me
  • You run commandsPokrećeš komande
  • State lives in the systemStanje je u sistemu

NixOS

virtualisation.docker.enable = true;
users.users.me.extraGroups = [ "docker" ];
  • You edit a file, then rebuildMijenjaš fajl, pa rebuild
  • State lives in the fileStanje je u fajlu
A real examplePravi primjer

My desktop, from the actual fileMoj desktop, iz pravog fajla

# hosts/zvijer/configuration.nix (excerpt)
imports = [
  ./hardware-configuration.nix
  ../_common/client.nix
  ../../system/security/yubikey.nix
];

boot.loader.grub = {
  enable = true;
  efiSupport = true;
  configurationLimit = 20;
  theme = pkgs.sleek-grub-theme.override { withStyle = "dark"; };
};

services.nordvpn.enable = true;

hardware.openrazer = {
  enable = true;
  users = [ userSettings.username ];
};

The full file is about 400 lines. sudo nixos-rebuild switch makes the system match it.Cijeli fajl ima oko 400 linija. sudo nixos-rebuild switch uskladi sistem s njim.

Changing hardwarePromjena hardvera

My GPU history, straight from git logMoja GPU historija, direktno iz git log-a

2026-02-14New 5070 Ti GPU setup
2026-02-15Pin kernel to 6.18 because nvidia sucks ass
2026-04-13Restore 6900XT GPU and remove nvidia bloat
2026-07-20zvijer: migrate GPU to NVIDIA RTX 5070 Ti

Each swap is a commit: drivers, Wayland variables, Ollama ROCm → CUDA. The last one was 3 files, about 40 lines.Svaka zamjena je jedan commit: drajveri, Wayland varijable, Ollama ROCm → CUDA. Zadnja je bila 3 fajla, oko 40 linija.

Every rebuild is also a generation. If one doesn't work, boot the previous one from GRUB, or:Svaki rebuild je i nova generacija. Ako nešto ne radi, butaš prethodnu iz GRUB-a, ili:

$ sudo nixos-rebuild switch --rollback
FlakesFlakeovi

Flakes: "experimental", used by everyoneFlakeovi: „eksperimentalni", a svi ih koriste

  • A flake is a repo with a flake.nix: inputs (nixpkgs, Home Manager, …) and outputs (machines, packages, dev shells)Flake je repo s flake.nix: inputi (nixpkgs, Home Manager, …) i outputi (mašine, paketi, dev shellovi)
  • flake.lock pins every input to an exact commit — mine pins 37flake.lock zaključa svaki input na tačan commit — moj ih ima 37
  • Every machine is an output: nixos-rebuild switch --flake .#ZVIJERSvaka mašina je output: nixos-rebuild switch --flake .#ZVIJER

Officially:Zvanično: still an experimental feature in Nix, since 2021. You turn it on yourself:i dalje eksperimentalna opcija u Nixu, od 2021. Uključuješ je sam:

nix.settings.experimental-features =
  [ "nix-command" "flakes" ];

In practice:U praksi: most configs, tutorials and tools start with flakes. The community treats them as stable.većina configova, tutorijala i alata kreće od flakeova. Zajednica ih smatra stabilnim.

# flake.nix (simplified)
{
  inputs = {
    nixpkgs.url =
      "github:nixos/nixpkgs?ref=nixos-unstable";
    home-manager.url =
      "github:nix-community/home-manager";
    nix-darwin.url =
      "github:nix-darwin/nix-darwin/master";
  };

  outputs = { nixpkgs, ... }: {
    nixosConfigurations.ZVIJER =
      nixpkgs.lib.nixosSystem {
        modules = [
          ./hosts/zvijer/configuration.nix
        ];
      };
  };
}
My setupMoj setup

One repo, every machineJedan repo, sve mašine

gitlab.com/stefan-matic/nixos-config

ZVIJERthe everything machine: work, gaming, 57" ultrawidemašina za sve: posao, gaming, 57" ultrawide
t14 · starlabslaptops, sharing one configlaptopi, dijele jedan config
z420 · dell-micro-3050home serverskućni serveri
macbookwork laptop, via nix-darwinposlovni laptop, preko nix-darwina
fold6Android phone, via nix-on-droidAndroid telefon, preko nix-on-droida
Raspberry Pismagic mirror, arcade, router — some running, some still being tweakedmagic mirror, arcade, router — neki rade, neki se još podešavaju
135.nix files.nix fajlova
~10.7klineslinija
550commits since Oct 2024commitova od oktobra 2024.
For macOS usersZa macOS korisnike

nix-darwin

Same approach on a Mac: system settings, packages and services in Nix files. It manages macOS, it doesn't replace it.Isti pristup na Macu: sistemske postavke, paketi i servisi u Nix fajlovima. Upravlja macOS-om, ne zamjenjuje ga.

  • Same Home Manager config as on Linux: zsh, neovim, tmux, gitIsti Home Manager config kao na Linuxu: zsh, neovim, tmux, git
  • GUI apps come from Homebrew casks, but the list lives in NixGUI aplikacije dolaze iz Homebrew caskova, ali je lista u Nixu
  • Apple still owns the OS, the disk and system updatesApple i dalje drži OS, disk i sistemske update-ove
$ darwin-rebuild switch --flake .#macbook
# hosts/macbook + hosts/_common/darwin.nix (excerpt)
security.pam.services.sudo_local.touchIdAuth = true;

homebrew = {
  enable = true;
  casks = [
    "ghostty"
    "raycast"
    "slack"
    "keepassxc"
  ];
};

home-manager.users.${userSettings.username} = {
  imports = [ ../../home/darwin.nix ];
};
StructureStruktura

Shared config, in layersZajednički config, u slojevima

SystemSistem

hosts/_common/default.nixevery machine: firewall, Docker, base packages, users, secretssvaka mašina: firewall, Docker, osnovni paketi, korisnici, secreti
hosts/_common/client.nixmachines with a screen: desktop, Bluetooth, VMs, login screenmašine s ekranom: desktop, Bluetooth, VM-ovi, login ekran
hosts/zvijer/…this machine only: GPU, GRUB with Windows, Razersamo ova mašina: GPU, GRUB s Windowsom, Razer

User (Home Manager)Korisnik (Home Manager)

home/_core.nixLinux and Mac: git, terminal, tmux, neovim, shell, languagesLinux i Mac: git, terminal, tmux, neovim, shell, jezici
home/_common.nixLinux only: Firefox, KeePassXC, KDE appssamo Linux: Firefox, KeePassXC, KDE aplikacije

Home Manager writes the dotfiles: ~/.config is generated from these files.Home Manager piše dotfileove: ~/.config se generiše iz ovih fajlova.

Change a shared file → every machine that imports it gets the change on its next rebuild.Promijeniš zajednički fajl → svaka mašina koja ga importuje dobije promjenu na sljedećem rebuildu.

Back to the titleNazad na naslov

Setting up a machine with NixOSPostavljanje mašine s NixOS-om

  1. Install NixOS from USB. The installer generates hardware-configuration.nix.Instaliraš NixOS s USB-a. Instaler generiše hardware-configuration.nix.
  2. Clone the repo and copy that file into hosts/<machine>/.Kloniraš repo i kopiraš taj fajl u hosts/<mašina>/.
  3. Rebuild:Rebuild:
$ git clone https://gitlab.com/stefan-matic/nixos-config ~/.dotfiles
$ sudo nixos-rebuild switch --flake ~/.dotfiles#starlabs

Back after the rebuildVrati se nakon rebuilda

Packages, services, shell, editor, desktop, keybindings, fonts.Paketi, servisi, shell, editor, desktop, prečice, fontovi.

Still manualI dalje ručno

Logging into accounts, SSH keys, your files (Syncthing handles those for me).Prijava na naloge, SSH ključevi, tvoji fajlovi (to mi rješava Syncthing).

Servers without a NixOS imageServeri bez NixOS imagea

nixos-infect

3–4 Contabo VPSes running dev and staging for an app I built with friends. Contabo only offered Ubuntu 24.04. nixos-infect turns a running Linux into NixOS, in place:3–4 Contabo VPS-a za dev i staging aplikacije koju sam pravio s prijateljima. Contabo je nudio samo Ubuntu 24.04. nixos-infect pretvori Linux koji već radi u NixOS, na licu mjesta:

  1. installs Nix on the running Ubuntuinstalira Nix na Ubuntu koji radi
  2. generates a NixOS config from the current disk and network setupgeneriše NixOS config iz trenutnog diska i mreže
  3. builds NixOS and installs GRUBbuilda NixOS i instalira GRUB
  4. reboots; the old system is moved to /old-root, SSH keys are keptrestartuje; stari sistem se premjesti u /old-root, SSH ključevi ostaju

From the README: "so named because of the high likelihood of rendering a system inoperable." Fresh servers only.Iz README-a: „so named because of the high likelihood of rendering a system inoperable." Samo na novim serverima.

# fresh Contabo Ubuntu 24.04 VPS, root, SSH keynovi Contabo Ubuntu 24.04 VPS, root, SSH ključ

# Contabo names it vmi123456.contaboserver.net;Contabo ga nazove vmi123456.contaboserver.net;
# NixOS doesn't accept dots in the hostnameNixOS ne prihvata tačke u hostnameu
$ hostname myserver

$ curl https://raw.githubusercontent.com/elitak/\
    nixos-infect/master/nixos-infect \
    | NIX_CHANNEL=nixos-25.11 bash -x

# …a few minutes and a reboot later…par minuta i jedan reboot kasnije
$ ssh root@myserver
[root@myserver:~]# nixos-version
25.11 …
The goodDobro

What else I use every dayŠta još koristim svaki dan

Dev environmentsDev okruženja

Each project directory gets its own toolsSvaki folder projekta ima svoje alate

One approach for every language, instead of nvm, pyenv, rbenv, tfenv, sdkman…Jedan pristup za sve jezike, umjesto nvm, pyenv, rbenv, tfenv, sdkman…

$ cd ~/Workspace/project-a
$ devbox init
$ devbox add terraform@1.5.7 nodejs@20 awscli2
$ devbox generate direnv && direnv allow

$ terraform version        # Terraform v1.5.7
$ cd ../project-b
$ terraform version        # Terraform v1.4.0
$ cd ~ && terraform version
zsh: command not found: terraform
  • devbox.json lists the tools and versions; it goes into the project's gitdevbox.json sadrži alate i verzije; ide u git projekta
  • direnv loads the environment when you cd in and unloads it when you leavedirenv učita okruženje kad uđeš u folder, a ukloni ga kad izađeš
  • Teammates get the same versions — they don't need NixOS, devbox installs Nix for themKolege dobiju iste verzije — ne treba im NixOS, devbox im sam instalira Nix
  • Nothing installed globallyNišta se ne instalira globalno
  • Same idea without devbox: a flake devShell + use flake in .envrcIsta ideja bez devboxa: flake devShell + use flake u .envrc
The badLoše

The Nix languageNix jezik

# hosts/zvijer/configuration.nix (top)
{ config, pkgs, lib, inputs, ... }:

let
  env = import ./env.nix { inherit pkgs; };
  inherit (env) systemSettings userSettings;
in
{
  home-manager.users.${userSettings.username} = {
    imports = [ ../../home/stefanmatic.nix ];
  };
}

The whole file is a function. inherit copies names into scope. let … in defines local values.Cijeli fajl je funkcija. inherit kopira imena u scope. let … in definiše lokalne vrijednosti.

A common mistakeČesta greška

[ pkgs.callPackage ./foo { } ]

This is a list of three items: a function, a path and an empty set. List items and function arguments are both separated by spaces.Ovo je lista od tri stavke: funkcija, putanja i prazan set. I stavke liste i argumenti funkcije se odvajaju razmakom.

[ (pkgs.callPackage ./foo { }) ]

It's a functional language. Most people learn it by reading other people's configs.To je funkcionalni jezik. Većina ga nauči čitajući tuđe configove.

The badLoše

Error messagesPoruke o greškama

A 6-line module that imports a file only when an option is on:Modul od 6 linija koji importuje fajl samo kad je opcija uključena:

{ config, lib, ... }: {
  options.services.myapp.enable =
    lib.mkEnableOption "myapp";
  imports = lib.optional
    config.services.myapp.enable ./extra.nix;
}

The trace points into nixpkgs internals, not your file. Newer Nix versions add hints like the one on the right; many errors still don't have one.Trace pokazuje na interni kod nixpkgs-a, ne na tvoj fajl. Novije verzije Nixa dodaju savjete kao ovaj desno; mnoge greške ih i dalje nemaju.

error:
  … while evaluating the attribute
    'config.services.myapp.enable'
    at /nix/store/r5l6…-source/lib/modules.nix:404:9
  … while calling the 'seq' builtin
    at /nix/store/r5l6…-source/lib/modules.nix:404:18
  … while evaluating the module argument
    `config' in ":anon-1":
  … if you get an infinite recursion here, you
    probably reference `config` in `imports`.

  (stack trace truncated; use '--show-trace')

  error: infinite recursion encountered
The learning curveKrivulja učenja

Happiness over timeSreća kroz vrijeme

time → vrijeme → happiness sreća Ubuntu NixOS "why is there no /bin/bash" „zašto nema /bin/bash" "what is a derivation" „šta je derivation" first painless reinstall prvi bezbolni reinstall enlightenment ✨ prosvjetljenje ✨
The badLoše

The learning curveKrivulja učenja

What was hardŠta je bilo teško

  • Docs are spread across the manual, the wiki, the forum and the source codeDokumentacija je razbacana po manualu, wikiju, forumu i izvornom kodu
  • Old and new ways to do the same thing (channels vs flakes), so guides contradict each otherStari i novi načini za istu stvar (channels vs flakes), pa se vodiči međusobno kontradiktuju
  • Flakes are what everyone uses, and they're still marked experimentalFlakeove svi koriste, a i dalje su označeni kao eksperimentalni
  • Downloaded binaries often don't run: no /bin/bash, no libraries in /usr/libSkinuti binarni fajlovi često ne rade: nema /bin/bash, nema biblioteka u /usr/lib

What helpedŠta je pomoglo

  • search.nixos.org for packages and every NixOS optionsearch.nixos.org za pakete i sve NixOS opcije
  • Reading other people's configs on GitHubČitanje tuđih configova na GitHubu
  • programs.nix-ld.enable = true; for most downloaded binariesprograms.nix-ld.enable = true; za većinu skinutih binarnih fajlova
  • Starting small: one machine, one file, flakes laterPočetak s malim: jedna mašina, jedan fajl, flakeovi kasnije
Should you try it?Da li da probate?

Who it's forZa koga je

Worth trying if youVrijedi probati ako

  • manage more than one machineimate više od jedne mašine
  • want your setup in git and reproducibleželite setup u gitu i ponovljiv
  • don't mind reading docs and source codene smeta vam čitanje dokumentacije i koda

Maybe not right now if youMožda ne sada ako

  • need a machine that just works this weekvam treba mašina koja jednostavno radi ove sedmice
  • depend on software that ships only as a binary installerzavisite od softvera koji dolazi samo kao binarni instaler
  • don't want to learn another languagene želite učiti još jedan jezik

It's not a fix for everything. A lot of things don't work out of the box — they take tweaking and trial and error.Nije rješenje za sve. Dosta stvari ne radi odmah — treba ih podešavati i isprobavati.

How to startKako početi

ThanksHvala

Questions?Pitanja?

If you didn't like this talk:Ako vam se predavanje nije svidjelo:

$ sudo nixos-rebuild switch --rollback
My config:Moj config: gitlab.com/stefan-matic/nixos-config · github.com/stefan-matic/nixos-config
Start here:Početak: nixos.org/learn · nix.dev · search.nixos.org
Slides:Slajdovi: nixos-presentation.stefan.matic.ba
← → / space / swipe · L = language← → / space / swipe · L = jezik