Configuration
Every Ursula configuration key, with TOML sections, environment variables, CLI flags, and precedence.
Ursula reads configuration from four layers, with later layers overriding earlier ones:
defaults < TOML file < environment variables < CLI flags
Pass a TOML config file with --config /path/to/ursula.toml. Without --config, Ursula uses defaults + environment + CLI.
Secret references in TOML
Any string value in the [cold] section may use a $ENV_VAR reference, which is resolved from the process environment at startup. If the referenced variable is missing, startup fails. Ursula never runs with a silently-incomplete credential.
[cold]
s3_access_key_id = "$URSULA_AWS_ACCESS_KEY_ID"
s3_secret_access_key = "$URSULA_AWS_SECRET_ACCESS_KEY"
[server]
Node identity, network bindings, and local storage.
| Key | Type | Default | CLI flag | Env | Notes |
|---|---|---|---|---|---|
node_id | u64 | 1 | --node-id | FDS_V3_NODE_ID | Must be > 0 and unique within the cluster. |
listen | String | 0.0.0.0:4437 | --listen | FDS_V3_LISTEN | Bind address for the public HTTP + peer gRPC listener (both share one port). |
advertise | String | 127.0.0.1:4437 | --advertise | FDS_V3_ADVERTISE | Address other nodes use to reach this one. Must be reachable from every peer. |
data_dir | Path | data | --data-dir | FDS_V3_DATA_DIR | Local directory for hot RocksDB, WAL, snapshots, and cold cache. |
hot_payload_backend | rocksdb | memory | rocksdb | --hot-payload-backend | FDS_V3_HOT_PAYLOAD_BACKEND | memory is intended for tests; production should stay on rocksdb. |
write_only_leader | bool | false | (none) | (none) | When true, reads that land on the leader are redirected to a random follower while one is available. |
[cluster]
Membership and bootstrap.
| Key | Type | Default | CLI flag | Env | Notes |
|---|---|---|---|---|---|
name | Option<String> | (none) | (none) | FDS_V3_CLUSTER_NAME | Logical cluster label. Informational. |
bootstrap_node_id | u64 | own node_id in single-node mode; required otherwise | --bootstrap-node-id | FDS_V3_BOOTSTRAP_NODE_ID | The only node allowed to initialize the cluster when no initialized peer is found. |
routes | Vec<String> | [] | --route (repeatable) | URSULA_CLUSTER_ROUTES_JSON (JSON array) | Advertised addresses of the other peers. Non-empty routes enables multi-node mode. |
In multi-node mode, cold.backend = "fs" is rejected. Every node must be able to recover shared cold chunks and snapshots.
[raft]
OpenRaft consensus timing. Defaults are tuned for typical LAN/AZ deployments; tune for cross-region.
| Key | Type | Default | Env | Notes |
|---|---|---|---|---|
heartbeat_interval_ms | u64 | 500 | FDS_V3_RAFT_HEARTBEAT_INTERVAL_MS | Leader → follower heartbeat cadence. |
election_timeout_min_ms | u64 | 1500 | FDS_V3_RAFT_ELECTION_TIMEOUT_MIN_MS | Lower bound of the randomized election timeout. |
election_timeout_max_ms | u64 | 3000 | FDS_V3_RAFT_ELECTION_TIMEOUT_MAX_MS | Upper bound. Must be greater than the lower bound. |
install_snapshot_timeout_ms | u64 | 120000 | FDS_V3_RAFT_INSTALL_SNAPSHOT_TIMEOUT_MS | Snapshot-install RPC deadline. Raise for large snapshots or slow networks. |
[cold]
Cold storage backend for flushed chunks and snapshot blobs.
| Key | Type | Default | Env | Notes |
|---|---|---|---|---|
backend | fs | s3 | fs | URSULA_COLD_BACKEND | Multi-node clusters must use s3 (or an S3-compatible backend). |
file_cache_max_bytes | Option<u64> | (none) | URSULA_COLD_FILE_CACHE_MAX_BYTES | Optional cap on the on-disk cold cache. Unset means no cap. |
s3_bucket | Option<String> | (none) | URSULA_COLD_S3_BUCKET | Required when backend = "s3". |
s3_region | Option<String> | (none) | URSULA_COLD_S3_REGION | Required when backend = "s3". |
s3_endpoint | Option<String> | (none) | URSULA_COLD_S3_ENDPOINT | Set for S3-compatible stores (MinIO, R2, …). Omit for AWS. |
s3_access_key_id | Option<String> | (none) | URSULA_COLD_S3_ACCESS_KEY_ID | Use $ENV_VAR references. Omit to fall back to the AWS SDK credential chain (IAM roles, etc.). |
s3_secret_access_key | Option<String> | (none) | URSULA_COLD_S3_SECRET_ACCESS_KEY | Same handling as s3_access_key_id. |
s3_session_token | Option<String> | (none) | URSULA_COLD_S3_SESSION_TOKEN | Optional STS session token. |
For production, prefer the AWS SDK credential chain (omit the explicit s3_* credential keys and rely on instance roles or AWS_* environment variables) so credentials never live on disk.
Runtime tuning (environment-only)
These knobs aren't in TOML; they're tuning levers exposed as environment variables. Most operators won't need to touch them. 0 disables the threshold.
| Env | Type | Purpose |
|---|---|---|
FDS_V3_WRITE_BACKPRESSURE_MAX_HOT_BYTES | usize | Cluster-wide hot-payload byte ceiling. Exceeding it returns 429 with Retry-After. |
FDS_V3_WRITE_BACKPRESSURE_TARGET_HOT_BYTES | usize | Target hot bytes after backpressure recovery. Defaults to 80% of MAX_HOT_BYTES; clamped to 1..=MAX_HOT_BYTES. |
FDS_V3_WRITE_BACKPRESSURE_MAX_STREAM_HOT_BYTES | usize | Per-stream hot byte ceiling. |
FDS_V3_WRITE_BACKPRESSURE_MAX_FLUSH_BACKLOG_BYTES | usize | Maximum bytes queued for cold flush before backpressure kicks in. |
FDS_V3_WRITE_BACKPRESSURE_MAX_APPLY_LAG_ENTRIES | u64 | Backpressure trigger when the apply queue falls behind committed log entries. |
FDS_V3_WRITE_BACKPRESSURE_RETRY_AFTER_SECS | u64 | Retry-After value returned to clients on backpressure. |
FDS_V3_PUBLIC_REQUEST_MAX_INFLIGHT | usize | Hard cap on concurrent public HTTP requests. |
FDS_V3_EXTERNAL_APPEND_ENABLED | bool | Enables the external-append fast path. |
FDS_V3_EXTERNAL_APPEND_MIN_PAYLOAD_BYTES | usize | Minimum payload size that takes the external-append path. |
The legacy env names FDS_V3_AUTO_FLUSH_MAX_HOT_BYTES, FDS_V3_AUTO_FLUSH_TARGET_HOT_BYTES, and FDS_V3_AUTO_FLUSH_RETRY_AFTER_SECS are still honored as aliases.
Minimal examples
Single node, filesystem cold:
version = 1
[server]
node_id = 1
listen = "127.0.0.1:4437"
advertise = "127.0.0.1:4437"
data_dir = "data"
[cold]
backend = "fs"
Three-node cluster, S3 cold (one file per node, with node_id, advertise, and routes rotated accordingly):
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"
See Deploy a cluster and Configure S3 for fuller walk-throughs.