Project 5: Distro identity and branding
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.
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
nixosConfigurationsexperience (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 honestBook 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;
};
}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-*-vmStep 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 || trueCapture outputs into lab-evidence.md or paste under PRODUCT.md “Evidence” section.
Optional screenshot: TTY login showing /etc/issue.
Step 4 — Artwork + consistency
- Sample or custom
logo.svg; license note inbranding/README.md.
- No official NixOS snowflake as your mark.
- Prefer SVG;
rsvg-convert -w 256 branding/logo.svg -o branding/logo.pngif a greeter needs PNG later.
- 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.mdcomplete (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.lockcommitted; no official NixOS marks as product logo.
Stretch goals
myDistro.editionoption varying motd.
- Doc package installing
PRODUCT.mdunder$out/share/doc/....
hosts/iso.nixstub importing only identity (ready for Project 6).
Self-check
- Why put
26.05in the product version string?
NAMEvsIDin os-release?
- Why modules instead of editing files after first boot?
- Which surfaces survive headless installs?
Next: Project 6 — ISO and themes.