Overview¶
What banlieue does, in one sentence: it gives you a single Kubernetes-native API for describing virtual machines, then dispatches each VM to whichever hypervisor backend you've plugged in (Cloud Hypervisor on your own KVM hosts, vSphere, libvirt, Proxmox VE, or one you write yourself) without your manifest ever changing shape.
This page is the fundamentals: what banlieue is, what it does, and how the pieces fit together at a single glance. For the reasoning behind these choices, see Why banlieue?. For the wiring, see Architecture.
The fundamental idea¶
There are three actors:
| Actor | What they do |
|---|---|
| User | Writes one VirtualMachine YAML. Runs kubectl apply. That's it. |
| banlieue controller | Watches VirtualMachines, resolves their refs, creates a backend-specific infrastructure CR, and mirrors status back. |
| Provider controller | Watches its infrastructure CR and talks to a real hypervisor (Cloud Hypervisor / vSphere / libvirt / Proxmox VE / …). |
Everything happens through the Kubernetes API. There is no second protocol, no RPC, no message bus. CRDs are the messages. The K8s API server is the bus.
High-level diagram¶
Three rules to remember when reading the diagram:
- Solid arrows are reads/writes against the K8s API. That's the only form of communication between any two controllers.
- Dashed arrows are watches or references — they exist in the data, not on a network wire.
- The user only ever interacts with the top-left box. Everything else is plumbing they shouldn't have to see.
One provider is not a pod. The Cloud Hypervisor provider runs on the KVM host itself, as a systemd service, and dials the API server; nothing dials the host. To the diagram that makes no difference: it watches its
CloudHypervisorMachines and patches their status like every other provider. All four infrastructure CRs are reconciled today; see Project status.
A 10-second walkthrough¶
-
The user writes:
-
The banlieue controller sees it, looks up
db-prod-largeandubuntu-22-04, matchesplacement.providerSelectoragainst the availableProviderCRs to pick one (here, a Cloud Hypervisor host), and creates aCloudHypervisorMachinecarrying the resolved spec. -
The Cloud Hypervisor provider, running on that host, sees the new
CloudHypervisorMachine, prepares the guest's disk and seed, starts its VMM as a systemd unit under the guest's own uid, and writes status (Ready=true, addresses, provider ID) back onto theCloudHypervisorMachine. On vSphere the same step is aVSphereMachineand a clone in vCenter. -
The banlieue controller sees that status and mirrors it onto
VirtualMachine.statusso the user cankubectl get vm db-prod-01and seeREADY=true.
If the user later changes placement.providerSelector to match a different
Provider, the same sequence happens — but now that Provider's backend
controller picks up the work, on the same Kubernetes API, with the same
status contract. Every backend has its infrastructure CR and reconciler, so
the swap works between any two of them; see
Project status.
What "fundamentally" buys you¶
Because everything below the user's CR is uniform and pluggable:
- Swap a backend by changing one field (
placement.providerSelector). - Mix backends in one cluster (sandboxes and pools on Cloud Hypervisor
hosts, prod on vSphere, dev on libvirt, edge on Proxmox), addressed by the
same
kind: VirtualMachine. - Add a new backend by writing a provider once; every existing
VirtualMachinebecomes deployable there with no manifest change. - Audit, RBAC, GitOps, OPA, dashboards, etc. all work the same way they work for any Kubernetes resource — no special tooling.
These properties are not features bolted onto banlieue. They are consequences of two design choices: the abstraction principle and the CRD-only contract. If you only have time to read two pages of Why banlieue?, read those.
Warm pools and single-use sandboxes¶
One class of VM cannot be provisioned on demand at all. A disk sealed to a per-VM vTPM can only be produced by letting the guest install itself on first boot with its own TPM attached (ADR-0040), which costs minutes — and the slowness is the security property, so no provisioning cleverness removes it. The only way to hand such a VM over quickly is to have installed it already.
Two more kinds cover that, built on the same uniform substrate as everything above:
VirtualMachinePoolmaintains a warm set: it refills as members leave, reaps ones poisoned mid-install, and rolls the set when the image is rebuilt. It creates nothing but ordinaryVirtualMachines — which is why there is noVSpherePoolorLibvirtPooland never will be.VirtualMachineClaimhands one member to one subject, once. There is no unbind and no return-to-pool: the VM itself is the isolation boundary between subjects, so release is always deletion, and deleting a claim blocks until the backend VM is really gone.
That pair is what makes banlieue usable as a sandbox substrate for CI jobs and
AI agents. Both are implemented and validated end-to-end against a real libvirt
host. The claim is the top of banlieue's API
(ADR-0097):
agent-specific layers such as AgentSandbox live in
mediatore.
What banlieue is not¶
- Not a hypervisor — it does not run VMs, it drives existing hypervisors.
- Not Kubevirt — VMs do not run as pods on K8s nodes.
- Not CAPI — banlieue's
VirtualMachineis not aclusterv1.Machine; the two coexist and even share providers, but banlieue's user-facing API is independent. - Not generic — it is opinionated about one contract (VMs), not a framework for modelling arbitrary resources.
See Comparisons and Non-goals for the full version.
Where to go from here¶
- Why banlieue? — the long-form argument.
- Architecture — the wiring, watches, and reconcile flow in depth.
- Provider Model — what a provider looks like and how to write one.
- Guides — install the controller and the vSphere provider on a real cluster.
- VirtualMachine Pools and Claims — warm capacity, and handing one member to one subject exactly once.