Deploy a cluster

Run a three-node Ursula cluster with Raft replication and shared cold storage.

A production-style Ursula deployment runs multiple voters and a shared cold backend. The common shape is three nodes: one leader at a time, two followers, quorum-committed writes, and follower-capable reads.

Topology

Use one config per node. Each node needs:

  • a stable server.node_id
  • a server.listen address for local binding
  • a server.advertise address reachable by other nodes
  • a persistent server.data_dir
  • the same cluster.bootstrap_node_id
  • cluster.routes containing the other nodes' advertised addresses
  • a shared cold backend such as S3

Node 1

version = 1

[server]
node_id = 1
listen = "0.0.0.0:4437"
advertise = "10.0.0.1:4437"
data_dir = "/var/lib/ursula"

[cluster]
name = "prod"
bootstrap_node_id = 1
routes = ["10.0.0.2:4437", "10.0.0.3:4437"]

[cold]
backend = "s3"
s3_bucket = "ursula-prod"
s3_region = "us-east-1"

Node 2

version = 1

[server]
node_id = 2
listen = "0.0.0.0:4437"
advertise = "10.0.0.2:4437"
data_dir = "/var/lib/ursula"

[cluster]
name = "prod"
bootstrap_node_id = 1
routes = ["10.0.0.1:4437", "10.0.0.3:4437"]

[cold]
backend = "s3"
s3_bucket = "ursula-prod"
s3_region = "us-east-1"

Node 3

version = 1

[server]
node_id = 3
listen = "0.0.0.0:4437"
advertise = "10.0.0.3:4437"
data_dir = "/var/lib/ursula"

[cluster]
name = "prod"
bootstrap_node_id = 1
routes = ["10.0.0.1:4437", "10.0.0.2:4437"]

[cold]
backend = "s3"
s3_bucket = "ursula-prod"
s3_region = "us-east-1"

Start each node:

./ursula serve --config /etc/ursula/ursula.toml

For managed cluster operations, use ursulactl. It can inspect cluster state, deploy the orchestrator service, bootstrap membership, run rolling upgrades, migrate nodes, and wait for operations to complete.

Bootstrap behavior

When no initialized peer is found, only cluster.bootstrap_node_id may initialize the cluster. Other nodes wait for an initialized peer or for the control plane to add them.

Cluster mode rejects cold.backend = "fs" because each node must be able to recover shared cold chunks and snapshots.