Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ All `just` targets run BuildStream inside the FSDK `bst2` container via `just bs
| Task | Skill |
|---|---|
| Build or debug the installer / DDI | [`docs/skills/ddi-installer.md`](docs/skills/ddi-installer.md), [`docs/skills/ddi-installer-build.md`](docs/skills/ddi-installer-build.md) |
| PXE/netboot installer deployment | [`docs/skills/pxe-netboot.md`](docs/skills/pxe-netboot.md) |
| Factory role, k3s sysext rationale, lab integration | [`docs/skills/factory-integration.md`](docs/skills/factory-integration.md) |
| Work with `systemd-sysext` / `systemd-confext` | [`docs/skills/systemd-sysext-extensions.md`](docs/skills/systemd-sysext-extensions.md) |
| Build or ship the k3s sysext | [`docs/skills/k3s-sysext.md`](docs/skills/k3s-sysext.md), [`docs/skills/k3s-sysext-ops.md`](docs/skills/k3s-sysext-ops.md) |
Expand Down
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ cluster-build REF="main":
export-installer: build-installer
rm -rf dist/installer-checkout
mkdir -p dist dist/installer-checkout
rm -f dist/bluefin-server-installer-*.raw.zst dist/bluefin-server-*.efi dist/SHA256SUMS
rm -f dist/bluefin-server-installer-*.raw.zst dist/bluefin-server-*.efi dist/bluefin-server-pxe-* dist/SHA256SUMS
just bst artifact checkout oci/bluefin-server-installer.bst --directory /src/dist/installer-checkout
mv dist/installer-checkout/* dist/
rm -rf dist/installer-checkout
Expand Down
12 changes: 6 additions & 6 deletions docs/skills/ddi-installer.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ metadata:
- OCI-only image work (no installer involvement).
- Bootc-specific changes.
- Desktop or nspawn machine image work.
- Adding a network-pull installer — the design is offline; the DDI is embedded as
a data partition.
- PXE/netboot deployment — see [pxe-netboot.md](pxe-netboot.md).

## Architecture

The installer is offline, self-contained, and systemd-native. The OS DDI payload
(`bluefin-server-ddi.bst`) is embedded as a data partition on the installer
media at build time. No network access is required at install time.
The default installer is offline, self-contained, and systemd-native. The OS DDI
payload (`bluefin-server-ddi.bst`) is embedded as a data partition on installer
media at build time. PXE boot can opt into fetching the same DDI over HTTP(S);
see [pxe-netboot.md](pxe-netboot.md).

The installer UI is systemd's built-in `systemd-sysinstall` which provides a
terminal-based interactive installation that:
Expand Down Expand Up @@ -142,7 +142,7 @@ For the detailed build/export/flash/release workflow, see
| "A bash script is simpler." | A bash script cannot run the systemd-native interactive installer TUI. Use `systemd-sysinstall`. |
| "Use knuckle instead." | knuckle is deprecated in favor of native `systemd-sysinstall` (systemd 261+). |
| "Hardcode `root=/dev/vda2` for QEMU." | Bare metal has different device names. Always use PARTUUID. |
| "Pull the DDI from the network at install time." | Network failures = broken installs. The DDI is embedded in the installer media. |
| "Pull the DDI from the network at install time." | Only PXE boot enables this; failures stop before target-disk installation. |
| "Put the DDI in the initrd cpio." | The DDI is 2 GiB+. The initrd cpio step must run before the DDI is placed in `/layer`. |
| "Store the DDI in the ESP (FAT32)." | FAT32 has a 4 GiB per-file limit. Use a separate XFS partition. |
| "Add an 8 GiB minimum size floor to the DDI." | The rootfs is immutable. It never grows in-place. Content + overhead is enough. |
Expand Down
1 change: 1 addition & 0 deletions docs/skills/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This is the lazy-load routing table for agent skills. Keep this file in memory w
| [`ci-tooling.md`](ci-tooling.md) | GitHub Actions, workflow SHA pinning, CI conventions | CI conventions and release pipeline rules. |
| [`ddi-installer-build.md`](ddi-installer-build.md) | Building the installer or DDI on the cluster | Cluster build pipeline and local installer/DDI build. |
| [`ddi-installer.md`](ddi-installer.md) | Installer boot flow, `systemd-sysinstall`, `systemd-repart` | High-level DDI install architecture and local smoke test. |
| [`pxe-netboot.md`](pxe-netboot.md) | Network booting the installer | PXE artifacts, kernel command line, and network DDI fetch. |
| [`factory-integration.md`](factory-integration.md) | Lab integration, boot-test workflow, factory role | How Bluefin Server is consumed by the CI lab. |
| [`gap-analysis-distros.md`](gap-analysis-distros.md) | Comparing Bluefin Server to other server OSes | Source-verified comparison to Ubuntu, Talos, Flatcar, FCOS. |
| [`k3s-sysext-ops.md`](k3s-sysext-ops.md) | Building the k3s sysext | BuildStream element and publish steps for the k3s sysext. |
Expand Down
33 changes: 33 additions & 0 deletions docs/skills/pxe-netboot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: pxe-netboot
description: Use when provisioning Bluefin Server over PXE or iPXE.
metadata:
type: reference
status: stable
---
# PXE/netboot installer

Releases include standalone `bluefin-server-pxe-vmlinuz-<version>` and
`bluefin-server-pxe-initrd-<version>.cpio.gz` assets. The matching DDI
(`bluefin-server-ddi-<version>.raw.zst`) is fetched only when `inst.ddi_url`
is supplied; normal installer media remains offline and uses its embedded DDI.

Example iPXE configuration:

```ipxe
#!ipxe
set base http://pxe.example/data
kernel ${base}/bluefin-server-pxe-vmlinuz-<version> systemd.unit=system-install.target console=tty0 console=ttyS0,115200 rw unattended inst.ddi_url=${base}/bluefin-server-ddi-<version>.raw.zst inst.ddi_sha256=<sha256> inst.target_disk=/dev/sda
initrd ${base}/bluefin-server-pxe-initrd-<version>.cpio.gz
boot
```

`inst.ddi_url` must use HTTP(S). The compressed DDI is unpacked into the
installer runtime; `inst.ddi_sha256` is recommended and is checked before
installation. `inst.target_disk` selects a whole-disk block device. Without
it, unattended mode selects the first writable disk, as with USB media.

Fetch or checksum failures stop installation before the target disk is
modified. Mirror release assets and verify them against the signed
`SHA256SUMS` manifest. Provide DHCP and DNS (or a reachable IP URL), and use
the serial console arguments above for headless systems.
7 changes: 5 additions & 2 deletions elements/installer/installer-stack.bst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ description: |
bluefin-server-installer.bst. This keeps the toolchain minimal and removes
the dracut dependency chain.

The DDI payload is embedded in the installer media (offline install).
No network access required at install time.
The DDI payload is embedded in installer media by default. PXE boot can
opt into fetching it over HTTP(S).

depends:
# Base runtime
Expand All @@ -27,6 +27,9 @@ depends:
- freedesktop-sdk.bst:components/cryptsetup.bst
- freedesktop-sdk.bst:components/xfsprogs.bst
- freedesktop-sdk.bst:components/dosfstools.bst
# PXE installer support: fetch and unpack an opt-in network DDI.
- freedesktop-sdk.bst:components/curl.bst
- freedesktop-sdk.bst:components/zstd.bst

# Kernel (installer boots from this; vmlinuz used as the UKI --linux= arg)
- freedesktop-sdk.bst:components/linux.bst
Expand Down
74 changes: 58 additions & 16 deletions elements/oci/bluefin-server-installer.bst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ description: |

Output: bluefin-server-installer-<ver>.raw.zst + SHA256SUMS

Offline installer: the OS DDI payload is embedded as a data partition on
the installer media. No network required at install time.
Default offline installer: the OS DDI payload is embedded as a data partition
on the installer media. PXE boot can opt into fetching the DDI over HTTP(S).

Initrd approach: dracut-free, cpio-native.
The installer-stack rootfs is packed directly into a cpio.zst initrd using
Expand Down Expand Up @@ -127,26 +127,64 @@ config:
#!/bin/bash
set -euo pipefail

# Auto-detect target disk for unattended install (any raw disk of type "disk" that is not the installer, is not read-only, and has size > 0)
TARGET_DISK=""
CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)"
get_cmdline_value() {
local key="$1" arg
for arg in ${CMDLINE}; do
case "${arg}" in
"${key}"=*) printf '%s' "${arg#*=}"; return 0 ;;
esac
done
return 1
}

INSTALLER_PART="$(readlink -f /dev/disk/by-partlabel/bluefin-installer-data 2>/dev/null || true)"
DDI_URL="$(get_cmdline_value inst.ddi_url || true)"
DDI_SHA256="$(get_cmdline_value inst.ddi_sha256 || true)"
if [ -n "${DDI_URL}" ]; then
case "${DDI_URL}" in
http://*|https://*) ;;
*) echo "ERROR: inst.ddi_url must use http:// or https://" >&2; exit 1 ;;
esac
DDI_COMPRESSED="/run/installer/bluefin-server-ddi.raw.zst"
DDI_RAW="/run/installer/bluefin-server-ddi.raw"
echo "==> Fetching DDI from ${DDI_URL}..."
mkdir -p /run/installer /dev/disk/by-partlabel
curl --fail --location --retry 3 --output "${DDI_COMPRESSED}" "${DDI_URL}"
if [ -n "${DDI_SHA256}" ]; then
printf '%s %s\n' "${DDI_SHA256}" "${DDI_COMPRESSED}" | sha256sum --check --status -
echo "==> DDI checksum verified."
fi
zstd --decompress --stdout "${DDI_COMPRESSED}" > "${DDI_RAW}"
rm -f "${DDI_COMPRESSED}"
ln -sfn "${DDI_RAW}" /dev/disk/by-partlabel/bluefin-installer-data
INSTALLER_PART=""
fi

# Auto-detect target disk for unattended install (any raw disk of type "disk" that is not the installer, is not read-only, and has size > 0)
TARGET_DISK="$(get_cmdline_value inst.target_disk || true)"
if [ -n "${TARGET_DISK}" ] && { [ ! -b "${TARGET_DISK}" ] || [ "$(lsblk -ndo TYPE "${TARGET_DISK}")" != "disk" ] || [ "$(lsblk -ndo RO "${TARGET_DISK}")" != "0" ]; }; then
echo "ERROR: inst.target_disk is not a valid whole-disk block device: ${TARGET_DISK}" >&2
exit 1
fi

while read -r name type ro size; do
[ "${type}" = "disk" ] || continue
[ "${ro}" = "0" ] || continue
[ "${size}" -gt 0 ] || continue
if [ -z "${TARGET_DISK}" ]; then
while read -r name type ro size; do
[ "${type}" = "disk" ] || continue
[ "${ro}" = "0" ] || continue
[ "${size}" -gt 0 ] || continue

# Skip the disk containing the installer partition
if [ -n "${INSTALLER_PART}" ] && [[ "${INSTALLER_PART}" == "${name}"* ]]; then
continue
fi
# Skip the disk containing the installer partition
if [ -n "${INSTALLER_PART}" ] && [[ "${INSTALLER_PART}" == "${name}"* ]]; then
continue
fi

TARGET_DISK="${name}"
break
done < <(lsblk -p -d -n -o NAME,TYPE,RO,SIZE -b)
TARGET_DISK="${name}"
break
done < <(lsblk -p -d -n -o NAME,TYPE,RO,SIZE -b)
fi

# Check for unattended in kernel command line
CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)"
if [[ " ${CMDLINE} " == *" unattended "* ]]; then
echo "==> Running in UNATTENDED mode..."
if [ -n "${TARGET_DISK}" ]; then
Expand Down Expand Up @@ -316,7 +354,11 @@ config:

# Also export the target UKI (.efi) for GitHub Releases and systemd-sysupdate
cp /layer/usr/lib/bluefin-server/bluefin-server.efi "bluefin-server-%{installer-version}.efi"
cp /layer/boot/vmlinuz "bluefin-server-pxe-vmlinuz-%{installer-version}"
cp /installer.cpio.gz "bluefin-server-pxe-initrd-%{installer-version}.cpio.gz"

sha256sum --binary "${FNAME}.zst" > SHA256SUMS
sha256sum --binary "bluefin-server-%{installer-version}.efi" >> SHA256SUMS
sha256sum --binary "bluefin-server-pxe-vmlinuz-%{installer-version}" >> SHA256SUMS
sha256sum --binary "bluefin-server-pxe-initrd-%{installer-version}.cpio.gz" >> SHA256SUMS
ls -lh