Skip to content
All projects

trapp-os

A customised Kairos image for provisioning a k3s control-plane node in my lab, with unattended installation, node configuration and custom boot and login screens.

Running in my lab3 min readView source
  • Kairos
  • k3s
  • Immutable Linux
  • Edge

trapp-os is my Kairos image. Ubuntu-based Kairos, k3s, a boot menu, a login banner and one node’s configuration, built into an ISO. Boot it on an empty VM and a control plane installs itself.

Why I built it

My Linux experience has mostly involved configuring running systems over SSH. This project explores a different workflow: defining the node through an installation image and testing the full process from ISO build to a working Kubernetes control plane.

Kairos does the heavy lifting. It’s the OS and the installer. My part is the configuration, the build script and the customisations.

Image contents and build process

AuroraBoot builds it, pinned to Kairos v3.6.0 with k3s v1.33.5 on Ubuntu 24.04.

experiments/kairos/
├── build.sh
├── image/
│   ├── boot-menu.cfg
│   └── login-banner.sh
├── nodes/
│   └── control-plane.yaml
└── artifacts/

image/ holds the shared branding. nodes/ is per-machine: hostname, Kubernetes role. The build script drops each file wherever the installer expects to find it.

Keeping them apart means I can change what a node is without touching the banner that every machine gets.

Unattended installation and boot order

The control-plane image installs unattended, which puts all the weight on boot order. If the VM keeps booting the ISO after installation, the installer runs again. And again.

Disk first fixes it. An empty VM has nothing on disk to boot, so the firmware falls back to the media; once there’s an install on disk, it boots that instead.

There’s also a manual entry in the boot menu for a live session. When an install didn’t start and I couldn’t see why, /proc/cmdline is what told me which boot options had actually been used.

Testing behaviour after reboot

The install finishing is not the test. In my lab tests with Kairos v3.7.2, some installs completed cleanly and then came back wrong after a reboot: the hostname wasn’t what I had set, k3s didn’t start, and persistent storage was missing. I haven’t confirmed the cause, and it may be specific to my configuration, but the build pins v3.6.0 because that version behaved consistently in the same tests.

So reboot became part of the experiment rather than the thing I did at the end. Does the node still know who it is, is k3s up, is the data still there.

Applying changes through the image

The banner, the boot menu and the node configuration all arrive through the installation image. Editing the source files does nothing to a VM that is already running. In this project’s workflow, I apply a change by rebuilding the ISO and reinstalling the test node. Kairos also supports upgrading nodes in place, but I haven’t set that up for this project yet.

That’s the main shift for me. On a traditional server, the running system is the thing you edit. Here, the image is the thing you edit, and the running system follows from it. It also means deciding up front what needs to survive a reinstall.