Custom NixOS ISOs and Distro Branding

Updated

September 12, 2026

Custom NixOS ISOs and Distro Branding

A rescue ISO with a renamed file is not a distro. The boring default is: import the official installer CD module, put identity and artwork in one shared module, and install from that ISO into the same flake — do not fork nixpkgs to change a logo.

The generators chapter builds images (ISO, qcow, SD). This chapter is the installer / live distro: boot menu, /etc/os-release, Plymouth, greeter, Calamares. The desk ships DeskOS internally — NixOS 26.05 with a name, colours, and a first-boot story the onboarding laptop already trusts.

NixOS logos and the snowflake are a separate brand. Do not recolour them and call the result yours. Use your own mark. Keep ID_LIKE=nixos so tools still know what they are on.

Mental model

Layer What a human sees Nix
File name / USB label DeskOS-26.05.iso, volume in lsblk isoImage.isoName, volumeID, edition
Boot menu Title, background, GRUB theme distroName, splash images, grubTheme, syslinuxTheme
Live OS hostnamectl, cat /etc/os-release system.nixos.distroId / distroName / extraOSReleaseArgs
Boot splash Logo while the kernel starts boot.plymouth
Graphical installer Calamares window, slideshow vendored branding.desc + PNGs
Installed machine Same name after nixos-install same identity module in nixosConfigurations
artwork/  +  modules/desk-identity.nix
                 │
     ┌───────────┼───────────┐
     ▼           ▼           ▼
  installer ISO   live USB   desk-laptop (flake)

iso (live) ≠ install-iso (has nixos-install / Calamares). Pick the installer module on purpose.

ISO9660 volumeID is short (keep it under 32 characters). A 60-character marketing name belongs in isoName and PRETTY_NAME, not on the volume.

Worked examples

Case 1: Minimal installer ISO from modulesPath

Save as flake.nix:

# flake.nix
{
  description = "DeskOS installer ISO";

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

  outputs = { self, nixpkgs }: {
    nixosConfigurations.desk-iso = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ({ modulesPath, pkgs, lib, ... }: {
          imports = [
            (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")
          ];

          isoImage.isoName = "DeskOS-26.05-x86_64.iso";
          isoImage.edition = "desk";
          isoImage.volumeID = "DESKOS-2605-X64";
          isoImage.makeEfiBootable = true;
          isoImage.makeUsbBootable = true;
          isoImage.appendToMenuLabel = " installer";

          environment.systemPackages = with pkgs; [
            vim git curl jq
            disko
          ];

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

          services.openssh.enable = true;
          users.users.nixos.openssh.authorizedKeys.keys = [
            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI… deskadmin@ops-01"
          ];

          system.stateVersion = "26.05";
        })
      ];
    };
  };
}
nix build .#nixosConfigurations.desk-iso.config.system.build.isoImage
ls -lh result/iso/

Output (shape):

-r--r--r-- 1 root root 1.2G  DeskOS-26.05-x86_64.iso

On 26.05 you can also:

nixos-rebuild build-image --image-variant iso-installer --flake .#desk-iso

If the flake already has nixos-generators, format = "install-iso" is the same installer family. format = "iso" is a live image without the installer scripts — wrong for onboarding.

Flash:

sudo dd if=$(readlink -f result/iso/*.iso) of=/dev/sdX bs=4M status=progress conv=fsync

Case 2: Identity — /etc/os-release, not a wallpaper

Save as modules/desk-identity.nix:

# modules/desk-identity.nix
{ lib, ... }:
{
  system.nixos.distroId = "deskos";
  system.nixos.distroName = "DeskOS";
  system.nixos.vendorId = "desk";
  system.nixos.vendorName = "Desk Platform";
  system.nixos.variant_id = "workstation";
  system.nixos.variantName = "DeskOS Workstation";
  system.image.id = "deskos-installer";
  system.image.version = "26.05";

  # When distroId != "nixos", NixOS already sets ID_LIKE=nixos.
  system.nixos.extraOSReleaseArgs = {
    HOME_URL = "https://git.desk.internal/platform/deskos";
    DOCUMENTATION_URL = "https://git.desk.internal/platform/deskos#readme";
    SUPPORT_URL = "https://desk.internal/helpdesk";
    BUG_REPORT_URL = "https://git.desk.internal/platform/deskos/issues";
    ANSI_COLOR = "0;38;2;47;95;117";
    LOGO = "deskos-mark";
    CPE_NAME = "cpe:/o:desk:deskos:26.05";
  };

  system.nixos.extraLSBReleaseArgs = {
    DISTRIB_DESCRIPTION = "DeskOS 26.05 (Yarara)";
  };

  networking.hostName = lib.mkDefault "deskos";
}

Import it from the ISO and from nixosConfigurations.desk-laptop. After install:

cat /etc/os-release
hostnamectl
NAME=DeskOS
ID=deskos
ID_LIKE=nixos
PRETTY_NAME="DeskOS 26.05 (Yarara)"
VARIANT="DeskOS Workstation"
IMAGE_ID=deskos-installer

ID=deskos is a lowercase token (os-release). NAME is the human string. Do not set ID=NixOS and a custom NAME — scripts key off ID.

Leave system.nixos.release / codeName alone. Those still say 26.05 / Yarara because that is the nixpkgs train. A fake VERSION_ID=1.0 that hides 26.05 is how advisories miss you.

Case 3: Bootloader artwork (GRUB + BIOS splash)

Splash files are PNG (or non-progressive JPEG). Generate placeholders with ImageMagick so the flake builds before the designer ships real art:

# modules/desk-iso-brand.nix
{ pkgs, lib, ... }:
let
  splashBios = pkgs.runCommand "deskos-splash-bios.png" {
    nativeBuildInputs = [ pkgs.imagemagick ];
  } ''
    magick -size 800x600 xc:'#1b2838' \
      -gravity center -fill '#7ebae4' -pointsize 48 \
      -annotate 0 'DeskOS' "$out"
  '';
  splashEfi = pkgs.runCommand "deskos-splash-efi.png" {
    nativeBuildInputs = [ pkgs.imagemagick ];
  } ''
    magick -size 1920x1080 xc:'#1b2838' \
      -gravity center -fill '#7ebae4' -pointsize 72 \
      -annotate 0 'DeskOS' "$out"
  '';
in
{
  isoImage.splashImage = splashBios;
  isoImage.efiSplashImage = splashEfi;
  isoImage.grubTheme = null; # skip nixos-grub2-theme; use colours below
  isoImage.syslinuxTheme = ''
    MENU TITLE DeskOS 26.05
    MENU RESOLUTION 800 600
    MENU COLOR SEL 7;37;40 #FFFFFFFF #FF2F5F75 std
    MENU COLOR UNSEL 37;40 #FF7EBAE4 #00000000 none
  '';

  boot.loader.grub.extraConfig = ''
    set color_normal=light-cyan/black
    set color_highlight=black/light-cyan
    set menu_color_normal=light-cyan/black
    set menu_color_highlight=black/light-cyan
  '';
}

Replace the runCommand PNGs with ./artwork/splash-bios.png once the files exist. Keep artwork in git (or a FOD), not fetched from a CMS at eval time.

isoImage.grubTheme expects a GRUB2 theme directory (theme.txt + fonts + background). pkgs.nixos-grub2-theme is the NixOS default — set it to null or to your theme package. Mixing a custom distroName with the stock snowflake theme looks like a counterfeit.

isoImage.contents drops extra files on the ISO:

{
  isoImage.contents = [
    {
      source = ./README-INSTALL.md;
      target = "/README-INSTALL.md";
    }
  ];
}

Case 4: Plymouth + live graphical session

For a graphical installer, import the Calamares CD instead of minimal:

# modules/desk-iso-graphical.nix
{ modulesPath, pkgs, ... }:
{
  imports = [
    (modulesPath + "/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix")
    ./desk-identity.nix
    ./desk-iso-brand.nix
  ];

  isoImage.edition = "plasma6";

  boot.plymouth.enable = true;
  boot.plymouth.theme = "breeze";
  boot.plymouth.logo = ./artwork/logo-48.png; # PNG only; 48×48 matches GDM

  boot.kernelParams = [ "quiet" ];
  boot.consoleLogLevel = 3;
  boot.initrd.verbose = false;

  services.displayManager.sddm.enable = true;
  services.desktopManager.plasma6.enable = true;
}

boot.plymouth.theme = "breeze" pulls a Plymouth theme whose osName is config.system.nixos.distroName — so Case 2’s DeskOS appears on the splash without a custom theme package. theme = "bgrt" keeps the firmware OEM logo; that is often what you want on laptops, not on a USB installer.

A live wallpaper (Plasma):

{
  environment.etc."deskos/wallpaper.png".source = ./artwork/wallpaper.png;
}

Point the Plasma look-and-feel at that path in a small xdg autostart if you must; do not vendor all of plasma-workspace-wallpapers (it inflates the ISO by hundreds of MiB).

Case 5: Calamares branding (the installer window)

Calamares on the NixOS graphical ISO uses pkgs.calamares-nixos-extensions, branding component nixos. Override the component with files you own.

Save as artwork/calamares/branding.desc (Calamares format; truncated to the strings that matter):

# artwork/calamares/branding.desc
---
componentName:  deskos
windowExpanding: fullscreen
windowPlacement: center
sidebar: widget
navigation: widget
strings:
    productName:         DeskOS
    shortProductName:    DeskOS
    version:             26.05
    shortVersion:        26.05
    versionedName:       DeskOS 26.05
    shortVersionedName:  DeskOS
    bootloaderEntryName: DeskOS
    productUrl:          https://git.desk.internal/platform/deskos
    supportUrl:          https://desk.internal/helpdesk
    knownIssuesUrl:      https://git.desk.internal/platform/deskos/issues
    releaseNotesUrl:     https://git.desk.internal/platform/deskos
    donateUrl:           https://git.desk.internal/platform/deskos
images:
    productIcon:         logo.svg
    productLogo:         logo-white.png
    productWelcome:      logo.svg
slideshow:               show.qml
style:   Raleway
welcomeStyleCalamares:   false

Put logo.svg, logo-white.png, and a one-slide show.qml next to that file. Then:

# modules/desk-calamares.nix
{ pkgs, ... }:
{
  environment.etc."calamares/branding/deskos".source = ./artwork/calamares;

  environment.etc."calamares/settings.conf".text = ''
    ---
    modules-search: [ local, /run/current-system/sw/lib/calamares/modules ]
    sequence:
      - show: [ welcome, locale, keyboard, partition, users, summary ]
      - exec: [ partition, mount, unpackfs, networkcfg, machineid, locale, keyboard, users, displaymanager, nixos, umount ]
      - show: [ finished ]
    branding: deskos
    prompt-install: false
    dont-chroot: false
  '';
}

bootloaderEntryName: DeskOS is what shows up in the firmware boot menu after install. If you leave it NixOS, the USB said DeskOS and the disk says NixOS — that is the bug users file as “install failed.”

Calamares still writes a NixOS configuration.nix. After first boot, switch the machine to the flake (nixos-rebuild switch --flake git+ssh://git.desk.internal/platform/deskos#desk-laptop) so identity + sshd + disk layout are the desk modules, not a one-shot Calamares file. The ISO is a delivery vehicle. The flake is the distro.

The trap

The trap is a wallpaper and a hostname with ID=nixos still in /etc/os-release, plus the official snowflake on the GRUB theme. That is a reskin people will treat as NixOS support. Set distroId, URLs, and your artwork, keep ID_LIKE=nixos, and say “based on NixOS 26.05” in README-INSTALL.md.

The other traps:

  • format = "iso" when you needed install-iso / installation-cd-minimal.nix — no nixos-install on the USB.
  • volumeID longer than the ISO9660 limit — stage-1 cannot find the CD.
  • Copying NixOS logo assets in violation of the NixOS branding guide.
  • A password in the ISO module (users.users.root.password = "desk") that ships to every USB in the drawer. SSH keys. Hashed passwords if you must.
  • Branding only the ISO, not nixosConfigurations. Day-two nixos-rebuild restores the snowflake.

The boring rule

  • Official installer module (installation-cd-minimal or graphical-calamares). 26.05 lock.
  • One desk-identity.nix imported by ISO and hosts. distroId lowercase; ID_LIKE=nixos.
  • Your artwork in git. No snowflake. Splash PNG / GRUB theme / Plymouth logo / Calamares branding.desc.
  • isoName + short volumeID. USB bootable (makeUsbBootable).
  • After install, the flake is source of truth. Calamares is first boot only.
  • Do not fork nixpkgs to change a string in os-release.

Try this

  1. Build Case 1. iso-info or isoinfo -d -i result/iso/*.iso and read the volume id.
  2. Boot the ISO in QEMU; cat /etc/os-releaseID=deskos and ID_LIKE=nixos.
  3. Swap Case 3 placeholders for real PNGs; rebuild; confirm GRUB no longer shows nixos-grub2-theme.
  4. On a graphical ISO, open Calamares and read the window title / bootloaderEntryName.
  5. Install into a VM, then nixos-rebuild switch --flake the same identity module; hostnamectl still says DeskOS.