Deploy on Kubernetes
Run Ursula on Kubernetes with the Helm chart.
The Ursula Helm chart starts a static-membership Raft cluster with stable
StatefulSet pod identities, durable per-pod Raft logs, a headless peer Service,
a client/admin Service, a quorum-protecting PodDisruptionBudget, and a Helm test
powered by ursulactl. The default chart also adds a stateless gateway for
single-URL external access and soft multi-pod spread hints.
The chart is for fresh static-membership clusters. It does not perform online Raft voter expansion, voter removal, leader handoff during Kubernetes rolling updates, or post-bootstrap membership flag mutation. Those operations belong to the future Ursula operator workflow.
Build an image
docker build -t ursula:dev .
For kind:
kind load docker-image ursula:dev
Install
For a local image loaded into the cluster:
helm install ursula charts/ursula \
--set global.image.repository=ursula \
--set global.image.tag=dev \
--set global.image.pullPolicy=Never
The default install starts three voters and 64 Raft groups. For a registry
image, set global.image.repository, global.image.tag, and optionally
global.imagePullSecrets.
Verify readiness
helm test ursula
The test mounts the chart-generated cluster-manifest.json and runs
ursulactl wait-ready. It succeeds only when every configured node reports the
expected Raft group count and every group has a leader.
Finish bootstrap
Fresh clusters start with raft.initMembershipPerGroup=true so Ursula can
initialize per-group Raft membership automatically. After helm test passes,
update your values and run:
helm upgrade ursula charts/ursula \
--reuse-values \
--set raft.initMembershipPerGroup=false
If you do not use --reuse-values, repeat your image overrides or use a values
file so the upgrade does not roll pods back to the chart default image.
Keep this value false for normal restarts and upgrades. A future Kubernetes operator will own this transition automatically.
Static membership
server.replicaCount controls the initial voter set for a fresh cluster. The
chart supports 1, 3, and 5; production clusters should use 3 or 5.
Changing server.replicaCount on an initialized cluster is not safe Raft voter
reconfiguration. Safe scaling requires an operator workflow that adds learners,
waits for catch-up, promotes voters, and removes old voters.
Cold storage
Cold storage is disabled by default. Production multi-node clusters should use S3 or an S3-compatible object store:
s3:
bucket: my-ursula-bucket
region: us-east-1
prefix: ursula-prod
coldStorage:
enabled: true
Prefer workload identity through ServiceAccount annotations instead of static S3 credentials:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/ursula-s3
For MinIO or another S3-compatible backend, set s3.endpoint.
Operational endpoints
Ursula exposes a single admin endpoint on the client plane:
GET /__ursula/metrics— per-node JSON snapshot with Raft group state, hot bytes, cold backpressure counters, and per-core mailbox depth.
There are no dedicated /__ursula/healthz or /__ursula/readyz probes yet.
Server HTTP probes are disabled in the current chart. Gateway probes use TCP
socket checks, and cluster-level readiness should come from helm test or
ursulactl wait-ready.
Access locally
kubectl port-forward svc/ursula 4437:4437
curl http://127.0.0.1:4437/__ursula/metrics
Upgrade limits
Until the operator exists, Kubernetes StatefulSet rolling updates do not
transfer leaders, coordinate applied-index catch-up, or mutate Raft membership.
Use ursulactl restart manually for drain-aware rolling restarts when you need
operationally safe restarts on an initialized cluster.