Walkthrough: ramboot mode¶
boot_mode=ramboot is the in-place sibling of bty’s flash boot modes.
The target machine never touches its local disk: it chains a slim
initrd that connects to an NBD multiplexer (nbdmux), mounts the
catalog image’s root partition read-only, overlays a tmpfs for writes,
and pivot_roots into the catalog image’s userspace. The overlay
vanishes on reboot.
Useful for CI runs where flashing is overkill, preview workflows (“just boot this for a minute and see”), or lab boxes that want to cycle through a handful of images per day without burning through SSD writes.
Linux-only target image (the initramfs uses overlayfs, which is a Linux kernel feature). The bty-web side runs anywhere; the operator’s UI is unchanged.
What you need before starting¶
You already have a working bty-web deploy with at least one catalog
entry bound to a machine. The bty-lab deploy defaults bring up the
extra moving parts automatically (since v0.62):
The
nbdmuxsidecar incompose.ymllistens on port 8082 (HTTP control plane) and 10809 (NBD listener).bty-web and nbdmux share
${BTY_HOST_DATA_DIR}/nbdmux/images/. bty-web decompresses catalog images there; nbdmux serves the same bytes over NBD without a copy.The Settings page gains a “Ramboot” card under the existing Display card.
You also need the ramboot-init netboot artifact for the
target’s architecture in ${BTY_BOOT_DIR} (or wherever bty-web
serves /boot/ from). It ships alongside the existing netboot-pc
trio on every bty release; the “Fetch artifacts” button on
/ui/netboot grabs all of them in one go.
If your deploy predates v0.62, run uvx bty-lab init --force . to
regenerate compose.yml with the new sidecar, then
podman compose pull && podman compose up -d.
Configure the bytes path¶
bty-lab init writes [nbdmux] url = "http://<host>:8082" into the
generated bty.toml for you (since v0.64.1; same pattern as
withcache), so the bytes path is already configured when the stack
comes up. The Settings -> Ramboot card is an override surface:
open it only when you run nbdmux somewhere other than the in-stack
sidecar (a separate LAN box, a different port), or when you want to
change the default overlay size.
The card carries two fields:
nbdmux URL: leave blank to inherit the deploy default; set to
http://<host>:8082(or wherever your nbdmux lives) to override. Saving an empty value AND clearing the[nbdmux] urlline inbty.tomldisables the boot mode globally; the iPXE chain then falls back to bty-tui with a reason on the events feed.Overlay size:
10Gis the default. Sets the tmpfs cap for the in-target write overlay. Make this bigger if your workload writes more than a few GB of logs / scratch state; cap it lower if you need to leave RAM for the running OS. The Linux mount layer parses the value at boot time; an invalid suffix surfaces on the serial console as the initramfs panics, not at form submit.
The values resolve as Settings override first, then $BTY_NBDMUX_URL
env, then the [nbdmux] url field in bty.toml, then unset.
Warm an image in nbdmux first¶
Since v0.65.0 the warming pipeline (fetch + decompress + register the export) lives in nbdmux, not bty-web. Operators populate nbdmux directly; bty-web only validates that a ref is ready before letting the operator bind a machine to it.
Open the nbdmux dashboard at http://<host>:8082/ and POST a warm
request. The simplest path is from a shell on the host:
curl -X POST http://<host>:8082/exports \
-H "Content-Type: application/json" \
-d '{
"name": "<bty_image_ref>",
"src_url": "<catalog-entry-src>",
"readonly": true
}'
<bty_image_ref> is the catalog entry’s ref (visible in bty-web’s
catalog table or /images). <catalog-entry-src> is the upstream
URL (oras://... or https://.../...img.gz); copy it from the same
row. nbdmux routes the fetch through the configured
NBDMUX_WITHCACHE_URL (set by bty-lab init to the in-stack
http://withcache:8081), so the bytes flow through the same LAN
cache as the flash path.
The nbdmux dashboard shows the state machine progressing
queued -> fetching -> decompressing -> ready with a percent-
complete bar. Once ready the export is live on nbdmux’s NBD
listener (port 10809 by default).
Bind a machine to ramboot¶
With the ref ready in nbdmux, open the machine’s edit page in bty-web and:
Pick the same catalog entry under “Image binding”.
Set “Boot mode” to ramboot.
Save.
bty-web validates the binding at save time: it queries nbdmux’s
/exports, finds the ref at status='ready', and persists the
machine record. If the ref isn’t in nbdmux’s ready list (or nbdmux
is unreachable), the form rejects with a 422 plus an explanatory
message; warm the export in nbdmux first, then re-save the machine.
The machine row’s small pill under the ramboot badge mirrors nbdmux’s status. The pill is read live from nbdmux’s API on each page render, so it reflects the current daemon state with no local cache table.
Boot the target¶
PXE-boot the target the same way you’d flash one: DHCP options
point at bty-web, the firmware fetches the iPXE script from
/pxe/<mac>, and bty-web responds with the ipxe_ramboot.j2
template iff:
boot_mode=rambooton the machine row, ANDnbdmux URL is configured, AND
the bound ref’s nbdmux export is reported as
status='ready'by the nbdmux daemon (bty polls vianbdmux_client.list_exportsat plan-emit time; nbdmux owns the warm pipeline end-to-end since v0.65.0, so bty no longer keeps its ownramboot_cachetable).
If any gate is open, the chain falls back to bty-tui so the
operator sees the wizard rather than the box panicking in the
initramfs. The reason lands on /ui/events as the
netboot.pxe.offered event’s details.
When all gates close, the target chainloads:
kernel ${bty-base}/boot/bty-ramboot-init-x86_64-v<version>.vmlinuz
boot=ramboot
bty.nbd=tcp://<nbdmux-host>:10809
bty.image=<ref>
bty.overlay_size=10G
bty.server=${bty-base}
bty.mac=<mac>
initrd ${bty-base}/boot/bty-ramboot-init-x86_64-v<version>.initrd
boot
The initramfs (built into bty-media’s ramboot-init variant) does
the rest: modprobe nbd plus overlay, nbd-client to the nbdmux
endpoint, partprobe /dev/nbd0, mount the largest partition (or
the bty.root_part=<devnode> override if you set one) read-only,
mount a tmpfs sized to bty.overlay_size, overlayfs the two at
/root, POST status=ramboot.up to bty.server (best-effort), and
pivot_root + exec /sbin/init.
From the catalog image’s perspective, nothing is unusual: it boots its own /sbin/init, runs its own services, and the root filesystem is read-only with copy-on-write semantics for changed blocks.
When something goes wrong¶
The initramfs panics with a descriptive message on each failure step, visible on the target’s serial console / tty:
ramboot: nbd-client failed to connect to ...– network reach to the nbdmux host:10809 is broken, or nbdmux isn’t running.ramboot: could not pick a root partition on /dev/nbd0– the catalog image has no detected partition (or none look like a root). Override withbty.root_part=/dev/nbd0pNon the iPXE cmdline.ramboot: failed to mount /dev/nbd0pN– the picked partition’s filesystem driver isn’t in the initramfs (only ext4 / xfs / btrfs are pre-loaded). Add the needed module tobty-media/live-build/config/includes.chroot/etc/initramfs-tools/hooks/bty-rambootand bake a new ramboot-init release.
The initramfs also best-effort POSTs status=ramboot.<step>_failed
to bty.server before panicking, so the failure surfaces on
/ui/events for the operator timeline even if you don’t have a
serial console attached.
If nbdmux’s warmer itself fails (upstream URL 404, decompress error,
withcache unreachable), the export’s status moves to failed on
nbdmux’s dashboard with the error message in the row’s error
field. The bty-side machine row mirrors the failed pill. Re-POST
to nbdmux’s /exports (same payload) to retry; the row will reset
to queued and the worker restarts the pipeline.
When to use ramboot, when to use flash¶
Need |
Pick |
|---|---|
CI runs a job, throws away the result |
ramboot |
Operator wants the disk reimaged for a tenant |
flash-once |
Per-job CI cadence with disk persistence |
flash-always |
“Just run this in RAM for an hour” |
ramboot |
Boot the existing disk, change nothing |
ipxe-exit (iPXE sanboot verb on BIOS / firmware exit on UEFI) |
ramboot does not write to disk; flash does. Pick on the durability question, not the speed question. (The first ramboot of a given ref takes the same wall-clock to pre-warm as a flash takes to write; the second through Nth ramboot of the same ref is fast because nbdmux is already serving.)