Lesson 05 of 12 · Provisioning
Tinkerbell & Metal3
Two open-source ways to provision bare metal from Kubernetes: Tinkerbell (hardware records, templates of container actions, workflows run by an in-memory OS) and Metal3 (BareMetalHost resources driven by Ironic), and how both plug into Cluster API.
Why Kubernetes-native provisioning?
Provisioning bare metal used to mean separate tools (Cobbler, Foreman, MAAS, Kickstart scripts). Tinkerbell and Metal3 describe hardware and provisioning as Kubernetes resources, so the same GitOps and Cluster API workflows that manage clusters also manage the machines underneath.
Tinkerbell is a recipe robot: each server gets a card (Hardware), you write a recipe (Template: "wipe the disk, copy the OS, set the name, reboot"), and a Workflow says "cook this recipe on that server". A tiny helper (HookOS) boots on the server and follows the recipe step by step. Metal3 is a different robot that looks after a list of servers (BareMetalHosts) and uses a well-known toolbox (Ironic) to prepare each one.
Tinkerbell
| Component | Job |
|---|---|
| Smee | DHCP (or works with your DHCP), TFTP, iPXE scripts |
| HookOS | Linux in memory; runs the tink worker |
| Tink (server, controller, worker) | Stores and runs workflows; actions are containers |
| Tootles | Metadata (EC2-style / cloud-init) for machines |
| Rufio | BMC operations (power, boot device, virtual media) as Kubernetes resources |
(Recent Tinkerbell releases package these components together in one deployment/Helm chart; check the docs for your version.)
A Hardware record:
apiVersion: tinkerbell.org/v1alpha1
kind: Hardware
metadata:
name: site042-node1
namespace: tink-system
spec:
disks:
- device: /dev/sda
interfaces:
- dhcp:
mac: "3c:ec:ef:00:00:01"
hostname: site042-node1
arch: x86_64
uefi: true
ip:
address: 10.42.0.11
netmask: 255.255.255.0
gateway: 10.42.0.1
name_servers: [ "10.42.0.1" ]
netboot:
allowPXE: true
allowWorkflow: true
A Template of actions (images from the Tinkerbell actions catalogue):
apiVersion: tinkerbell.org/v1alpha1
kind: Template
metadata:
name: ubuntu-raw
namespace: tink-system
spec:
data: |
version: "0.1"
name: ubuntu-raw
global_timeout: 1800
tasks:
- name: os-installation
worker: "{{.device_1}}"
volumes:
- /dev:/dev
- /dev/console:/dev/console
actions:
- name: stream-image
image: quay.io/tinkerbell/actions/image2disk:latest
timeout: 900
environment:
DEST_DISK: /dev/sda
IMG_URL: http://10.42.0.5:8080/ubuntu-2404.raw.gz
COMPRESSED: true
A final action normally reboots or kexecs into the installed OS; the actions catalogue provides these. (Pin action image versions in production, and check the current catalogue: action names and reboot/kexec patterns vary between releases.) A Workflow references the template and the hardware (hardwareRef, templateRef, and hardwareMap mapping device_1 to the MAC). Watch progress with kubectl get workflows -n tink-system -w.
Metal3
Metal3 runs the Bare Metal Operator with Ironic (the OpenStack bare-metal service, used standalone):
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
name: site042-node1
namespace: metal3
spec:
online: true
bootMACAddress: "3c:ec:ef:00:00:01"
bmc:
address: redfish-virtualmedia://10.42.10.11/redfish/v1/Systems/1
credentialsName: site042-node1-bmc # Secret with username/password
rootDeviceHints:
deviceName: /dev/sda
image:
url: http://10.42.0.5:8080/ubuntu-2404.qcow2
checksum: http://10.42.0.5:8080/ubuntu-2404.qcow2.sha256sum
checksumType: sha256
format: qcow2
userData:
name: site042-node1-userdata # Secret with cloud-init
The host moves through states: registering → inspecting (hardware inventory collected) → available → provisioning → provisioned. Ironic can also clean disks and apply BIOS/RAID settings.
Cluster API integration
- CAPT (Cluster API Provider Tinkerbell) and CAPM3 (Cluster API Provider Metal3) let CAPI Machines claim and provision servers.
- EKS Anywhere on bare metal uses Tinkerbell underneath (lesson 08); OpenShift's bare-metal installer uses Metal3.
| Tinkerbell | Metal3 | |
|---|---|---|
| Model | Workflows of container actions | Declarative host state via Ironic |
| Flexibility | Very flexible (any action) | Opinionated, mature Ironic features (cleaning, RAID, BIOS) |
| Boot | PXE/iPXE (Smee) or virtual media via Rufio | PXE or virtual media via Ironic |
| Used by | EKS Anywhere | OpenShift, CAPM3 users |
Try it: provision a VM like bare metal
- Follow the Tinkerbell sandbox/playground guide (libvirt or Vagrant VMs) to install the stack on a small Kubernetes cluster.
- Create a Hardware record for a VM (its MAC), a Template that streams a small raw image, and a Workflow; boot the VM and watch the actions.
- Break it: point
IMG_URLat a missing file and read the failing action's status. - Alternatively, run metal3-dev-env (Metal3's development environment with VMs and sushy-tools) and watch a BareMetalHost go from registering to provisioned.
- Compare how each tool records hardware and reports failure.
Going deeper: provisioning at scale
- Generate Hardware/BareMetalHost records from the site inventory in Git (a CSV or YAML per site), never by hand.
- Keep BMC credentials in Secrets delivered from a vault, with per-host passwords.
- Stream compressed raw images over HTTP from a server near the machines; image download is the slowest step.
- Plan re-provisioning: the same records and templates rebuild a failed node (lesson 12).
Recap
- Tinkerbell: Hardware + Template (container actions) + Workflow, run by HookOS; Smee boots, Rufio drives BMCs, Tootles serves metadata.
- Metal3: BareMetalHost resources, Bare Metal Operator + Ironic, with inspection and cleaning.
- Both plug into Cluster API (CAPT, CAPM3); EKS Anywhere uses Tinkerbell.
- Generate records from inventory in Git; keep images close to the hardware.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.