Project 5: Distro identity and branding

Updated

July 30, 2026

Project 5 — Name your distro: identity, versioning, branding assets

Goal: Define a product identity for a NixOS-based derivative: codename, version scheme, artwork, and modules that surface branding (os-release, issue/banner, optional loader labels, shipped logo).

This is the “what is this OS called?” project—before ISO polish (Project 6) and product packaging (Project 7).

Field Value
Baseline Nix 2.34, NixOS / nixpkgs 26.05
Time ~2–4 hours
Depends on Stage II host flake comfort; the modules as consumer chapter–19 modules
Artifact Distro flake slice + PRODUCT.md + branding/

Why this project exists

A “custom distro” that still says NAME=NixOS everywhere is only a config repo. Identity work forces you to:

  • Pick a distinct name (trademark-safe).
  • Encode version/codename policy.
  • Express branding as NixOS modules, not ad-hoc files after install.
  • Treat artwork and licenses as first-class inputs.

Projects 6–7 reuse this module graph. Do not skip PRODUCT.md.

Important

Trademark: “NixOS” is a trademark of the NixOS Foundation. Use a distinct product name; say “NixOS-based” when describing heritage. Do not ship official snowflake art as your logo. See community branding guidance.


Prerequisites

  • A NixOS VM or machine you can rebuild (prefer disposable).
  • Flake-based nixosConfigurations experience (the flake hosts chapter+).
  • Optional: copy book sample assets from content/99-projects/branding/.

Theory

Identity surfaces

Surface User-visible? Typical mechanism
/etc/os-release Tools, neofetch, support scripts NixOS options / etc files
/etc/issue TTY login banner environment.etc.issue
System label Boot loader generations system.nixos.label
/etc/<distro>/logo.svg Docs, greeter hooks later environment.etc
Pretty hostname motd SSH banners users.motd / issue.net

Not every surface must be perfect on day one. Pick ≥3 and make them consistent.

Version scheme for derivatives

Track base nixpkgs explicitly:

<distro-version> = <nixpkgs-release>.<distro-patch>[-label]
example: 26.05.0-lab1
Component Meaning
26.05 Upstream release train you track
.0 Your product revision on that train
-lab1 Optional channel/codename tag

Record the flake input rev in PRODUCT.md when you cut a “release.”

Modules as product API

options.myDistro.*   →  config that applies branding

Downstream hosts set:

myDistro.enable = true;
myDistro.name = "YararaOS";

instead of copy-pasting os-release text into every host.

os-release merge reality

NixOS already generates os-release-related content. On 26.05, prefer documented options when they exist (names evolve—verify on your pin), for example patterns around distro id / pretty name if available. Overwriting environment.etc."os-release" can fight the module system.

Lab rule: after rebuild, cat /etc/os-release is the source of truth. Adjust the module until your name is visible without breaking activation.


Product sheet (PRODUCT.md)

Create and fill:

# PRODUCT.md

| Field | Value |
|-------|-------|
| Distro name | |
| Version | 26.05.0-lab1 |
| Codename | |
| Homepage | |
| Bug tracker | |
| Base | nixos-26.05 |
| Default edition | minimal |
| Trademark notes | distinct name; NixOS-based |
| Flake input rev (optional) | |

Example names: YararaOS, MyLabOS, RiverNix—anything not “NixOS” alone.


Repo layout

my-distro/
  flake.nix
  flake.lock
  PRODUCT.md
  branding/
    logo.svg
    README.md          # license for assets
  modules/
    identity.nix
  hosts/
    lab.nix            # disposable VM/host
    iso.nix            # stub for Project 6

Copy sample branding from this book

# adjust path to your checkout of this monorepo
cp -r /path/to/books/NixOS/content/99-projects/branding ./branding
# remix logo.svg; keep license notes honest

Book kit:

File Purpose
logo.svg Sample mark (CC0)
os-release.fragment.example Field checklist
plymouth-theme-notes.md Splash strategy (Projects 6–7)
README.md License + trademark notes

Step 1 — Identity module

modules/identity.nix:

{ config, lib, pkgs, ... }:
let
  cfg = config.myDistro;
in {
  options.myDistro = {
    enable = lib.mkEnableOption "distro identity defaults";

    name = lib.mkOption {
      type = lib.types.str;
      default = "MyLabOS";
      description = "Pretty distro name (distinct from NixOS trademark).";
    };

    id = lib.mkOption {
      type = lib.types.str;
      default = "mylabos";
      description = "Lowercase ID-like token for os-release ID=";
    };

    version = lib.mkOption {
      type = lib.types.str;
      default = "26.05.0-lab1";
    };

    codename = lib.mkOption {
      type = lib.types.str;
      default = "lab";
    };

    homeUrl = lib.mkOption {
      type = lib.types.str;
      default = "https://example.invalid/";
    };
  };

  config = lib.mkIf cfg.enable {
    system.nixos.label = "${cfg.name}-${cfg.version}";

    # Validate on 26.05: prefer first-class options if present on your pin.
    # The etc approach is a lab lever—adjust when it conflicts with generators.
    environment.etc."os-release".text = lib.mkDefault ''
      NAME="${cfg.name}"
      ID=${cfg.id}
      ID_LIKE=nixos
      VERSION="${cfg.version}"
      VERSION_CODENAME=${cfg.codename}
      PRETTY_NAME="${cfg.name} ${cfg.version} (NixOS-based)"
      HOME_URL="${cfg.homeUrl}"
      DOCUMENTATION_URL="${cfg.homeUrl}docs"
      SUPPORT_URL="${cfg.homeUrl}support"
      BUG_REPORT_URL="${cfg.homeUrl}issues"
    '';

    environment.etc.issue.text = lib.mkDefault ''
      ${cfg.name} ${cfg.version} \n \l
    '';

    users.motd = lib.mkDefault ''
      Welcome to ${cfg.name} ${cfg.version} (${cfg.codename})
      Base: NixOS / nixpkgs 26.05 track — lab derivative
    '';

    environment.etc."${cfg.id}/logo.svg".source = ../branding/logo.svg;

    # Optional: make support tooling discover the brand easily
    environment.variables.DISTRIB_ID = cfg.id;
  };
}
Warning

If activation or other modules fight environment.etc."os-release", switch to the documented 26.05 options for distro naming and keep issue + logo + system.nixos.label as your guaranteed branding. Record what worked in PRODUCT.md.

Use the field checklist in branding/os-release.fragment.example when editing.


Step 2 — Lab host + flake

hosts/lab.nix:

{ config, lib, pkgs, ... }: {
  imports = [ ../modules/identity.nix ];

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

  networking.hostName = "mylabos-lab";

  # Minimal disposable VM assumptions — adapt to your hardware config:
  # boot.loader, fileSystems, users.users.... from your existing lab host.
}

flake.nix (sketch):

{
  description = "MyLabOS — Project 5 identity";

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

  outputs = { self, nixpkgs }: {
    nixosConfigurations.lab = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux"; # or your arch
      modules = [
        ./hosts/lab.nix
        # ./hardware-configuration.nix  # real disk/boot
      ];
    };
  };
}
nix flake lock
sudo nixos-rebuild switch --flake .#lab
# or: nixos-rebuild build-vm --flake .#lab && ./result/bin/run-*-vm

Step 3 — Verify identity

On the running system:

cat /etc/os-release
cat /etc/issue
cat /etc/motd 2>/dev/null || true
ls -la /etc/mylabos/   # or your cfg.id
hostnamectl 2>/dev/null || true

Capture outputs into lab-evidence.md or paste under PRODUCT.md “Evidence” section.

Optional screenshot: TTY login showing /etc/issue.


Step 4 — Artwork + consistency

  1. Sample or custom logo.svg; license note in branding/README.md.
  2. No official NixOS snowflake as your mark.
  3. Prefer SVG; rsvg-convert -w 256 branding/logo.svg -o branding/logo.png if a greeter needs PNG later.
  4. Confirm name/version match across options defaults, os-release, issue, system.nixos.label, motd—drift multiplies in Project 6.

Pitfalls

Symptom Likely cause Fix
Still says NixOS everywhere Module not imported / disabled Import + enable
os-release reverts Lost merge to generator Supported options; less force
Logo missing Bad relative path Path relative to module file
Trademark risk Named only “NixOS …” Distinct NAME / ID
Rebuild fails after clean eval Missing boot/fs modules Add real hardware config

Acceptance criteria

  • PRODUCT.md complete (name, version, base, trademark note).
  • branding/logo.svg + license note.
  • Identity module: clean rebuild of lab host/VM.
  • os-release and/or issue show your name (document option path if needed).
  • Logo under /etc/<id>/ (or equivalent).
  • flake.lock committed; no official NixOS marks as product logo.

Stretch goals

  1. myDistro.edition option varying motd.
  2. Doc package installing PRODUCT.md under $out/share/doc/....
  3. hosts/iso.nix stub importing only identity (ready for Project 6).

Self-check

  1. Why put 26.05 in the product version string?
  2. NAME vs ID in os-release?
  3. Why modules instead of editing files after first boot?
  4. Which surfaces survive headless installs?

Next: Project 6 — ISO and themes.