Lab 0B — NixOS development machine

Updated

July 30, 2026

Lab 0B — NixOS development machine

Goal: Configure a comfortable, stable environment for writing flakes, reviewing nixpkgs, running builds, and pushing configs to lab VMs—without turning your editor session into the system under test.

On a single Elitebook, Lab 0B usually lives on the host (same metal as Lab 0A). The split is role, not necessarily second hardware.

Tip

Text-first infrastructure (flakes, modules, virsh/incus CLI) is also what makes human + AI co-maintenance safe: agents can read and propose diffs against git. GUI-only state and untracked shell fixes cannot. Keep Lab 0B disciplined: if it matters, it is in the repo.

Dev machine vs journey guest

Dev (this lab) Journey guest (nixlab)
Purpose Author configs, run nix tools Be the OS you break
Rebuild frequency Low High
Secrets Your age keys, git ssh Lab secrets only
Editor Yes Optional
“Does this need to be pretty?” Yes (you live here) No

Part 1 — Install Nix (if host is not NixOS)

Multi-user install; enable flakes:

# ~/.config/nix/nix.conf
experimental-features = nix-command flakes

On NixOS host, put the same under nix.settings.experimental-features.

Part 2 — Essential packages

NixOS host module sketch

{ pkgs, ... }: {
  environment.systemPackages = with pkgs; [
    git
    git-lfs
    vim
    neovim
    ripgrep
    fd
    jq
    tree
    htop
    tmux
    direnv
    nix-direnv
    nixfmt-rfc-style   # or nixfmt — pick one and stick to it
    nil                # LSP for Nix
    # optional:
    # vscode / emacs — your preference
  ];

  programs.direnv.enable = true;
  programs.direnv.nix-direnv.enable = true;

  # nice for rebuilds against VMs
  environment.systemPackages = with pkgs; [
    # already have git etc.
  ];
}

Non-NixOS host

nix profile install nixpkgs#git nixpkgs#neovim nixpkgs#direnv nixpkgs#nix-direnv nixpkgs#nil

Part 3 — Editor + LSP

Editor Nix LSP
Neovim/VS Code nil or nixd
Emacs same + envrc

Minimal checks:

nil --help   # or nixd
rg "mkOption" ~/src/nix-config -n | head

Part 4 — Git layout for the whole journey

~/src/nix-config/          # or ~/lab/90daysofx/nixos-config
  flake.nix
  flake.lock
  hosts/
    nixlab/
    deploy-a/
  modules/
  homes/                   # from Stage III
  secrets/                 # encrypted only (Stage IV)
  .envrc                   # use flake
  README.md
cd ~/src/nix-config
git init
echo ".direnv" >> .gitignore
echo "result" >> .gitignore
echo "result-*" >> .gitignore

.envrc:

use flake
direnv allow

Part 5 — Build ergonomics

Keep host builds from melting a 2012 laptop

nix.settings = {
  max-jobs = 2;          # Elitebook: start low
  cores = 2;
  # auto-optimise-store = true; # optional
};

Substituters

Default cache.nixos.org is fine. Later stages may add Cachix/Attic—document tokens only in sops, never in world-readable nix.

Remote / guest as builder (optional, Stage V+)

When nixlab is stronger or you want isolation:

# conceptual — configure nix.buildMachines later in curriculum

Early on: build on host, deploy to guest is simplest.

Part 6 — Access to journey VMs

# ssh config ~/.ssh/config
Host nixlab
  HostName 192.168.122.10
  User YOU
  IdentityFile ~/.ssh/id_ed25519_lab

Host deploy-a
  HostName 192.168.122.11
  User YOU
  IdentityFile ~/.ssh/id_ed25519_lab

Generate a lab-only key:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_lab -C "lab-elitebook"

Do not reuse your primary GitHub key as the only lab key if you prefer blast-radius control.

Part 7 — Workflow commands you will live with

# evaluate / build a guest config from the flake (host)
nix build .#nixosConfigurations.nixlab.config.system.build.toplevel

# apply (from host, once SSH works)
nixos-rebuild switch --flake .#nixlab --target-host YOU@nixlab --use-remote-sudo

# or: ssh and rebuild inside guest
ssh nixlab 'sudo nixos-rebuild switch --flake /path/to/config#nixlab'

# format
nix fmt   # if configured

Part 8 — Home Manager on the dev role

Optional but nice on the host:

  • Shell (fish/zsh), git, ssh config
  • Keep HM on host separate from guest HM experiments

When Stage III teaches HM, practice on nixlab first if you want isolation; port habits back to host later.

Part 9 — Binary cache & secrets (preview only)

Later stage Dev machine prep
sops-nix Install sops, age; create age key backup offline
CI GitHub/GitLab SSH deploy key for the flake repo
Cachix Account ready; don’t put signing key in git

Create age key early if you want:

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# back up keys.txt to password manager / encrypted USB

Part 10 — Quality of life on old hardware

Tip Why
External monitor + keyboard 2570p is a server that happens to fold
tmux/zellij Long builds survive SSH drops
Disable heavy desktop effects on host Free RAM for VMs
nix-store --optimise occasionally Disk
Don’t run browser + 2 VMs + big compile Sequentialize

Dev machine checklist

  • nix flake metadata works in ~/src/nix-config (even empty flake)
  • direnv loads a simple devShell
  • Editor can jump/lint Nix files
  • SSH to nixlab (or console) documented
  • Lab SSH key created; age key backup plan written
  • max-jobs/cores set conservatively
  • .gitignore includes result and .direnv

Anti-patterns

Anti-pattern Prefer
Editing production-like host to “try modules” Try on nixlab (or host-sim VM)
One 2k-line configuration.nix on host Layout from Stage III early for guests
Storing secrets in world-readable nix sops/agenix later
Building with max-jobs = auto until OOM Cap jobs on Elitebook
“YOLO” imperative fix left uncommitted Encode fix in flake; commit with why
Relying on virt-manager clicks you cannot replay Keep a virt-install / incus snippet in README

Next

Lab 0C — HP Elitebook 2570p profile
→ Then Day 1 — Why Nix exists