# Bare Metal Operator: a Kubernetes API for physical hosts

> The Bare Metal Operator gives Kubernetes a BareMetalHost API for tracking, inspecting and provisioning physical machines. It is a controller stack with pluggable provisioners, a multi branch release train and a documentation trail that stops before the operational details most operators need.

**metal3-io/baremetal-operator** — Bare metal host provisioning integration for Kubernetes

- Repository: https://github.com/metal3-io/baremetal-operator
- Stars: 767 · Forks: 338
- Language: Go
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/metal3-io-baremetal-operator

## Kubernetes gets an inventory of physical machines

Kubernetes describes containers, so a server in a colocation rack has no natural place in its vocabulary until something translates hardware into API objects. The Bare Metal Operator does that translation. It implements a Kubernetes API for managing bare metal hosts and keeps an inventory of what is available as instances of the BareMetalHost custom resource definition. For a platform team that rents rack space, owns its own machines, or mixes on premises capacity with cloud instances, that inventory is the point. Instead of keeping a spreadsheet of serial numbers and disk sizes, the state lives in the API server, where other controllers can read it, policies can act on it, and access follows the same RBAC rules as everything else in the cluster. The intended reader is therefore not a hobbyist running a single server but an operator who wants physical hardware to participate in the same declarative model as the rest of the estate.

## Three controller jobs: inspect, provision, clean

The README lists what the operator knows how to do, and the list is short enough to be useful. First, it inspects a host's hardware details and reports them on the corresponding BareMetalHost, covering CPUs, RAM, disks, NICs and more. Second, it provisions hosts with a desired image. Third, it cleans a host's disk contents before or after provisioning. The data flow follows from those three jobs: an operator creates a BareMetalHost object describing a machine, the controller reconciles that object against reality, the inventory fields get filled in with what was actually detected, and the image and cleaning steps drive the machine toward the declared state. That is the mechanism visible from the repository layout, with the custom resource definition in the apis directory, the controller logic under internal and pkg, and the executable in main.go. The README also says more capabilities are being added regularly, which is a signal that this list is a moving target rather than a finished contract.

## Provisioners are compiled Go plugins, not network services

The interesting architectural choice is how the operator reaches a machine. The Dockerfile builds the manager binary with a normal go build, but then builds a separate artifact as a Go plugin, which means the provisioning logic is loaded into the controller process rather than reached over the network. The build target names the output ironic-provisioner.so and compiles the package under pkg/provisioner/ironic/plugin, and a second target builds a demo plugin the same way. The project also publishes a shared SDK stage that pins the Go toolchain and module versions so third parties can build out of tree provisioner plugins with parity to the operator itself, and a separate Dockerfile.plugin-test exists for that work. The apis and pkg/hardwareutils directories are separate Go modules with their own go.mod files, so the hardware detection helpers and the API types can be consumed separately from the controller. If you are evaluating extensibility, this is where the extension point sits: a new provisioning backend is a plugin, not a fork.

## Setup runs through make manifests and the metal3 namespace

The README is honest about what it leaves out. It gives no end user install command, and instead points to a resources list containing API documentation, a development environment setup guide, configuration notes, testing notes and a plugin provisioner guide. So the first real work is reading docs/dev-setup.md and docs/configuration.md, not copying a curl into a terminal. What the repository does give you is the operator's own configuration surface. The Makefile sets RUN_NAMESPACE to metal3, exports OPERATOR_NAME as baremetal-operator, and exports the kernel URL the deployment uses to serve the Ironic Python agent image:

```bash
export OPERATOR_NAME=baremetal-operator
export DEPLOY_KERNEL_URL=http://172.22.0.1:6180/images/ironic-python-agent.kernel
```

The Makefile notes that those values must match what sits in config/default/ironic.env. There is also a manual step for contributors: whenever config changes, the README asks you to run make manifests, which renders config/render/capm3.yaml, and it warns you not to edit that rendered file by hand. The examples directory holds ready shapes for a single host, a demo host list, a host with bad credentials and a worker node, which are the cheapest starting point for a first use.

## Pull requests are judged against CAPM3, not against BMO alone

The test strategy reveals a dependency on other projects and on hardware you probably do not own. A pull request in this repository is tested with CAPM3 integration tests, and the README describes two kinds of them: CAPM3 end to end integration tests and ansible integration tests. You will see one of each as a required test on a pull request. Beyond that, the project runs its own e2e suites, one required set and one optional set that is described as still under active development, and the goal stated in the README is that the CAPM3 based tests will eventually be replaced by the operator's own e2e tests. The branch matrix is wide. CentOS hosts the CAPM3 end to end job and Ubuntu hosts the ansible job, and both run for main and for the release-0.14, release-0.13 and release-0.12 branches. For an adopter this matters in a practical way: the project tracks the Cloud Native Infrastructure Foundation compliance badge alongside OpenSSF best practices and scorecard badges, and the CI story is one where a change can pass locally and still fail on a matrix of operating systems and upstream branches.

## Where the operator is the wrong tool

There are clear cases where reaching for this operator is a mistake. If your workloads run on rented cloud instances, there is nothing for it to inspect, and a cloud scheduler already holds the inventory you would be re-creating. If you have a handful of servers and no intention of exposing them to a Kubernetes API, the operator adds a controller, a custom resource definition and a plugin surface to solve a problem that a provisioning script handles in a fraction of the effort. The sharpest limitation is that the path from a BareMetalHost object to a running operating system is not self contained. The operator depends on the Ironic ecosystem, visible in the go.mod requirement on the ironic standalone operator API and in the ironic-deployment directory at the top level, and the Makefile serves a kernel image for the Ironic Python agent over HTTP. The README does not document rollback for a failed provisioning attempt, so a host stuck mid image load has no stated recovery path, and the README does not describe what happens when the detected hardware does not match what the BareMetalHost claims.

## Two release lines shipped on the same day in October

Maintenance signals here are recent and unusually busy. The last push was on 2026-10-20, the repository is not archived, and three releases sit within five weeks: v0.14.2 on 2026-10-15, v0.13.6 on 2026-10-15 and v0.14.1 on 2026-09-22. Two tagged releases on the same day, one from the 0.14 line and one from the 0.13 line, tells you the project maintains parallel branches and that upgrade means picking a line rather than always taking the newest tag. The dependency versions line up with that pace: the go.mod file requires Go 1.26.0, the Kubernetes libraries at v0.36.5, cluster-api v1.14.2 and controller-runtime v0.24.1, and the project ships its own module version flag so plugins can be built against the same code. The licence is Apache-2.0, which is the permissive licence most operators expect from a CNCF project, and the repository carries a SECURITY_CONTACTS file alongside a DCO. The practical cost of an upgrade is the gap between your Kubernetes API version and the one these dependencies target.

## How it compares with driving the provisioner directly

The alternative approach is to skip the operator and talk to the provisioning backend yourself, whether through its API or through a tool built for one time installs. The difference is where the state lives and who reconciles it. Talking to the backend directly means your inventory is whatever your scripts and your notes say it is, and recovery from a half finished deployment is your script's problem. Running the operator turns each machine into a Kubernetes object, so the desired state is declared, reconciliation is continuous, and the same cluster policies, audit trail and RBAC that cover the rest of your workloads also cover the hardware. The trade-off runs the other way too. The direct route is one process and one API, while the operator route adds a controller, a custom resource definition, a plugin build step and a dependency on upstream CAPM3 behaviour in CI. For hardware fleets that grow and shrink, or for machines that must be reconciled against declared intent, the declarative route earns its overhead. For a fixed set of servers, it does not.

## Conclusion

Adopt the Bare Metal Operator if you own hardware and want it described through Kubernetes objects, and pair it with the Ironic provisioner that its plugin is built for. Do not adopt it as a general purpose installer for cloud images. Before committing, check that the branch you intend to run matches your Kubernetes API version, and read docs/configuration.md, because the README does not explain how the operator is deployed into a live cluster.

## FAQ

### What is the BareMetalHost custom resource used for in the Bare Metal Operator?

It is the inventory record for available bare metal hosts, maintained as instances of the BareMetalHost custom resource definition. The operator also writes inspection results back onto it, including CPU, RAM, disk and NIC details.

### How does the Bare Metal Operator actually provision a host?

The README states that the operator provisions hosts with a desired image and can clean a host's disk contents before or after provisioning. The provisioning logic is compiled as Go plugins, including ironic-provisioner.so and a demo plugin, so a backend is loaded into the controller process rather than reached over the network.

### What should I run after changing the config directory?

The README asks contributors to run make manifests whenever the config directory changes, which renders config/render/capm3.yaml. That rendered file should not be edited by hand.

### Is there an install command for the Bare Metal Operator in the README?

No. The README provides no end user installation command and instead links to API documentation, a development environment setup guide, configuration notes, testing notes and the provisioner plugin guide. The Makefile exposes the operator name and the kernel URL used for the Ironic Python agent.

### Which branches and operating systems does the integration test matrix cover?

CAPM3 end to end integration tests run on CentOS and ansible integration tests run on Ubuntu, and both run against the main branch and the release-0.14, release-0.13 and release-0.12 branches. A pull request is expected to pass one CAPM3 end to end test and one ansible integration test.

## Sources

- [Issues](https://github.com/metal3-io/baremetal-operator/issues)
- [License: Apache-2.0](https://github.com/metal3-io/baremetal-operator/blob/main/LICENSE)
- [metal3-io/baremetal-operator on GitHub](https://github.com/metal3-io/baremetal-operator)
- [README](https://github.com/metal3-io/baremetal-operator/blob/main/README.md)
- [Releases](https://github.com/metal3-io/baremetal-operator/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/metal3-io-baremetal-operator
