Run locally
Ursula's HTTP server is the ursula binary. It takes its configuration from CLI flags and a small set of environment variables - there is no config file yet.
Default (in-process, non-replicated)
With no storage flag, Ursula runs an in-process non-replicated default runtime (no Raft at all). Nothing is persisted. Best for state-machine smoke tests and first-impression testing:
cargo run --bin ursula
It binds 127.0.0.1:4437, picks a core count from your CPU, and uses the in-memory engine.
In-memory Raft
OpenRaft with an in-memory log. Fast iteration, no persistence, but the runtime exercises the real Raft path:
cargo run --bin ursula -- --storage-backend memory
Disk-backed single node
Persistent local storage. Streams survive a restart:
cargo run --bin ursula -- --storage-backend disk --disk-path ./data
The --disk-path directory will contain a raft-log/ subdirectory with protobuf-framed Raft log records per group plus a shared journal.bin per core. Removing the directory wipes state cleanly.
Smoke test
curl -X PUT http://127.0.0.1:4437/demo
curl -X PUT http://127.0.0.1:4437/demo/hello
curl -X POST http://127.0.0.1:4437/demo/hello \
-H 'Content-Type: application/octet-stream' \
--data-binary 'hello'
curl 'http://127.0.0.1:4437/demo/hello?offset=-1'
Inspect runtime metrics (placement, mailbox depth, per-core counters):
curl http://127.0.0.1:4437/__ursula/metrics | jq .
Flags
| Flag | Default | Notes |
|---|---|---|
--listen ADDR | 127.0.0.1:4437 | TCP bind address |
--core-count N | available_parallelism | Worker threads (each pinned to its mailbox event loop) |
--raft-group-count N | core_count × 16 | Total Raft groups. Higher = better stream-to-group hash spread |
--storage-backend BACKEND | default runtime | memory or disk |
--disk-path DIR | none | Required for disk; Raft logs live under DIR/raft-log |
--raft-node-id ID | none | Required for static gRPC cluster mode |
--raft-peer ID=URL | none (repeatable) | Static peer list for the cluster |
--raft-cluster-config FILE | none | JSON file equivalent of --raft-node-id + --raft-peer |
--raft-init-membership[-per-group] | off | One-time membership bootstrap |
--storage-backend disk uses the durable OpenRaft log store. Static gRPC clusters use the same disk backend or memory for volatile tests.
Environment variables
A small set of knobs are read at startup:
URSULA_LIVE_READ_MAX_WAITERS_PER_CORE(default65536) - SSE waiter cap per coreURSULA_COLD_MAX_HOT_BYTES_PER_GROUP(default 64 MiB) - when a group's hot ring exceeds this, cold flush kicks inURSULA_COLD_BACKEND(memoryors3),URSULA_COLD_S3_*,URSULA_COLD_ROOT,URSULA_COLD_FLUSH_*- cold-storage configuration. See configure S3URSULA_TOKIO_CONSOLE- when set, initializes the Tokio console subscriber (requires building with--features tokio-consoleandRUSTFLAGS="--cfg tokio_unstable")
Next
- Deploy a cluster - three-node static gRPC Raft
- Configure S3 - shared cold storage
- Operations - the EC2 helper and runtime introspection