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.

KeyTypeDefaultCLI flagEnvNotes
node_idu641--node-idFDS_V3_NODE_IDMust be > 0 and unique within the cluster.
listenString0.0.0.0:4437--listenFDS_V3_LISTENBind address for the public HTTP + peer gRPC listener (both share one port).
advertiseString127.0.0.1:4437--advertiseFDS_V3_ADVERTISEAddress other nodes use to reach this one. Must be reachable from every peer.
data_dirPathdata--data-dirFDS_V3_DATA_DIRLocal directory for hot RocksDB, WAL, snapshots, and cold cache.
hot_payload_backendrocksdb | memoryrocksdb--hot-payload-backendFDS_V3_HOT_PAYLOAD_BACKENDmemory is intended for tests; production should stay on rocksdb.
write_only_leaderboolfalse(none)(none)When true, reads that land on the leader are redirected to a random follower while one is available.

[cluster]

Membership and bootstrap.

KeyTypeDefaultCLI flagEnvNotes
nameOption<String>(none)(none)FDS_V3_CLUSTER_NAMELogical cluster label. Informational.
bootstrap_node_idu64own node_id in single-node mode; required otherwise--bootstrap-node-idFDS_V3_BOOTSTRAP_NODE_IDThe only node allowed to initialize the cluster when no initialized peer is found.
routesVec<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.

KeyTypeDefaultEnvNotes
heartbeat_interval_msu64500FDS_V3_RAFT_HEARTBEAT_INTERVAL_MSLeader → follower heartbeat cadence.
election_timeout_min_msu641500FDS_V3_RAFT_ELECTION_TIMEOUT_MIN_MSLower bound of the randomized election timeout.
election_timeout_max_msu643000FDS_V3_RAFT_ELECTION_TIMEOUT_MAX_MSUpper bound. Must be greater than the lower bound.
install_snapshot_timeout_msu64120000FDS_V3_RAFT_INSTALL_SNAPSHOT_TIMEOUT_MSSnapshot-install RPC deadline. Raise for large snapshots or slow networks.

[cold]

Cold storage backend for flushed chunks and snapshot blobs.

KeyTypeDefaultEnvNotes
backendfs | s3fsURSULA_COLD_BACKENDMulti-node clusters must use s3 (or an S3-compatible backend).
file_cache_max_bytesOption<u64>(none)URSULA_COLD_FILE_CACHE_MAX_BYTESOptional cap on the on-disk cold cache. Unset means no cap.
s3_bucketOption<String>(none)URSULA_COLD_S3_BUCKETRequired when backend = "s3".
s3_regionOption<String>(none)URSULA_COLD_S3_REGIONRequired when backend = "s3".
s3_endpointOption<String>(none)URSULA_COLD_S3_ENDPOINTSet for S3-compatible stores (MinIO, R2, …). Omit for AWS.
s3_access_key_idOption<String>(none)URSULA_COLD_S3_ACCESS_KEY_IDUse $ENV_VAR references. Omit to fall back to the AWS SDK credential chain (IAM roles, etc.).
s3_secret_access_keyOption<String>(none)URSULA_COLD_S3_SECRET_ACCESS_KEYSame handling as s3_access_key_id.
s3_session_tokenOption<String>(none)URSULA_COLD_S3_SESSION_TOKENOptional 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.

EnvTypePurpose
FDS_V3_WRITE_BACKPRESSURE_MAX_HOT_BYTESusizeCluster-wide hot-payload byte ceiling. Exceeding it returns 429 with Retry-After.
FDS_V3_WRITE_BACKPRESSURE_TARGET_HOT_BYTESusizeTarget 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_BYTESusizePer-stream hot byte ceiling.
FDS_V3_WRITE_BACKPRESSURE_MAX_FLUSH_BACKLOG_BYTESusizeMaximum bytes queued for cold flush before backpressure kicks in.
FDS_V3_WRITE_BACKPRESSURE_MAX_APPLY_LAG_ENTRIESu64Backpressure trigger when the apply queue falls behind committed log entries.
FDS_V3_WRITE_BACKPRESSURE_RETRY_AFTER_SECSu64Retry-After value returned to clients on backpressure.
FDS_V3_PUBLIC_REQUEST_MAX_INFLIGHTusizeHard cap on concurrent public HTTP requests.
FDS_V3_EXTERNAL_APPEND_ENABLEDboolEnables the external-append fast path.
FDS_V3_EXTERNAL_APPEND_MIN_PAYLOAD_BYTESusizeMinimum 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.