This document outlines how to build, test, and ship the Holo Node operating system image. The image is a customised Fedora CoreOS (FCOS) build that boots directly into the Holo node stack.
The image is built using Butane (which compiles a human-readable YAML config to Ignition JSON) and coreos-installer to produce a bootable ISO.
The core files for the build process are organized as follows:
-
config/node.bu: The Butane YAML file. This is the human-editable configuration. -
scripts/build.sh: The primary bash script that executes the build. -
.github/workflows/build.yml: The GitHub Actions pipeline for automated builds.
To build the ISO locally, you need a few dependencies installed on your machine.
-
Linux: ```bash
curl -L https://github.com/coreos/butane/releases/latest/download/butane-x86_64-unknown-linux-gnu -o /usr/local/bin/butane && chmod +x /usr/local/bin/butane
-
macOS: ```bash
brew install butane
-
Linux (via cargo): ```bash
cargo install coreos-installer
-
Fedora/RHEL: ```bash
sudo dnf install coreos-installer
-
Debian/Ubuntu:
sudo apt install curl jq -
macOS:
brew install curl jq
To build the standard x86_64 ISO, run the following from the repository root:
Bash
chmod +x scripts/build.sh
./scripts/build.sh
What the script does:
-
Compiles
config/node.buintoignition.jsonusing Butane. -
Downloads the latest stable FCOS ISO.
-
Embeds the Ignition config into the ISO using
coreos-installer. -
Outputs the final
.isofile into the project root.
To build for ARM architectures, pass the architecture variable:
Bash
ARCH=aarch64 ./scripts/build.sh
The config/node.bu file defines exactly how the node is configured on its very first boot.
The holo user is the only SSH-accessible account. No SSH keys are baked in by default. If you need a permanent recovery key that the UI cannot remove, add it to the ssh_authorized_keys section of this file.
The node-setup.sh first-boot script is inlined directly in node.bu. We use a bash script instead of a baked-in binary to stay under the 262KB initramfs size limit imposed by the live ISO. Its only job is to fetch node-manager from GitHub Releases.
Unit
Type
Enabled
Description
node-setup.service
oneshot
yes
First-boot download of node-manager.
node-manager.service
simple
yes
Permanent management server on port 8080.
podman-auto-update.timer
timer
yes
Nightly container image refresh.
You can validate your Butane configuration without running a full build:
Bash
butane --strict --check config/node.bu
To run a full end-to-end test in a virtual machine (using QEMU):
Bash
# Create a virtual disk
qemu-img create -f qcow2 test-disk.img 20G
# Boot the generated ISO
qemu-system-x86_64 \
-m 4096 \
-cpu host \
-enable-kvm \
-drive file=holo-node-x86_64.iso,format=raw,if=ide,media=cdrom \
-drive file=test-disk.img,format=qcow2,if=virtio \
-boot d \
-nographic \
-serial stdio \
-net user,hostfwd=tcp::8080-:8080
The .github/workflows/build.yml workflow automatically builds ISOs on pushes to main and on version tags.
To publish a release ISO as a GitHub Release artifact, simply create and push a version tag:
Bash
git tag v1.2.0
git push origin v1.2.0
When to ship a new ISO:
You only need to rebuild and ship a new ISO when the FCOS base image requires significant security patches for new hardware installs, or if the node.bu config changes. Running nodes update themselves via rpm-ostree and GitHub API polling, so they do not require new ISOs.