Project 6: Distro ISO and themes
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 (
myDistroidentity, 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";
}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.
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:stdioWithout 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/mylabosStep 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:
- Test identity module on
build-vm/ normal host first (Project 5).
- Change one theme knob per ISO rebuild when debugging.
- Keep a
hosts/iso-minimal.nixandhosts/iso-graphical.nixif you experiment—do not thrash a single file blindly.
- Use binary caches; do not disable substitutes “for purity theater” on ISO day.
- 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.namePlymouth (optional after minimal ISO is green)
boot.plymouth.enable = true;
boot.plymouth.theme = "spinner";
# later: themePackages + custom theme derivation; test only in a fat VMTimebox 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.isoImageor 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
- Graphical installer + greeter logo.
- Custom Plymouth from
logo.svg.
- Generators: qcow2 and iso from same modules.
- Serial QEMU smoke grepping os-release.
Self-check
- Why console theming first?
- Which derivation path built the ISO?
- How does Project 5 prevent ISO-only drift?
- What do you drop first under a 30‑minute CI budget?