A new robot on the bench has an empty disk. It has no name and no operating system, so the fleet can’t reach it. Someone has to put an image on it, and every way of doing that by hand leaves the result depending on who stood at the machine.

In the field I see three answers. Someone types a hostname at an installer prompt. Someone keeps a user-data file per box. Someone builds a USB image per site and carries it over. In all three, the person at the machine decides, and nothing reviewable records it.

This series uses a fourth. The edge server already sits on the site’s network and already stores the images the robots boot from, as the reference architecture lays out. So the robot installs itself from it. This is the first day-zero article, and everything after it assumes robots arrive running the fleet’s image. It builds on connecting edge sites through NAT for what the edge server does once it has a site to serve.

A stylized, interactive animation of robots powering on and asking a server who they are. Click a robot, drag to orbit. The lab below runs the real flow.

What a blank robot can ask

A machine with an empty disk can ask one question. Its firmware finds nothing to boot, falls through to the network, and broadcasts DHCP. Whatever answers decides what happens next.

The series ships a Yocto image, so there’s no installer that asks questions, and no preseed file to answer. The image is the install. What the robot needs is a small program that writes a finished image to the disk. That program is a second, tiny Yocto image: a kernel and an initramfs holding bmaptool, efibootmgr and about ninety lines of shell. It runs entirely from memory.

The edge server hands it over with three ordinary services. dnsmasq answers DHCP and serves one file over TFTP, which is Debian’s snponly.efi build of iPXE. iPXE then asks DHCP again, gets told to fetch boot.ipxe over HTTP, and nginx serves that script along with the installer’s kernel and initramfs. The firmware’s own network stack only has to do TFTP. Everything large travels over HTTP, which is faster and easier to inspect.

What the installer does

The installer takes a DHCP lease, finds the disk, and runs bmaptool copy against the image on the edge server. The image is a wic.gz with a wic.bmap beside it. The bmap lists which blocks hold data and carries a checksum for each one, so the installer writes only the used blocks and detects corruption on the way. In the lab the 31 MB image went to the disk at about 30 MiB/s.

Then it fixes the firmware. It deletes the network boot entries, adds one entry for the disk’s EFI partition, prints the resulting boot order, and reboots. Every failure prints a reason on the serial console and stops. A half-installed robot that restarts into a loop would be much harder to find than one that says why it stopped.

The second start goes to the disk. The robot boots the fleet’s image: systemd-boot, a kernel, busybox, a DHCP client and an SSH server, and nothing else. It asks the edge server for an address, but it doesn’t need the edge server to boot. The image exists to prove the install, and the SSH server lets you look around afterwards.

the robotblank disk, no operating systemfirst startno bootable disk, so thefirmware tries the networksecond startboots the Yocto imagefrom its own diskthe edge serverplain files, no cluster neededdnsmasqDHCP and TFTPsnponly.efithe iPXE programnginxboot script, installer,kernel, wic.gz and wic.bmap1 DHCP: who is there?2 TFTP: the iPXE program3 HTTP: boot.ipxe, installer, kernel4 HTTP: the image, written to diskafter step 4 the installer restarts the robot; the network is no longer used
blog.bensoussan.de
Figure 1. A blank robot falls through to the network and asks four things in turn: DHCP, the iPXE program over TFTP, the boot script over HTTP, then the installer and image over HTTP. After the installer writes the disk, the robot restarts from it and boots without further help. The edge server only serves files.

What this looks like in the lab

The lab runs the whole flow in two libvirt VMs: an edge VM with the three services, and a robot VM with a blank disk and no internet. A fresh clone, prebuilt images and about ten minutes get you from nothing to a robot you can log into. The edge server always gives the robot the same address, so from your host:

ssh root@192.168.79.10
# Welcome to the Robot!
# This image was loaded through iPXE from the edge server.

The root password is empty, which is fine only because the install network is isolated. vagrant ssh edge gives you a shell on the install server, and the robot’s serial console shows the install as it happens.

I ran it from a blank robot several times, including from a fresh clone of the repository. Each time the robot installed in about thirty seconds, and it still booted with the edge server stopped.

A cold build of both images took about five hours on eight cores and 50 GB of disk. A rebuild after a small change took minutes, because Yocto’s caches carry it. That’s why the lab ships the built images and why the build is a separate step from the install.

Why the usual ways lose

Typing the hostname at the installer prompt

The decision happens in the least supervised place possible: a console, a tired person, a visit already running late. A typo becomes the robot’s permanent name, copied into every config that keys on it, and fixing it costs another visit. Nothing records who chose the name or why.

cloud-init user data per box

cloud-init answers what configuration a machine applies on first boot. It needs an operating system already on the disk, and it needs user data delivered before the machine sees it. Someone maintains an artefact per box and gets it onto the machine somehow.

A USB image built per site

One image per site makes identity a build matrix. Every image gets rebuilt when anything in it changes, the sticks on the shelf drift from the build system, and the person at the machine carries the result. With a dozen sites that’s tedious. With fifty it’s a part-time job.

A Debian preseed install

Preseed answers an installer’s questions, and it’s the right tool when the fleet runs Debian. A Yocto image has no installer to answer. Cobbler and Foreman are built around the kickstart and preseed model, so they would sit idle. netboot.xyz offers a menu of upstream installers and no way to serve our own image.

A Tinkerbell cluster on the edge server

I designed this article around Tinkerbell first. It does the job: it keys on MAC address, boots a small in-memory OS and runs a workflow that writes the image. It also needs a Kubernetes cluster on the provisioning server, and one record per robot rendered from a Git allow-list before the robot arrives. A MAC address authenticates nothing, since it can be spoofed in seconds, so the allow-list only decides what the server is willing to say. A static iPXE chain plus a 90-line installer does the same install with files an edge server already knows how to serve. If a fleet grows to need per-machine workflows, Tinkerbell is still the next step up.

What this does not prove

The lab proves the install flow in VMs and nothing about real hardware. QEMU’s OVMF firmware behaves like many UEFI implementations, but a vendor board may re-add network boot entries or ignore the boot order the installer sets. In libvirt the installer’s efibootmgr call has no effect at all, because libvirt regenerates the boot order from its own settings. The lab therefore puts the disk first and the network second, so a blank disk falls through. That’s also how I’d configure a robot, and I haven’t tested it on a real board.

Nothing here checks who built the image. The installer verifies block checksums, which catches corruption, and a hostile edge server or a substituted image would pass. Secure Boot isn’t used. Signing the boot chain, so that iPXE refuses an image the fleet’s key didn’t sign, is the next step and belongs with the update articles. The same goes for identity. Every robot installs the same image and is called robot. Giving each one a name that sits on the data partition and survives updates is later articles’ work, along with a per-device key generated at first boot.

The edge server has to be on the robot’s segment when it boots. For a replacement robot at an existing site, it already is. For the first robot of a new site, the first install happens on a bench against the same server.


I’m David Bensoussan. I build deployment and update infrastructure for hardware and robot fleets. If you’re working out how to ship software to machines you can’t reach, book 30 minutes and we can work out your fleet’s update path.

Try It Yourself

Run it locally with the 07-pxe-provisioning environment in the robot-fleet-lab repository. You need Linux with KVM, libvirt, Vagrant and the vagrant-libvirt plugin. scripts/prereqs.sh lists whatever is missing, scripts/fetch-images.sh downloads the prebuilt images, and the README walks through the rest. Its troubleshooting section covers the failures I hit, and scripts/clean.sh removes everything afterwards.