Day 5 — NixOS Install, Flake Host & Generations
Day 5 — NixOS Install, Flake Host & Generations
Day 11 — Install NixOS lab
Stage II · ~4h (hands-on heavy)
Goal: Install a disposable NixOS 26.05 “Yarara” lab system (VM strongly preferred), boot it, capture hardware config notes, and prepare for flake-based management tomorrow.
Do not experiment on your only daily-driver disk. Use a VM, spare machine, or spare disk. Reinstall without drama is a feature.
Why this day exists
Generations, rollback, modules, and firewall only become real on a host. Stage I was the package manager + language. Stage II is owning a machine as code—starting with a clean install.
Theory 1 — What you are installing
| Piece | Role |
|---|---|
| NixOS | Linux distribution built from Nix modules |
| nixpkgs 26.05 “Yarara” | Package + module set baseline for this volume |
| Installer image | Live environment to partition + install |
| First generation | Initial system closure after install |
After install you have a bootable system. Flake ownership is Day 12—today can use installer defaults, but mentally prepare to migrate immediately.
Theory 2 — Disposable VM emphasis
| Host choice | Recommendation |
|---|---|
| VM (QEMU/KVM, UTM, VirtualBox, VMware, …) | Preferred |
| Spare laptop/NUC | Good |
| Dual-boot daily driver | Risky for learning |
| Cloud VM | Fine if you accept networking/SSH from the start |
VM resources (practical)
| Resource | Starting point |
|---|---|
| RAM | 4–8 GB |
| Disk | 40–64 GB+ |
| CPU | 2+ cores |
| Firmware | UEFI if available |
| Snapshot | Take one after first successful boot |
Snapshots turn “I broke GRUB” into “restore 30 seconds.”
Theory 3 — UEFI vs legacy BIOS
| Mode | Common loader on NixOS |
|---|---|
| UEFI | systemd-boot (often default) or GRUB EFI |
| BIOS/CSM | GRUB |
Prefer UEFI for new labs. Note which you used—Day 20 returns to boot loaders.
Theory 4 — Disk layout options
Manual (classic installer path)
- Partition (ESP + root, optional swap)
- Format
- Mount at
/mnt, ESP at/mnt/boot
nixos-generate-config --root /mnt
- Edit
configuration.nix
nixos-install
Disko (intro only)
Disko declares disks as code. Full Disko redesign is Stage VI (Day 57). Today:
- Know Disko exists
- Optionally follow a simple Disko example if you already know it
- Otherwise manual once is fine and educational
Do not block install on learning Disko deeply today.
Suggested simple layout (UEFI)
ESP (vfat) 512MB–1GB → /boot
root (ext4 or btrfs) → /
swap (optional)
Theory 5 — nixos-generate-config outputs
| File | Role |
|---|---|
hardware-configuration.nix |
Detected devices, fileSystems, initrd modules |
configuration.nix |
High-level system config (you own this) |
Rules of thumb
- Treat
hardware-configuration.nixas machine-specific; rarely hand-merge casually
- Put policy (users, services, packages) in
configuration.nix/ modules
- Tomorrow both get imported from a flake
Theory 6 — Installer networking and time
Install needs:
- Network for substitutes (unless offline media strategy)
- Correct clock helps TLS
# live environment examples (names vary)
ip a
ping -c 2 1.1.1.1Theory 7 — First boot mental model
After nixos-install and reboot:
bootloader → kernel+initrd → stage-1/2 → systemd → multi-user
system profile: /nix/var/nix/profiles/system
generation N is current
You can log in as root or the user you created. Prefer SSH key setup ASAP (Day 14 formalizes); avoid building habits around password-only remote root.
Theory 8 — Credentials hygiene on a lab
| Practice | Why |
|---|---|
| Unique lab password | Compromise isolation |
| Note in password manager | Labs still get attacked if exposed |
| Prefer host-only networking until ready | Reduce drive-by SSH noise |
| Snapshot before experiments | Fast recovery |
Worked examples bank
Example A — Download image
Use official docs for current ISO:
Pick 26.05 graphical or minimal ISO matching your arch (x86_64, aarch64).
Verify checksums when provided.
Example B — Partition sketch (sfdisk/parted-level ideas)
/dev/vda1 EFI System FAT32 /boot
/dev/vda2 Linux root ext4 /
Device names: vda (virtio), sda, nvme0n1—use what the live system sees (lsblk).
Example C — Generate + install skeleton
# after mounts ready
sudo nixos-generate-config --root /mnt
sudo nano /mnt/etc/nixos/configuration.nix
sudo nixos-install
# set root password when promptedExample D — Minimal configuration knobs to set during install
{
imports = [ ./hardware-configuration.nix ];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "lab";
time.timeZone = "UTC"; # set yours
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" ];
# initialPassword only for offline lab convenience — rotate later
initialPassword = "changeme";
};
services.openssh.enable = true; # if you want SSH into VM
system.stateVersion = "26.05"; # match release; do not casually change later
}Exact options must match your installer generation—adapt from generated file rather than blind paste.
Lab 1 — Plan and snapshot strategy
Write in ~/lab/90daysofx/02-nixos/day11/NOTES.md (on the host machine):
- Hypervisor / hardware choice
- UEFI or BIOS
- Disk size
- Hostname
- Username
- Networking mode (NAT/bridge)
- Snapshot plan
Lab 2 — Install NixOS 26.05
- Boot installer ISO
- Partition + format + mount
nixos-generate-config --root /mnt
- Edit config: hostname, user, boot loader, optional SSH
nixos-install
- Reboot into installed system
- Take a VM snapshot labeled
day11-fresh-install
If install fails, journal the error class (bootloader, mount, network, disk).
Lab 3 — Post-install inventory
On the new system:
nixos-version
uname -a
lsblk -f
cat /etc/os-release
ls /etc/nixos/Copy off-box (screenshot/scp) or retype into host NOTES.md:
nixos-versionoutput
- Disk layout
- Boot loader in use
- IP address if any
Lab 4 — Preserve hardware config
# on NixOS lab
sudo cp -a /etc/nixos /root/nixos-etc-backup-$(date +%F)On host lab notes, record that Day 12 will create a flake and import hardware-configuration.nix.
Optional: already enable flakes in system config for smoother Day 12:
nix.settings.experimental-features = [ "nix-command" "flakes" ];Then sudo nixos-rebuild switch once if you edit on the running system.
Lab 5 — Break-glass familiarity
Without destroying the snapshot casually:
- Locate boot menu / how to pick previous generation (may only have gen 1)
- Confirm you can log in on console
- If SSH enabled, log in from host once
Common gotchas
| Symptom | Theory |
|---|---|
| No bootable device | ESP not mounted at /mnt/boot or wrong firmware mode |
| Install can’t reach cache | Network/DNS in live env |
| Forgot root password | Live ISO recovery / reinstall—why snapshots matter |
hardware-configuration wrong root UUID |
Generated before final mounts |
| Using unstable ISO by mistake | Check 26.05 label |
| Daily driver install “for fun” | Restore from backup; prefer VM next time |
Checkpoint
- Disposable NixOS 26.05 lab boots
- User + privilege path known (root/wheel)
- Disk + boot mode documented
/etc/nixosinspected
- VM snapshot taken (if VM)
- Notes stored on host machine
Commit
# on host workstation notes repo
cd ~/lab/90daysofx/02-nixos/day11
git init 2>/dev/null || true
git add .
git commit -m "day11: nixos 26.05 lab install notes"Tomorrow
Day 12 — Flake host skeleton. Put the machine under nixosConfigurations and rebuild with nixos-rebuild switch --flake .#host—flakes from the first serious ownership moment.
Day 12 — Flake host skeleton
Stage II · ~4h
Goal: Manage the lab host with a flake (nixosConfigurations) from day one of ownership—nixos-rebuild switch --flake .#<host> is the source of truth.
Syllabus expert default: do not invest weeks in non-flake /etc/nixos only to rewrite. Migrate now while the config is small.
Why this day exists
A NixOS system without a flake pin drifts like a channel laptop. Flakes give you:
- Locked nixpkgs (and later inputs)
- Named hosts in one repo
- A rebuild command you can document and automate
Theory 1 — nixosConfigurations output
outputs = { self, nixpkgs, ... }: {
nixosConfigurations.lab = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./hosts/lab/configuration.nix
./hosts/lab/hardware-configuration.nix
];
};
};| Piece | Role |
|---|---|
nixosSystem |
Module system entry for a host |
system |
CPU-OS triple for packages |
modules |
List of NixOS modules (files/functions) |
attr name lab |
Host id in .#lab |
Rebuild:
sudo nixos-rebuild switch --flake /path/to/repo#lab
# or from repo:
sudo nixos-rebuild switch --flake .#labTheory 2 — Where the flake lives
| Location | Pros | Cons |
|---|---|---|
/etc/nixos/flake.nix |
Simple path | Mixed with generated hardware; git harder |
/home/you/nixos-config git repo |
Proper history | Need path on rebuild |
| Separate git + copy | Flexible | Sync discipline |
Recommended for this volume: git repo in your home (or shared path), rebuild with explicit flake path. Optionally symlink strategy later.
Theory 3 — Module list composition
modules = [
./hardware-configuration.nix
./configuration.nix
./modules/ssh.nix # later
];Each module is typically:
{ config, lib, pkgs, ... }: {
# options / config
}Or a plain attrset in simple files. Day 18–19 deepen modules; today: imports work.
Theory 4 — Minimal flake host contents
flake.nix
{
description = "lab flake host";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
outputs = { self, nixpkgs }: {
nixosConfigurations.lab = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; # match your VM
modules = [
./hosts/lab/configuration.nix
./hosts/lab/hardware-configuration.nix
];
};
};
}hosts/lab/configuration.nix
{ config, lib, pkgs, ... }: {
networking.hostName = "lab";
nix.settings.experimental-features = [ "nix-command" "flakes" ];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
time.timeZone = "UTC";
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" ];
};
environment.systemPackages = with pkgs; [ vim git ];
services.openssh.enable = true;
system.stateVersion = "26.05";
}hardware-configuration.nix
Copy from the machine’s generated file—do not invent UUIDs.
Theory 5 — First rebuild flow
edit flake modules
↓
nixos-rebuild switch --flake .#lab
↓
eval modules → system derivation → realize → activate
↓
new system generation
| Outcome | Meaning |
|---|---|
| Success | New generation is current |
| Eval error | Fix Nix before build |
| Build error | Package/build failure |
| Activation error | System may be partially switched—read output carefully |
Theory 6 — specialArgs / extraSpecialArgs (preview)
nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; };
modules = [ … ];
}Used later to pass flake inputs into modules. Not required for the minimal host.
Theory 7 — Hostname vs flake attribute
| Name | Where |
|---|---|
networking.hostName |
OS hostname |
nixosConfigurations.lab |
Flake attribute |
They may match but are not required to. Document both in README.
Worked examples bank
Example A — Build without switching (safe)
nixos-rebuild build --flake .#lab
# or
nix build .#nixosConfigurations.lab.config.system.build.toplevelExample B — Switch
sudo nixos-rebuild switch --flake .#labExample C — Show config value
nix eval .#nixosConfigurations.lab.config.networking.hostNameLab 1 — Repository layout on the lab host
mkdir -p ~/nixos-config/hosts/lab
cd ~/nixos-config
git initsudo cp /etc/nixos/hardware-configuration.nix ~/nixos-config/hosts/lab/
sudo chown "$USER":"$USER" ~/nixos-config/hosts/lab/hardware-configuration.nixWrite flake.nix + hosts/lab/configuration.nix (adapt user, system, boot).
git add .
nix flake lock
git add flake.lock
git commit -m "day12: initial nixosConfigurations.lab"Lab 2 — First flake rebuild
cd ~/nixos-config
sudo nixos-rebuild switch --flake .#labVerify:
nixos-version
hostname
nix eval .#nixosConfigurations.lab.config.networking.hostNameLab 4 — README for future you
README.md:
# nixos-config
## Rebuild
sudo nixos-rebuild switch --flake .#lab
## Host
- flake attr: lab
- hostname: lab
- nixpkgs: 26.05 (see flake.lock)Lab 5 — Optional: match /etc/nixos to flake
Some prefer:
# conceptual — only if you understand implications
# keep hardware in repo; rebuild always with --flakeDo not create circular confusion. Simplest: always cd ~/nixos-config && sudo nixos-rebuild switch --flake .#lab.
Common gotchas
| Symptom | Theory |
|---|---|
| Absolute import paths broken | Prefer relative module paths in repo |
Wrong system |
Eval/build target mismatch |
| hardware config not updated after disk change | Regenerate carefully |
experimental-features missing |
Rebuild cannot use flakes cleanly |
| Dirty git / missing files | Flake file inclusion |
stateVersion folklore |
Set to install release; read release notes before changing |
Checkpoint
nixosConfigurations.<host>exists
hardware-configuration.niximported from flake repo
switch --flake .#hostsucceeded
- Lock committed
- README documents rebuild
- Small declarative change proven
Commit
cd ~/nixos-config
git add .
git commit -m "day12: flake host skeleton rebuild switch"Tomorrow
Day 13 — Rebuild modes & generations. switch / test / boot / build, generation listing, and a deliberate break → rollback.
Day 13 — Rebuild modes & generations
Stage II · ~4h
Goal: Master nixos-rebuild modes, system generations, and rollback—including a deliberate break and recovery on the disposable lab.
Why this day exists
NixOS’s famous safety is not automatic—it is generational profiles + boot entries + knowing which rebuild verb you used. Without that theory, “rollback” is folklore.
Theory 1 — System profile and generations
NixOS points the running system at a profile:
/nix/var/nix/profiles/system → system-N-link → store path
Each successful deployment can add a generation N. Boot loaders list generations so you can boot an older one when the new world is wrong.
| Concept | Meaning |
|---|---|
| Generation | A complete system closure you can activate/boot |
| Current | What the profile points at now |
| Rollback | Point profile (and/or boot default) at older generation |
Theory 2 — Rebuild modes
sudo nixos-rebuild <mode> --flake .#lab| Mode | Effect |
|---|---|
switch |
Build + activate now + set boot default to new gen |
boot |
Build + set boot default; do not activate until reboot |
test |
Build + activate now; do not change boot default |
build |
Build only; no activation |
dry-activate |
Show activation plan (when supported/useful) |
build-vm |
Build a VM of the config (extra workflows) |
Choosing modes
| Situation | Prefer |
|---|---|
| Normal daily change | switch |
| Risky change; want easy reboot escape | test first or boot |
| Remote SSH-only host | Extra care—bad switch can lock you out |
| CI / check | build |
Remote note
If you break networking/SSH on a remote machine with switch, you may need console. Lab VMs have consoles—use them.
Theory 3 — Activation vs boot default
test: running config changes; reboot may return to previous default
switch: running + default updated
boot: default updated; running stays old until reboot
Worked story
testa firewall change → SSH still works →
switchto commit it to boot default
Or:
boota kernel change → reboot when ready →
- If unbootable, select previous generation in boot menu
Theory 4 — Listing and switching generations
sudo nix-env -p /nix/var/nix/profiles/system --list-generations
# or
ls -l /nix/var/nix/profiles/system*
sudo nixos-rebuild list-generations # when available in your versionRollback helpers:
sudo nixos-rebuild switch --rollback
# or boot older generation from boot menuAlso:
sudo /nix/var/nix/profiles/system-<N>-link/bin/switch-to-configuration switchPrefer documented nixos-rebuild interfaces when possible.
Theory 5 — What is not rolled back
| Rolled back with system gen | Not rolled back |
|---|---|
| Packages, units, users declared in config | Your data in /home, /var |
Most /etc files managed by NixOS |
Databases, untracked files |
| Kernel (for that gen) | Out-of-band disk edits |
Punchline: rollback restores system closure, not time-travel for application data.
Theory 6 — GC and generations
Old generations root large closures. Cleaning:
sudo nix-collect-garbage -d # deletes old profile gens — careful
sudo nixos-rebuild … # after understandingDo not delete generations until you have a known good current system and understand you are dropping rollback points.
Theory 7 — Flake + rebuild flags worth knowing
sudo nixos-rebuild switch --flake .#lab
sudo nixos-rebuild switch --flake .#lab --show-trace
sudo nixos-rebuild switch --flake .#lab -L
sudo nixos-rebuild switch --flake path:/home/you/nixos-config#lab| Flag | Use |
|---|---|
--show-trace |
Eval errors |
-L |
Build logs |
--impure |
Avoid unless you know why |
--update-input / flake update |
Separate concern (Day 24) |
Worked examples bank
Example A — Safe build
cd ~/nixos-config
nixos-rebuild build --flake .#labExample B — test then switch
sudo nixos-rebuild test --flake .#lab
# exercise the change
sudo nixos-rebuild switch --flake .#labExample C — rollback
sudo nixos-rebuild switch --rollbackLab 1 — Inventory generations before chaos
sudo nix-env -p /nix/var/nix/profiles/system --list-generations
readlink -f /nix/var/nix/profiles/systemRecord current generation number in notes.
Lab 2 — Mode drills (harmless package)
- Add a tiny package with
bootmode only.
- Confirm it is not on PATH until reboot (or document actual behavior you observe).
- Reboot (VM snapshot first).
- Confirm package present.
Then:
- Use
testfor another package.
- Confirm available immediately.
- Reboot without
switchand observe whether it persists as default—write what happened.
Lab 3 — Deliberate break → rollback
VM snapshot first.
Introduce a recoverable break, for example:
# BAD on purpose — comment clearly
# environment.etc."BREAK".text = "…";
# or disable ssh while on console-only VM:
# services.openssh.enable = false; # only if you have consoleSafer break: set environment.systemPackages to include a typo attribute that fails eval—that is not a rollback drill. For rollback you need a successful bad generation:
environment.etc."day13-break".text = "broken intentionally";
# and something mildly annoying but not fatal, OR
# networking.firewall.allowedTCPPorts = [ ]; with ssh still via consoleBetter pedagogical break:
switcha config that installs a known packagecowsay.
- Then
switcha config that removes it and setsenvironment.variables.DAY13 = "broken";.
rollbackand showcowsay/env difference.
Or classic:
sudo nixos-rebuild switch --flake .#lab
# introduce change that succeeds but you dislike
sudo nixos-rebuild switch --rollbackDocument generation numbers before/after.
Lab 5 — Incident card
Create ~/nixos-config/docs/rollback.md:
# Rollback card
1. Console access method: …
2. List generations: …
3. Rollback command: …
4. Boot menu steps: …
5. Last known good generation: …Common gotchas
| Symptom | Theory |
|---|---|
| Rollback “did nothing” for app data | Data ≠ system gen |
| Locked out via SSH | Use console; prefer test for net changes |
| No older generations | GC’d or first install |
test change vanished after reboot |
Expected if never switched |
| Flake path wrong under sudo | Use absolute path or root’s cwd discipline |
Checkpoint
- Explain switch/test/boot/build
- List system generations
- Performed a rollback successfully
- Boot menu generation selection documented
- Incident card written
- VM snapshot restored option still available
Commit
cd ~/nixos-config
git add .
git commit -m "day13: rebuild modes generations rollback card"Tomorrow
Day 14 — Users & access. Declarative users, wheel/sudo, and SSH keys only for remote admin habits.