Skip to main content

Cluster API Provider OpenStack Ships v1beta2 Behind a New ORC Dependency: What Declarative Fleet Lifecycle Looks Like on Private OpenStack Instead of Hetzner

9 min readDora NodaDora Noda
Share
On this page

VMware licensing costs have risen 800% to 1,500% at some organizations since Broadcom's acquisition closed, and Gartner expects more than a third of VMware workloads to have moved to another platform by 2028. A lot of that "another platform" is landing on OpenStack — the open-source cloud stack now running upward of 40 million compute cores worldwide, much of it inside enterprises and telcos that already had a private-cloud team and just needed a cheaper place to point it.

That migration wave is arriving at an interesting moment for Cluster API Provider OpenStack (CAPO), the Kubernetes SIG project that lets Cluster API provision and manage fleets of machines on an OpenStack cloud the same declarative way it does on AWS, vSphere, or bare-metal Hetzner. In June 2026, CAPO shipped v0.15.0-alpha.0, its first release carrying the new v1beta2 API. The version bump is a normal SIG-Cluster-Lifecycle housekeeping cycle. What's not normal is what's sitting underneath it: CAPO now hard-depends on a separate project, openstack-resource-controller (ORC), to do the actual work of talking to OpenStack.

That's the part worth reading closely if you're running Cluster API on private infrastructure and Hetzner isn't your provider. Here's what shipped, what ORC actually changes about how CAPO reconciles a fleet, and what it means for the "Cluster API's fleet-lifecycle model isn't Hetzner-specific" pitch to a team that already runs its own OpenStack cloud.

What Actually Shipped in CAPO v1beta2

v1beta2 is a straightforward API-surface cleanup, tracked as an umbrella effort under issue #3054 and released alpha in v0.15.0-alpha.0 on June 25, 2026. The concrete field changes:

ChangeWhat it means
flavor standardized on OpenStackMachineOne consistent way to specify instance size, instead of the mixed flavor/flavorID shape carried over from v1beta1
managedNetworks added to OpenStackClusterCluster-scoped network lifecycle becomes an explicit, named field instead of inferred from other settings
managedRouters added to OpenStackClusterSame treatment for router lifecycle — CAPO can now own router creation/teardown as a first-class declared intent
apiServer fields grouped under spec.apiServerThe scattered API-server-related fields on OpenStackCluster collapse into one nested block
status.readystatus.initialization.provisionedAligns with the broader Cluster API v1beta2 contract's push to stop overloading "ready" for multiple distinct meanings
Boolean polarity fixes, allNodesSecurityGroupRules renameSmaller cleanups: negative-polarity booleans flipped positive, and a misleadingly-named field renamed to say what it actually scopes (control-plane and worker nodes, not literally "all nodes")

None of this is architecturally interesting on its own — it's the same kind of API-contract tidying every Cluster API infrastructure provider does on its march toward v1beta2 compliance with the core CAPI contract. The interesting part is what CAPO's docs now say you need installed before any of these fields do anything.

The Real Change: ORC Becomes a Hard Dependency

CAPO's development documentation is blunt about it: "CAPO depends on ORC. No matter how you choose to work, you will need to deploy ORC in order to make CAPO functional." The install step is one command against your management cluster:

bash
kubectl apply -f https://github.com/k-orc/openstack-resource-controller/releases/latest/download/install.yaml

Prior to this, CAPO's own controllers talked to Nova, Neutron, Glance, and Cinder directly through the Gophercloud SDK, buried inside CAPO's internal reconcile loops. An OpenStackMachine object existed in Kubernetes, but the actual Nova server, Neutron port, and attached volumes it corresponded to did not — they were opaque side effects of CAPO's controller code, visible only by cross-referencing OpenStack's own CLI or dashboard against whatever CAPO's status fields happened to surface.

ORC replaces that with one Kubernetes custom resource per OpenStack primitive: Image, Flavor, Network, Subnet, Router, Port, and (per issue #2814, the tracking issue for retiring CAPO's internal OpenStackServer controller in favor of ORC) eventually Server itself. Each one is a real object you can kubectl get and kubectl describe, with its own status.conditions reporting exactly where that specific Nova instance or Neutron network sits in its reconciliation — created, still provisioning, failed, whatever the actual OpenStack-side state is. The maintainers' stated reason for the split is candid: "We risk diverging from ORC, we have to maintain our own controller. It would simply be preferable to adopt ORC and have one less thing to worry about." Rather than keep reimplementing OpenStack resource management inside CAPO, they're consuming a purpose-built controller for it — and shipping the migration as a phased rollout, running both the internal and external paths side by side for at least one minor release before removing the old one.

What Declarative Fleet Lifecycle Actually Buys You Over Terraform-on-OpenStack

This is the part that matters if you're deciding how to provision fleet infrastructure on a private OpenStack cloud in the first place, because Terraform-on-OpenStack is the default alternative every team already knows how to reach for.

Terraform's OpenStack provider is declarative in the sense that you describe the desired end state in HCL — but it only reconciles that state when someone or something runs terraform apply. Between applies, nothing is watching. If an operator manually edits a security group rule in the Horizon UI or the openstack CLI to unblock an incident, that drift sits silently until the next scheduled plan catches it — and depending on what changed, the plan CAPO's Terraform-based equivalent would compute isn't always an in-place fix. Some OpenStack resources don't support certain updates through the provider at all, and the only plan Terraform can produce is a destroy-and-recreate of the live resource, which for a Nova instance or a router means an outage nobody asked for over a change nobody wanted reverted that way.

Cluster API plus ORC has no equivalent "run apply" step. ORC's controllers watch every Image, Network, Router, and Server object continuously and requeue reconciliation on a fixed interval regardless of whether anyone triggered anything — the same model core Kubernetes uses to keep a Deployment's replica count correct. The same Horizon-console security-group edit gets noticed and reverted back to spec automatically, typically within the controller's next reconcile pass, with no separate "plan" step required.

That cuts both ways, and it's worth being honest about which way it cuts against you. If that manual edit was the intended fix — an operator opening a port during an incident because the GitOps pipeline that should have shipped the real fix is itself down — a continuously-reconciling controller will fight the operator and revert it back to the last-applied spec, same as it would fight a mistake. Terraform's plan/apply gap is a liability when you want drift caught fast, but it's also the only reason an emergency out-of-band change survives until someone gets around to updating the source of truth. Declarative-and-continuous is not strictly better than declarative-and-batched; it trades "drift persists until you notice" for "drift gets stomped whether you meant it or not." The fix in both models is the same discipline — land the emergency change in the spec, not just in the running system — but Cluster API/ORC gives you less runway to do that before it undoes your workaround for you.

It's also worth being precise about why OpenStack specifically needed a dedicated resource controller when Cluster API Provider Hetzner (CAPH) — the provider bex runs in production — never spun one out. Hetzner's API is flat: a HetznerMachine maps to roughly one Hetzner Cloud server call, one placement group, one private network. OpenStack's API is layered — Nova, Neutron, Cinder, and Glance are four separate services with their own resource graphs and dependency ordering (a Neutron port has to exist before the Nova server that attaches to it can boot; a Cinder volume has to be available before it can attach). Modeling that dependency graph correctly, with retries and backoff at each layer, is enough surface area to justify its own controller — which is exactly the gap ORC fills. CAPH didn't need an ORC-equivalent because Hetzner's API never had that graph to model in the first place.

Why This Matters for a Self-Hosted PaaS Pitch Beyond Hetzner

The underlying claim worth stating plainly: Cluster API's fleet-lifecycle API — Cluster, Machine, MachineSet, MachineDeployment — was never Hetzner-specific to begin with. It's a fixed, narrow API implemented against whichever infrastructure provider sits underneath it: CAPD for local development, CAPH against Hetzner in production today, and — for a prospect that already operates a private OpenStack cloud rather than buying bare Hetzner Robot servers — CAPO instead.

For a self-hosted, git-push PaaS like bex, that's a real answer to a real objection: a platform-engineering team standing up their own OpenStack cloud specifically to get off VMware licensing isn't a team that wants to also stand up a second, unrelated cloud-shaped vendor relationship with Hetzner just to run a PaaS control plane on top of it. Swapping the machine-fleet provider from CAPH to CAPO — plus installing ORC as one more controller in the management cluster — is the entire delta. The App and Database CRDs, the git-push-to-deploy control plane, and everything a tenant actually interacts with sit unchanged above that layer, because Cluster API's fixed Machine/MachineDeployment contract was the abstraction boundary all along.

The honest caveat: this is a "meet them where they are" pitch, not a "you should go adopt OpenStack" one. OpenStack's operational complexity is real and well documented — it's a platform that rewards teams who already have a dedicated cloud-ops function, which is exactly the profile of a team migrating off VMware under licensing pressure, not a team starting from zero. And ORC itself is still v1alpha1 — the maintainers say the core API patterns are stable, but there's no announced beta timeline, so treat the exact CRD shape as something that can still move before it settles.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, provisioned through the same Cluster API model this post describes. Star the repo on GitHub or deploy your first app today.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex