Project 6: Distro ISO and themes

Updated

July 30, 2026

Project 6 — Installer ISO with themes (Plymouth / greeter / console)

Goal: Produce a bootable installer ISO (or live image) for your branded distro using NixOS installer profiles (isoImage) and/or nixos-generators, with visible theming: console colors/fonts, optional Plymouth splash, greeter/wallpaper hooks for a graphical profile, and ISO volume metadata.

Field Value
Baseline Nix 2.34, NixOS 26.05
Time ~3–8 hours (downloads + iteration)
Depends on Project 5 identity module; the images/generators chapter images helpful
Artifact .iso that boots in QEMU + branding checklist

Why this project exists

A distro people can boot beats a distro that only exists as nixosConfigurations.laptop. ISO generation connects:

  • Module graph (identity + themes + installer profile)
  • Image format (system.build.isoImage)
  • Runtime verification (QEMU)

You will also learn why theme options differ by display manager and why console branding is the reliable baseline.


Prerequisites

  • Project 5 green (myDistro identity, logo, PRODUCT.md).
  • Disk space: plan for multi‑GB store growth.
  • KVM recommended:
ls /dev/kvm && echo "KVM ok"
  • Patience: first ISO eval/build is the slowest.

Theory / architecture

flake.nix
  nixosConfigurations.iso
       │
       ├─ installer profile (minimal or graphical)
       ├─ modules/identity.nix     (Project 5)
       └─ modules/themes.nix       (this project)
       │
       ▼
  config.system.build.isoImage
       │
       ▼
  result/iso/*.iso  →  QEMU / VirtualBox / USB
Approach Notes
modulesPath installer CD profiles Classic; well documented
nixos-generators format = "iso" Same modules, format abstraction (the images/generators chapter)
Graphical Calamares/GNOME profiles Heavier; better greeter experiments

Theme layers (cheap → expensive)

Layer Cost Reliability in QEMU
Console font/colors + issue Low High
ISO volume label / isoName Low High
Plymouth stock theme Medium Medium (needs quiet splash path)
Custom Plymouth package High Medium
GDM/SDDM logos Medium Option names move across pins

Lab strategy: nail console + os-release + volume label first; add Plymouth/greeter after the ISO boots cleanly.


Lab tree (extends Project 5)

my-distro/
  PRODUCT.md
  flake.nix
  flake.lock
  branding/
    logo.svg
    README.md
  modules/
    identity.nix
    themes.nix
  hosts/
    lab.nix
    iso.nix
  NOTES-ISO.md          # build time, size, QEMU line

Step 1 — ISO host configuration

hosts/iso.nix:

{ config, pkgs, lib, modulesPath, ... }: {
  imports = [
    "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
    # Graphical experiment (much larger) — pick one profile only:
    # "${modulesPath}/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix"
    ../modules/identity.nix
    ../modules/themes.nix
  ];

  myDistro.enable = true;
  myDistro.name = "MyLabOS";
  myDistro.id = "mylabos";
  myDistro.version = "26.05.0-iso1";

  # Live image ergonomics
  networking.wireless.enable = lib.mkForce false; # avoid iwd/wpa fights on minimal ISOs if forced elsewhere
  services.openssh.enable = true;

  # Installer images often define a `nixos` user — keep passwords per template docs.
  # Do not bake production secrets into public ISO flakes.

  # ISO metadata — confirm option names on your 26.05 pin if eval fails
  isoImage.volumeID = lib.mkDefault "MYLABOS_2605";
  isoImage.isoName = lib.mkDefault "mylabos-26.05-x86_64.iso";
}
Note

Exact attribute names under isoImage.* can differ slightly by release. If eval errors, nixos-option / source grep on your pin and adjust. Volume label is a branding surface either way.


Step 2 — Themes module

modules/themes.nix:

{ config, lib, pkgs, ... }: {
  # --- Console branding (do this first) ---
  console = {
    font = "Lat2-Terminus16";
    colors = [
      # 16-color palette, hex without leading #
      "1d1e28" "ff5555" "50fa7b" "f1fa8c"
      "bd93f9" "ff79c6" "8be9fd" "f8f8f2"
      "6272a4" "ff6e6e" "69ff94" "ffffa5"
      "d6acff" "ff92df" "a4ffff" "ffffff"
    ];
  };

  # --- Plymouth (optional; enable after minimal ISO works) ---
  # boot.plymouth.enable = true;
  # boot.plymouth.theme = "spinner";
  # boot.plymouth.themePackages = [ ]; # custom later

  # --- Graphical greeter hooks (only on graphical installer profiles) ---
  # Option paths vary by DM + nixpkgs revision. Verify before relying on them.
  # services.displayManager.sddm… / services.xserver.displayManager.gdm…
  # services.xserver.displayManager.gdm.logo = ./../branding/logo.png;

  environment.systemPackages = with pkgs; [
    git
    vim
    # fastfetch or neofetch — nice for DEMO screenshots
  ];
}

Read book notes: branding/plymouth-theme-notes.md.

Tip

If greeter logo options moved on your pin, do not block the project. Ship console + os-release + volume label + logo under /etc/<id>/, and document greeter as stretch.


Step 3 — Flake output

{
  description = "MyLabOS ISO — Project 6";

  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";

  outputs = { self, nixpkgs }: {
    nixosConfigurations.iso = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [ ./hosts/iso.nix ];
    };

    # Optional convenience package-like output via apps/scripts in README
  };
}

Build

nix build -L .#nixosConfigurations.iso.config.system.build.isoImage
ls -lh result/iso/

Record in NOTES-ISO.md:

  • Wall-clock time
  • ISO size
  • nixpkgs rev from flake.lock
  • Exact build command

Alternative: nixos-generators

# sketch — pin nixos-generators and follows nixpkgs
# packages.x86_64-linux.iso = nixos-generators.nixosGenerate {
#   system = "x86_64-linux";
#   format = "iso";
#   modules = [ ./hosts/iso.nix ];
# };

Prefer one path for the lab so debugging stays sane.


Step 4 — Boot test (QEMU)

ISO=$(ls result/iso/*.iso | head -1)
qemu-system-x86_64 \
  -m 2048 \
  -cdrom "$ISO" \
  -boot d \
  -enable-kvm \
  -serial mon:stdio

Without KVM, drop -enable-kvm and expect slower boots; raise -m if the graphical profile OOMs.

VirtualBox / UTM

Import the ISO as an optical drive, boot order CD first. Same verification checklist.

Verification checklist (live session)

Check Pass? Notes
Boots to installer/live environment
cat /etc/os-release shows product name
cat /etc/issue branded
Console palette/font applied
Logo present under /etc/<id>/
SSH or local login works
ISO volume label visible (where shown)
Plymouth (if enabled) optional
# inside live system
cat /etc/os-release
cat /etc/issue
ls /etc/mylabos

Step 5 — Branding matrix

Copy into NOTES-ISO.md and fill status:

Surface Status How verified
os-release / issue
Console colors/font
ISO volumeID / isoName
Logo on live FS
Plymouth skipped / ok
Installer wallpaper/logo skipped / ok

Acceptance for Project 6: branding visible on ≥2 surfaces in the live environment, ISO boots, build command documented.


Step 6 — Iterate without tears

ISO rebuilds are expensive. Iteration tips:

  1. Test identity module on build-vm / normal host first (Project 5).
  2. Change one theme knob per ISO rebuild when debugging.
  3. Keep a hosts/iso-minimal.nix and hosts/iso-graphical.nix if you experiment—do not thrash a single file blindly.
  4. Use binary caches; do not disable substitutes “for purity theater” on ISO day.
  5. Tag git when an ISO is good: iso-26.05.0-iso1-green.
# eval-only sanity (faster than full ISO when checking option typos)
nix eval .#nixosConfigurations.iso.config.networking.hostName
nix eval .#nixosConfigurations.iso.config.myDistro.name

Plymouth (optional after minimal ISO is green)

boot.plymouth.enable = true;
boot.plymouth.theme = "spinner";
# later: themePackages + custom theme derivation; test only in a fat VM

Timebox custom Plymouth—failures are expensive.


Pitfalls

Symptom Likely cause Fix
Out of disk /nix full Free tens of GB; careful GC
QEMU hang Wrong arch / low RAM Match system; raise -m
Unbranded live Modules not on iso host Fix imports
isoImage.* unknown Pin difference Grep installer modules
Graphical ISO never ends Huge closure Minimal profile for green

Acceptance criteria

  • ISO builds (system.build.isoImage or generators).
  • VM boots live/installer environment.
  • Branding on ≥2 surfaces.
  • Build command, size, time in NOTES-ISO.md.
  • ISO version tag (-iso1) noted; no production secrets.

Stretch goals

  1. Graphical installer + greeter logo.
  2. Custom Plymouth from logo.svg.
  3. Generators: qcow2 and iso from same modules.
  4. Serial QEMU smoke grepping os-release.

Self-check

  1. Why console theming first?
  2. Which derivation path built the ISO?
  3. How does Project 5 prevent ISO-only drift?
  4. What do you drop first under a 30‑minute CI budget?

Next: Project 7 — Complete distro product.