SUSE's Edge Image Builder (EIB) skips config management entirely: describe a
cluster in one YAML definition, and it hands back a self-installing ISO that partitions the
disk, installs the OS, and bootstraps RKE2 on first boot. No PXE server, no Ansible run, no
manual kubeadm join. I started from a reference definition for a full SUSE
Observability stack — five nodes, three servers and two agents, with cert-manager,
Longhorn, MetalLB, ingress-nginx, and the observability charts themselves all baked in. Before
touching any of that on real hardware, I stripped it down to see if the basic mechanism worked
at all.
One Build, Not Two
The whole exercise was one build, validated once in a disposable VM, then written straight to the five physical boxes — nothing rebuilt for hardware. The VM's only job was answering “does this image even install and boot” somewhere free to fail, before trusting the same file to real disks.
Building the ISO
EIB ships as a container, not a package — building the ISO is a podman run
against it with the working directory bind-mounted in. This all assumes a SL Micro 6.x host with
podman installed; having KVM on the same box makes the VM-test phase practical too.
podman run --rm -it \
--privileged \
-v "${SCRIPT_DIR}":/eib \
"${EIB_IMAGE}" \
build --definition-file "${DEFINITION_FILE}"
--privileged is needed because EIB loop-mounts and manipulates the base ISO and the raw disk image it builds on top of it.
The Installer That Wouldn't Say Anything
First boot in the test VM produced nothing on the console — not an error, just silence.
The inherited kernel args included quiet, suppressing all boot output including
whatever the kiwi imaging step was doing to the disk. Getting a usable picture took:
console=ttyS0,115200n8— routes kernel output to the serial consolenomodeset— no framebuffer, keeps output on the text consoletextmode=1— forces the SUSE installer into text moderd.kiwi.debug— verbose logging for kiwi's disk-imaging phase, where the real failures were hiding
NMState Doesn't Know It's a VM
The network config was written against real hardware and named the interface em1
— a biosdevname-style name. KVM's virtio NICs don't get biosdevname names; they come up as
predictable enp* names instead, so the interface never came up in the VM until
renamed to match.
Two Bugs, Baked Permanently into the Image
Two first-boot scripts fix things that only show up after watching a boot fail once:
- growfs fix — works around a known SUSE bug where the root filesystem doesn't grow to fill the disk. Resolves the root device to its parent disk/partition, then runs
growpartandsystemd-growfs. - tmpfs over /etc/cni — mounts a small tmpfs so Calico's CNI config starts clean on every boot instead of accumulating stale state.
Five Real Boxes Called Lenny
The cluster's called Lenny because the machines are Lenovo M910q
tiny-form-factor PCs — the name is just what's left of “Lenovo” once you're
naming five of them after it. RKE2 v1.33.3+rke2r1 with Calico, three servers and two
agents, an apiVIP fronting the HA control plane:
| Host | Role | IP |
|---|---|---|
lenny1.shed.local | server (initializer) | 172.16.0.11 |
lenny2.shed.local | server | 172.16.0.12 |
lenny3.shed.local | server | 172.16.0.13 |
lenny4.shed.local | agent | 172.16.0.14 |
lenny5.shed.local | agent | 172.16.0.15 |
Trusting Real Disks Before Real Data Goes on Them
A disposable VM's disk doesn't have a failure mode worth testing for; five physical drives about to hold real data do. Each one gets a SMART extended self-test, polled every 30 seconds until it completes, then checked for reallocated sectors, pending sectors, and uncorrectable offline sectors.
Completing the Definition
A few real gaps got closed after the fact. cert-manager, Longhorn, and MetalLB — all
stripped for the single-node test image — are back, verified against the real Helm repos
rather than assumed (a lesson learned the hard way: cert-manager and MetalLB were both wrongly
assumed to live in the same repo as Longhorn, and neither does). apiHost came back
alongside apiVIP, with a tls-san entry so clients connecting via the
VIP don't hit a TLS mismatch. And checking each physical node directly turned up a second disk
(/dev/nvme0n1, ~932G) on every box, dedicated to Longhorn storage, that nothing in
the definition had ever declared — now provisioned by a first-boot script that never
reformats a disk that already has a filesystem on it.
None of the individual pieces here were exotic. What mattered was the order: strip a six-chart, five-node definition down to the smallest thing that could possibly boot, prove the imaging mechanism itself worked in a VM where failure is free, then scale back up one real constraint at a time instead of debugging all of it, on real hardware, at once.