← Back
Home › Systems › Cloud platform and compute

bare-metal-provisioning

Turns racks of unconfigured servers into machines that users can request, install and return, much like cloud virtual machines.

MAAS, Hetzner robot

Open the bare-metal-provisioning pagebare-metal-provision… Desired-state reconciliationdistributed: Declare the target in a store; controllers repeatedly diff actual against desired from full state and apply idempotent fixes, so missed events only delay convergence.›Durable job queuedistributed: Persisted tasks that workers lease for a visibility timeout, ack on success and retry with backoff; at-least-once execution that survives worker and queue crashes.›Bmc out of band managementhardware-os: IPMI/Redfish BMC control: power, boot device, sensors, serial-over-LANImage based provisioninghardware-os: Stream and verify disk images onto machines, then first-boot configurationX86 boot processhardware-os: Firmware to kernel handoff: UEFI/BIOS, boot protocols, page tablesIp address managementnetworking: CIDR allocation, subnet carving and lease tracking with bitmaps or prefix treesPxe network bootnetworking: DHCP + TFTP/HTTP (iPXE) network boot of bare-metal machinesCryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›Secure boot chainsecurity: Each boot stage verifies the signature of the next before running it, anchored in an immutable ROM key hash, with version counters against downgrade and a TPM log of what ran.›SSH protocol (transport, auth,channels)security: A three-layer protocol on one TCP connection: encrypted packet transport, user authentication, then multiplexed channels with windows for shells, files and port forwards.›X509 certificatessecurity: Certificate chains, path validation, SANs, revocation and short-lived issuanceWatch notificationsdistributed: A store numbers every write; clients list at revision R, then watch from R+1 and receive an ordered change stream with no gap, no duplicate, and a resume point.›Exponential backoff with jittersystems: Retry after delays that grow geometrically and are randomized, so failed clients spread out instead of hammering a recovering service in synchronized waves.›Dead-letter queuedistributed: A side queue for messages that keep failing: after N delivery attempts or a permanent error, park them with the reason so the main queue keeps moving.›Idempotency keysfinance: Client-chosen unique key per logical operation, stored with a request fingerprint and the final response, so retries replay the first result instead of acting twice.›Write-ahead logstorage: Append each change to a checksummed sequential log and fsync it before touching data pages; the basis of crash durability in nearly every database.›Conditional writesdistributed: Put-if-absent / if-match compare-and-swap on objects or keys as a commit primitiveLeasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›HTTP range requestsnetworking: Ask for byte offsets with Range and get a 206 slice back; the standard way to resume, seek and read big remote files piecewise.›Roaring bitmapscompression: Compressed set of 32-bit integers in 64K-value chunks, each a sorted array, bitmap or run list; fast intersections, the standard for bitmap indexes.›Radix triedata-structures: Compressed prefix tree for path routing, domain matching and prefix lookupsLongest prefix matchnetworking: IP prefix lookup structures (DIR-24-8, Poptrie, LC-trie) for routes, ACLs and geo-IPMessage sequence numbersdistributed: A per-stream counter on every message lets the receiver detect loss, duplicates and reordering, ack cheaply and resume after reconnect with "send from N".›Grpc protocolnetworking: GRPC framing over HTTP/2: unary and streaming RPCs, deadlines, statusMVCCstorage: Keep timestamped versions of each row so readers see a consistent snapshot without blocking writers; the default concurrency control in modern databases.›PRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›CRC32C checksumshardware-os: The Castagnoli CRC-32 with dedicated instructions on x86 (SSE4.2) and ARMv8. It is the standard fast integrity check for storage blocks, logs and network frames.›fsync, O_DSYNC and O_DIRECThardware-os: What fsync, fdatasync, O_DSYNC, O_DIRECT and F_FULLFSYNC really guarantee on Linux and macOS, and the exact call sequences for crash-safe files.›Fencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›SIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›

How it is built

Architecture overview

A bare-metal provisioning system turns a rack of unconfigured servers into machines that a user can request, install and hand back, with the same ease as a cloud VM. The machine has no operating system and no agent to talk to, so everything starts from hardware that is already there: a baseboard management controller (BMC) that can power the box and choose its boot device, and a network card that can boot from the network. The system powers the machine on, makes it fetch a small boot program over DHCP and TFTP or HTTP, runs a temporary in-memory agent on it, lets that agent inspect the hardware, and later writes a disk image and reboots into it. A central store holds the desired state of every machine (what it should be running) and controllers drive each machine toward it, retrying and recovering as steps fail.

The one idea that makes it work is that the machine is treated as an unreliable remote device with a state machine: every step (power, boot, inspect, write, verify, reboot) is a small idempotent action that can fail, time out or be repeated, and the durable state of the whole fleet lives in one store, never in a process.

Parts:

 operator / API ──► desired state store (MVCC, watch) ◄──── controllers (reconcile loops)
                           │ job queue (leases, retries)            │
              ┌────────────┴───────────┐                            │
              ▼                        ▼                            ▼
        IPAM / inventory         BMC driver ──(IPMI/Redfish)──► BMC ──► power, boot device
        (pools, MAC, BMC IP)                                          │
                                                                      ▼
   DHCP + TFTP/HTTP (iPXE) ◄──── PXE firmware on NIC ◄─────── machine powers on
              │ boot loader + kernel + initrd (signed)
              ▼
     in-memory agent ──gRPC/HTTP──► control plane: inventory, then "write image X"
              │ image over HTTP (range, hash check)
              ▼
     disk written, fsync ─► set boot device = disk ─► reboot ─► first-boot config

There are two lifecycles of one machine: enrolment (discover, inspect, benchmark, mark ready) and allocation (deploy, use, release, wipe). Both reuse the same boot path and the same agent.

How the parts fit together

Inventory and addressing

ip-address-management (no page yet) is the allocator for subnets, DHCP ranges and static addresses. A /24 or /16 pool is a bitmap of used addresses, and Roaring bitmaps keep large, sparse pools small and make "first free address in this range" a fast scan. Prefix lookups ("which subnet and VLAN does this address belong to?") use longest-prefix-match (no page yet) over a radix-trie (no page yet). The contract with the rest of the system is small: given a machine identity (MAC address or BMC-reported serial number), return its subnet, VLAN, lease and BMC address, and never hand the same address to two machines. That last guarantee is a conditional write on the allocation record (conditional-writes (no page yet), no page yet), not a lock in a process. DHCP leases themselves are time-limited by design, which is the same idea as Leases: an address ownership that expires unless renewed.

Out-of-band control

bmc-out-of-band-management (no page yet) is the only reason bare metal can be automated at all. The BMC is a small separate computer on the server board with its own network port and power, so it works when the main machine is off or broken. Two protocols matter: IPMI (older, binary, UDP) and Redfish (REST and JSON, defined by the DMTF). The provisioning system uses them for four verbs only: power on/off/cycle, set the next boot device (PXE once, then disk), read sensors and inventory, and open a serial-over-LAN console to see why a boot failed. Contract: the BMC is slow (seconds per call), flaky and a security risk, so it sits on a separate management network, calls go through one driver per vendor family, and every call is retried with backoff and jitter so a rack that powers back up after an outage does not hammer a few BMCs in lockstep. Credentials are secrets in a store, never in logs; Redfish uses TLS with x509-certificates (no page yet), and BMC shells and serial consoles are reached over SSH.

Network boot

pxe-network-boot (no page yet) is the bootstrap. The NIC firmware broadcasts a DHCP request that says "I am a PXE client of architecture X"; the answer carries an address plus the name and server of a boot file (see RFC 2131 for DHCP and RFC 4578 for the PXE-related options). Classic PXE downloads the file by TFTP, which is slow and has no integrity. The standard fix is a chain: the first file is iPXE, which then continues over HTTP with a script that the control plane generates per machine. That script is the control point: it can pick a kernel and initrd by MAC, run an installer, or tell an enrolled machine to "boot from local disk". The DHCP server must answer differently for BIOS, UEFI and already-iPXE clients, otherwise a machine loops between iPXE and the first loader.

What runs after the loader is the x86-boot-process (no page yet): firmware hands control to a loader, the loader to a kernel and initrd. When Secure Boot is on, each stage must verify the next (Secure boot chain); a provisioning system either ships a signed shim and kernel or turns the feature off for the ephemeral environment and enforces it again on the installed OS. Choose one policy for every machine.

Image deployment

image-based-provisioning (no page yet) is faster and more repeatable than running an installer on each node: take a prebuilt root filesystem image and write it. The in-memory agent downloads the image over HTTP, hashes it while streaming, writes it to the target disk, then configures first boot (hostname, network, SSH keys, cloud-init-style user data), fixes the boot loader, and sets the boot device to disk.

Blocks and why each is there:

  • Cryptographic hashes — the image has a digest recorded when it is published; the agent refuses to boot a disk whose digest differs. SHA-256 is the interoperable choice; BLAKE3 is faster on a many-core machine, which matters for multi-GB images.
  • HTTP range requests — an interrupted download resumes at the last byte instead of restarting; a cache or mirror in the rack can serve the same image to dozens of machines.
  • CRC32C checksums — a cheap per-chunk check against corruption in transfer or on disk, hardware-accelerated on x86 and ARM; use it for chunk framing, not as a security check.
  • fsync and direct I/O — "written" must mean durable before the agent reports success and reboots; skipping the flush gives machines that boot fine in the lab and lose their root filesystem after a power cut.

Contract with the controller: the agent receives an image reference plus digest, a target disk (by a stable identifier such as WWN, not /dev/sda) and a user-data blob, and reports a final state. It must be safe to re-run from the start, because a power loss in the middle is normal.

Controller and state store

Desired-state reconciliation is the heart of the control plane. Each machine has a spec (desired: image, network, owner) and a status (observed: power state, last step, last error). A controller loop compares them and issues the next small action. Idempotent steps replace long scripts that are lost when a process dies. The store behind it needs revisions: MVCC gives each write a version, and Watch notifications let controllers list at revision R and then watch from R+1 with no gap and no duplicate, so a restarted controller resumes exactly where it was.

Contract between neighbours: the user only edits the spec; controllers only write status (plus the next action in the queue); two controllers never act on the same machine at once (see the work queue). Machine state is a small explicit state machine (new, enrolling, ready, allocated, deploying, deployed, releasing, failed). Illegal transitions are rejected by the store with a conditional write. MAAS and Ironic both expose roughly this lifecycle to users (see their docs).

Work queue

Provisioning steps take minutes and talk to hardware that fails, so the controller does not call BMCs inline; it enqueues work. A Durable job queue persists each job, hands it to a worker under a Lease, and retries it if the lease expires. The sharp edge is the stuck worker: a worker whose lease expired may still be running and sends a power-off after a newer worker already started the install. Fencing tokens close that hole: each lease carries a growing number, and the BMC driver (or the state store) rejects an action with an older token than the latest one for that machine. Each action also carries an idempotency key, so a retried "set boot device to PXE" is the same call, not a second one. The queue's own log follows write-ahead log rules. Jobs that keep failing go to a dead-letter queue where an operator sees "machine R12-U14 failed to PXE three times, last BMC error X" instead of an invisible retry storm.

The in-memory agent is a small client that has no state and no credentials except a one-time token baked into the iPXE script for that boot. It talks to the control plane over HTTPS or gRPC (grpc-protocol, no page yet), reports inventory (CPUs, memory, disks, NICs, firmware versions), pulls its next task and streams logs. Use message sequence numbers on the agent's report stream so the control plane can detect lost and duplicated messages after a flaky network or a controller restart. The link is pull-based on purpose: the machine reaches out, because the control plane cannot reach it until the OS is up.

Data and control flow

Enrolling a new machine:

  1. An operator racks the machine, plugs in the BMC and data ports, and registers the BMC address and credentials (or leaves it to discovery).
  2. The controller allocates a management IP and a PXE-boot lease; the BMC driver sets the next boot device to PXE and powers the machine on.
  3. Firmware DHCP-boots iPXE; iPXE requests a script by MAC; the control plane returns the enrolment kernel and initrd.
  4. The agent starts, reports hardware inventory, optionally runs memory and disk tests, and posts results.
  5. The controller records inventory in status, labels the machine by hardware class, and marks it ready. The BMC powers it off.

Deploying a machine:

  1. A user or scheduler writes a spec: image, network, SSH keys. The store bumps the revision; the controller's watch fires.
  2. The controller picks a ready machine by a conditional write on its state (ready to allocated), so two requests cannot claim the same one.
  3. A job is queued: set boot device to PXE once, power cycle. A worker takes it under a lease with a fencing token.
  4. The machine boots the deployment agent through the same iPXE path. The agent downloads the image from a rack-local mirror, verifies the digest while writing, flushes, writes first-boot data and tells the controller it is done.
  5. The controller sets the boot device to disk, reboots, and waits for the OS to call back (a phone-home request or a health probe) before marking the machine deployed. If it does not within the timeout, the job goes back to the queue with backoff, and after N attempts to the dead-letter queue.
  6. On release the machine is wiped and returned to ready, or flagged for repair.

Scaling

In the order they usually bite:

  1. Image transfer — hundreds of machines downloading multi-GB images from one server saturate its uplink. Levers: rack-local caches or mirrors, range-request resume, a compressed image, scheduling rolling batches rather than all at once.
  2. DHCP and TFTP/HTTP boot servers — a datacenter power event makes every machine PXE-boot at the same moment. Levers: HTTP instead of TFTP, a DHCP relay per rack with local servers, and backoff with jitter on retries.
  3. BMC concurrency — BMCs are slow and some fall over under parallel requests. Levers: a per-BMC concurrency limit of one in the driver, queue partitioning by rack, batching reads (inventory) with a long refresh interval.
  4. Control-plane state — thousands of machines with frequent status writes. Levers: write status only on change, keep logs out of the state store, shard controllers by rack or site. A single database (MAAS uses PostgreSQL) goes a long way, but the watch stream should not be the bottleneck.
  5. Multi-site — latency and partitions between sites. Lever: a regional controller close to each rack that works during a WAN outage and syncs desired state upward.

Boot time per machine is dominated by hardware POST and a reboot, not software; fleet-wide time is cut by parallelism across machines.

Failure modes

FailureSymptomCauseFix
PXE loopmachine keeps reloading the boot menuDHCP answers iPXE clients with the iPXE binary again; wrong boot file for BIOS vs UEFIdistinguish client types in DHCP (user class, architecture option); script ends with "exit" to disk after install
BMC unreachablepower and boot-device calls time outmanagement VLAN or ARP issue, BMC hung, credentials changedretry with backoff, mark "BMC down", BMC reset through a vendor tool; keep a separate health probe
Stale worker acts latemachine powered off in the middle of an installlease expired while the worker was still alivefencing token checked by the driver or store; idempotency keys per action
Image corruptionmachine boots to a broken root or kernel panicspartial download, wrong digest, no flush before rebootverify digest on write and again before reboot; fsync; resume by range request
Wrong disk wiped or writtendata loss on a machine meant to be kept/dev/sdX ordering changed between bootsselect disks by WWN or serial; refuse deploy on a disk with data unless the spec says so
Double allocationtwo users get the same machine or IPnon-atomic claim in the controllerconditional write on the machine/IP record; unique constraint in the store
Secure Boot refuses the loaderblank screen or "violation" after power onunsigned or untrusted kernel/shim, stale key databaseship a signed chain or disable in the enrolment environment by policy; track key expiry
Retry storm after outageBMCs and DHCP overloaded, many machines in failed stateall retries aligned in timejitter, per-rack concurrency limits, DLQ for repeat offenders
Stuck "deploying"no progress, no erroragent never phones home, controller waits foreverper-step deadlines in the state machine; serial console log capture

Real implementations

namewhat it isrepolicencenotes
MAASCanonical's bare-metal cloud: DHCP, PXE, commissioning, image deploy, IPAM, BMC power drivers; region and rack controllerscanonical/maasAGPL-3.0the full stack in one product; see maas.io/docs
OpenStack Ironicbare-metal service with pluggable drivers (IPMI, Redfish, vendor) and a deploy agentopenstack/ironicApache-2.0used standalone or as part of OpenStack; docs
Tinkerbellprovisioning engine: network and ISO boot, BMC interactions, metadata service, workflow engine (per its repo description)tinkerbell/tinkerbellApache-2.0the older tinkerbell/tink repo is archived; docs
Metal3 baremetal-operatorKubernetes custom resources for bare-metal hosts, uses Ironic underneathmetal3-io/baremetal-operatorApache-2.0the desired-state-in-Kubernetes variant of this design
iPXEthe open-source network boot firmware used in the chain-loading stepipxe/ipxe—scripting, HTTP, TLS boot; ipxe.org

The alternative: do not provision metal yourself. Rent servers from a provider with a web API and an installer (hourly or monthly), and build only the layer above. That saves the BMC, DHCP and image machinery, at the price of provider lock-in, limited control over firmware, and a smaller choice of images and network layouts. Provision your own metal when you own the racks, need custom networking or firmware, or run enough machines that the provider's margin exceeds the cost of the team.

Sources

  • DHCP: R. Droms, Dynamic Host Configuration Protocol, RFC 2131, 1997. RFC
  • Y. Hu, M. Ni, D. Liu, Dynamic Host Configuration Protocol (DHCP) Options for the Intel Preboot eXecution Environment (PXE), RFC 4578, 2006: client system architecture and PXE DHCP options. RFC
  • iPXE project documentation: chain loading
  • DMTF Redfish standard: redfish.dmtf.org
  • MAAS documentation: maas.io/docs
  • OpenStack Ironic documentation: user guide
  • Tinkerbell documentation: tinkerbell.org/docs

Similar systems

Share the most building blocks with this one.