Home · Install · Hosts

battery-operator is a Kubernetes operator for battery, which keeps warm pools of flintlock MicroVMs. With it, you declare a pool of Firecracker MicroVMs and claim one the way you use anything else in a cluster:

See it work

This recording is one Kubernetes Node with KVM. A Pool fills with two Firecracker MicroVMs. A Consumer claims one with the Client Library, and runs a command in it through the Exec Agent. The Pool starts a new MicroVM in place of the one claimed. Then a claim made with kubectl binds, is renewed, and goes Expired when nobody renews it. Deleting the Pool deletes its MicroVMs.

Nothing in it is a fake: the Host runs Firecracker, flintlockd and battery’s own poolmgrd, in a Lima VM with nested virtualization on a Mac. The real hosts trial brings it up, and hack/real-hosts/demo.sh --record records it again.

How it fits together

  Kubernetes API                     Operator pod                        each Host (a Node)
 ┌─────────────────┐     ┌──────────────────────────────────┐     ┌───────────────────────────┐
 │ Pool            │◀───▶│ Pool, Claim, Inventory           │     │ Exec Agent (DaemonSet)    │
 │ MicroVMClaim    │     │ Controllers, certificate signer  │     │   checks the claim, then  │
 │ Nodes, CSRs     │     │            │ gRPC, loopback      │     │   relays exec ───────┐    │
 └─────────────────┘     │            ▼                     │     │                      ▼    │
                         │ battery poolmgrd (sidecar) ──────┼─────┼─▶ flintlockd ──▶ Firecracker
                         └──────────────────────────────────┘ mTLS└───────────────────────────┘

Try it

You need a Kubernetes cluster whose Hosts meet the Host prerequisites: KVM, containerd’s thin pool, and flintlockd with its exec API. The quickest way is the real hosts trial, which makes one Lima VM on a Mac, or any Ubuntu machine with KVM, into such a cluster:

hack/real-hosts/up.sh                     # the Host, k3s, cert-manager and the Operator
export KUBECONFIG=hack/real-hosts/.state/kubeconfig
kubectl apply -f examples/demo/demo.yaml  # a namespace, a Holder and a Pool
kubectl -n demo wait --for=jsonpath={.status.available}=2 pool/demo

The demo manifests say what each object is for.

To install a release on your own cluster, with Flux or with kubectl, see Install.

Claims are resources

Every claim is a MicroVMClaim in the cluster, so kubectl get shows which MicroVMs are held, on which Node, and until when:

apiVersion: battery.liquidmetal-x.dev/v1alpha1
kind: MicroVMClaim
metadata:
  name: build-1
  namespace: demo
spec:
  poolRef:
    name: demo
  serviceAccountName: demo-holder   # the Holder: only it can use the MicroVM
$ kubectl -n demo get microvmclaims
NAME      POOL   PHASE   NODE        EXPIRES                AGE
build-1   demo   Bound   bo-host-1   2026-09-27T20:46:09Z   6s
$ kubectl -n demo get microvmclaim build-1 -o jsonpath='{.status}' | jq '{phase, microVM, host, leaseExpiresAt}'
{
  "phase": "Bound",
  "microVM": { "uid": "01M3J9T8R6826GNNC6PJ3TDTSC" },
  "host": { "agentAddress": "192.168.5.15:10270", "nodeName": "bo-host-1" },
  "leaseExpiresAt": "2026-09-27T20:46:09Z"
}

EXPIRES is the time the claim’s Lease runs out. The Lease lasts the Pool’s lease.expiryThreshold. The holder renews it by setting spec.renewTime, and the Client Library does this for its claims. A claim nobody renews goes Expired, and battery deletes its MicroVM. Deleting a claim releases its MicroVM at once.

Claim a MicroVM from Go

A Consumer uses the Client Library, pkg/claimclient:

cl, err := claimclient.New(claimclient.Config{
	Client:    kubeClient, // a controller-runtime client, as the Holder
	Namespace: "demo",
	ServingCA: servingCA,  // the Operator's serving CA
})

// Claim blocks until the claim is Bound.
claim, err := cl.Claim(ctx, claimclient.Request{
	Pool:               "demo",
	ServiceAccountName: "demo-holder",
})
defer claim.Release(ctx)

// Dial reaches the Exec Agent on the MicroVM's Host.
conn, err := claim.Dial(ctx)

The demo’s Consumer, hack/real-hosts/smoke, is a complete example.

How it is built

Status

v1alpha1. The Operator, the Exec Agent and the Client Library work end to end on a real Host. The project lives outside battery so it can be proven without disturbing battery, with the aim of being adopted by liquidmetal-dev. The design was discussed in liquidmetal-dev/battery#46.