Repository navigation
Writing a Spec
A spec is a YAML file that describes one distro. linuxlive checks it against a schema before building anything, so most mistakes show up in seconds:
python -m linuxlive validate path/to/spec.yamlThe validator lists every problem it finds, for example version: 0.1 is not of type 'string'. It works in two passes: first the shape of the file (field names, types, allowed values), then the images and app ids. If the first pass finds problems, fix them and run it again to see the second.
schema_version: 1
name: Classroom OS
id: classroom
version: "2026.10"
base: fedora
desktop: kde
apps:
- firefox
- libreoffice
- gcompris
- tuxpaint
- inkscape
branding:
wallpaper: art/classroom-wallpaper.png
logo: art/classroom-logo.png
accent_color: "#1565c0"| Field | Required | What it does | Rules |
|---|---|---|---|
schema_version |
yes | Version of the spec format. | Always 1. |
name |
yes | The display name, shown in the boot menu, on the boot splash, in the desktop's About screen and in /etc/os-release. |
Up to 40 characters: letters, digits, spaces and . _ + -. It must start with a letter or digit and end with a letter, digit or +. |
id |
no | The machine-friendly name, used for the hostname, file names and IMAGE_ID in /etc/os-release. |
2 to 30 characters: a lower-case letter, then lower-case letters, digits and -. Made from name if you leave it out (see below). |
version |
yes | Your distro's version. | Up to 20 characters: starts with a letter or digit, then letters, digits and . + ~ -. Put it in quotes, as in "0.1". |
base |
yes | The distribution your distro is built from. |
ubuntu or fedora (see Bases). |
desktop |
yes | The desktop environment. |
gnome, kde or cinnamon (see Desktops). |
apps |
no | Apps to preinstall from Flathub. | A list of app ids from the Apps table, at most 50, no repeats. Leave it out for no extra apps. |
branding |
yes | Your wallpaper, logo and accent colour. | See Branding. |
Any other field is an error, which catches typos such as desktops: or wallpapper:.
When id is missing, linuxlive lower-cases the name, turns every run of other characters into one -, and cuts the result to 30 characters:
| name | id |
|---|---|
My Distro 2 |
my-distro-2 |
Lawn Darts Linux |
lawn-darts-linux |
2048 Arcade |
os-2048-arcade (an id must start with a letter, so os- is added) |
Set id yourself if you want something shorter, such as id: lawndarts.
base |
Builds on | How it boots live |
|---|---|---|
ubuntu |
Ubuntu 26.04 LTS, from the official ubuntu:26.04 container image |
casper, like Ubuntu's own live images |
fedora |
Fedora 44, from Fedora's Atomic (bootc) desktop images | dracut dmsquash-live with Fedora's livesys scripts, with SELinux enforcing |
Both are 64-bit x86 only. Ubuntu ISOs come out smaller: about 2.7 to 2.9 GiB for the examples, against about 4 to 4.5 GiB for Fedora.
desktop |
Desktop | On Ubuntu | On Fedora | Built and boot-tested in CI |
|---|---|---|---|---|
gnome |
GNOME | Ubuntu's minimal GNOME desktop | the Fedora Silverblue 44 image | Ubuntu only |
kde |
KDE Plasma | Plasma with SDDM, Discover, Konsole and Dolphin | the Fedora Kinoite 44 image | Fedora only |
cinnamon |
Cinnamon | Cinnamon with LightDM, on X11 | Fedora's base Atomic image plus Cinnamon and LightDM, on X11 | both |
Every combination compiles and is covered by the unit tests. The two marked as not boot-tested (GNOME on Fedora, KDE on Ubuntu) should work but haven't been booted in CI yet. Every desktop gets a graphical software store with Flathub set up, so people can add more apps in the live session.
Apps are installed from Flathub, so the same id works on both bases. Use the id in the first column.
| App id | Name | Flathub ID |
|---|---|---|
firefox |
Firefox | org.mozilla.firefox |
chromium |
Chromium | org.chromium.Chromium |
thunderbird |
Thunderbird | org.mozilla.Thunderbird |
libreoffice |
LibreOffice | org.libreoffice.LibreOffice |
vlc |
VLC | org.videolan.VLC |
gimp |
GIMP | org.gimp.GIMP |
inkscape |
Inkscape | org.inkscape.Inkscape |
krita |
Krita | org.kde.krita |
blender |
Blender | org.blender.Blender |
audacity |
Audacity | org.audacityteam.Audacity |
obs-studio |
OBS Studio | com.obsproject.Studio |
kdenlive |
Kdenlive | org.kde.kdenlive |
vscode |
Visual Studio Code | com.visualstudio.code |
steam |
Steam | com.valvesoftware.Steam |
lutris |
Lutris | net.lutris.Lutris |
heroic |
Heroic Games Launcher | com.heroicgameslauncher.hgl |
discord |
Discord | com.discordapp.Discord |
gcompris |
GCompris | org.kde.gcompris |
tuxpaint |
Tux Paint | org.tuxpaint.Tuxpaint |
flatseal |
Flatseal | com.github.tchx84.Flatseal |
Each app makes the ISO bigger, because its Flatpak runtime comes too. Apps that share a runtime (most KDE apps, for example) cost less together. Some apps, such as Steam, are useful only on real hardware with a network connection.
The list lives in linuxlive/data/catalog.yaml. To add an app, add a line under apps: with a short id, its name and its Flathub ID, then run python scripts/check_catalog.py (it needs a network connection) to confirm the ID exists on Flathub. Pull requests for new apps are welcome.
| Field | Required | What it is | Rules |
|---|---|---|---|
wallpaper |
yes | The desktop background. | PNG or JPEG, at least 1280×720 pixels. |
logo |
yes | Your logo on the boot splash, as the system logo that About screens read from /etc/os-release, on GNOME's login screen, and as the KDE application launcher and the Cinnamon menu button. |
A square PNG of exactly 128, 256 or 512 pixels. |
accent_color |
no | The highlight colour. KDE uses it exactly. GNOME picks the nearest of its built-in accent colours. Cinnamon has no accent setting, so it is ignored there. On Ubuntu it also colours the text-mode boot screen. |
#rrggbb, for example "#e0661f". Put it in quotes: YAML treats an unquoted # as the start of a comment. |
Paths are relative to the spec file. If the spec is examples/mydistro.yaml, then assets/mydistro/logo.png means examples/assets/mydistro/logo.png.
For a build on your own machine, any image that meets the rules above works. For a build in GitHub Actions, the wallpaper must also pass CI's screenshot check, which confirms the wallpaper really appears on the booted desktop. The check finds the wallpaper's main colour and then looks for it in screenshots of the running system. So the wallpaper must be:
- a PNG, not a JPEG;
- 8-bit and non-interlaced, which is what most editors save by default. Avoid "16 bits per channel" and "interlaced" or "progressive" options;
- colourful, with one dominant colour. The check ignores greys, near-whites and near-blacks, and takes the most common remaining hue. That colour, give or take a little, must then fill at least a quarter of a screenshot. Large areas of one colour (grass, sky, sea, a bold gradient) pass. A mostly black, white or grey wallpaper fails with "the wallpaper has no dominant colour to look for", and a busy photo where no colour dominates may fail too.
If the "Verify" step fails on the wallpaper, make it more strongly one colour and run the build again.
For the logo, a simple shape with a transparent background reads best at small sizes. Use 256 or 512 pixels for a sharp boot splash.
| Place | What you see |
|---|---|
| Boot menu |
Try <name> <version> and Try <name> <version> (safe graphics)
|
| Boot splash | Your logo with a spinner |
| Desktop and lock screen | Your wallpaper, and your accent colour on KDE and GNOME |
About screen and /etc/os-release
|
Your name, version, id and logo. PRETTY_NAME reads like Lawn Darts Linux 0.1 (based on Ubuntu 26.04.1 LTS). |
| Disk label of the DVD or USB stick | Your name and version in capitals, for example LAWN_DARTS_LINUX_0_1
|
| GNOME login screen | Your logo |
| KDE application launcher, Cinnamon menu button | Your logo |
Ubuntu's and Fedora's own logos are their trademarks, so linuxlive replaces them: Fedora builds use Fedora's neutral generic-logos package, and on Ubuntu the dock's Show Apps button uses GNOME's grid icon instead of the Ubuntu logo. Text consoles greet with your distro's name too. The PRETTY_NAME still says which base your distro is built on.
Since linuxlive v0.4.0 a spec can add things of its own. All four are optional; the Workshop's Extras step on linuxlive.com writes them for you.
packages: # from the base's own archive (apt on Ubuntu, dnf on Fedora)
- htop
files: # copied into the system, under /etc or /usr
- src: files/motd
dest: /etc/motd
mode: "0644" # optional; "0755" for a program
scripts:
build: # as root while the image is built, with the network
- scripts/build.sh
first-boot: # once, as root, on an installed computer before its login screen
- scripts/first-boot.sh
first-login: # once for each user at their first login, as that user
- scripts/first-login.sh
repos: # GitHub code, fetched at an exact commit
- url: https://github.com/owner/name
commit: 3f2a9c1e5b7d4e8a9c217d4e5f6a8b904c1d2e3f
run: make install PREFIX=/usr| Field | When it runs | Rules |
|---|---|---|
packages |
In the build, after the desktop and before your files | Names from Ubuntu's or Fedora's archive; they differ between the two (vim on Ubuntu, vim-enhanced on Fedora). At most 100. |
files |
Copied with linuxlive's own files | Under /etc or /usr, and not over a file linuxlive writes (/etc/linuxlive, /usr/lib/linuxlive, /usr/libexec/linuxlive, os-release). On Fedora, not /usr/local (it lives in /var there). The source must be inside the spec's folder. 20 MB in all. |
scripts.build |
In the build, after repos, before linuxlive's own last step |
Root, network on, working directory the scripts' folder. The distro's settings are in LL_NAME, LL_ID, LL_VERSION, LL_BASE and LL_DESKTOP. A failing script fails the build. |
repos |
In the build, before scripts.build
|
A public GitHub repository and a full 40-character commit. run runs as root in the unpacked source. Each repo and commit is added to the ISO's package list, so its source can be found. |
scripts.first-boot |
Once, on an installed computer, before its login screen, with the network | Root. A failing script is logged in /var/lib/linuxlive/first-boot.log; the others still run. Not in the live session. |
scripts.first-login |
Once for each user, at their first login; in the live session, at every start | That user, in their home folder. Logged in ~/.local/state/linuxlive/first-login.log. |
Scripts run in the order listed. Start each with #!/bin/sh (or #!/bin/bash) and set -eu. On Fedora the system is an image: a build script can write to /usr and /etc, but anything it puts in /var, /home or /usr/local is not kept.
linuxlive's own branding, installer and boot checks run after your build steps, and the CI checks each extra: every package is installed, every file has your contents, and every script ran and succeeded, in the live session and on the installed disk.
Scripts and repos run as root on every computer that installs your distro. Read them through before you share it. The Library reviews every line before it publishes a distro with code of its own, and linuxlive.com's coming Build it for me takes specs without scripts or repos.
| Message | Fix |
|---|---|
version: 0.1 is not of type 'string' |
Quote the version: version: "0.1". |
branding/accent_color: None is not of type 'string' |
Quote the colour: accent_color: "#2e7d32". |
apps: unknown app 'gimp2' (known apps: ...) |
Use an id from the Apps table. |
Additional properties are not allowed ('desktops' was unexpected) |
That field name is misspelled or doesn't exist. |
branding/logo: 'logo.png' is 1920x1080; it must be square, 128, 256, 512 pixels |
Resize the logo to a square of 128, 256 or 512 pixels. |
branding/wallpaper: 'wallpaper.png' is 256x256; it must be at least 1280x720 |
Use a bigger wallpaper. |
branding/wallpaper: file 'wallpaper.png' not found |
The path is relative to the spec file. Check the folder and the spelling, including upper and lower case. |
id: could not derive a valid id from name ... |
Add an id: line yourself. |
Next: build it with Building with GitHub Actions or Building Locally.
Make a distro
Build it
Use it
Project