Configuration Reference¶
Three layers, later wins: defaults → JSON config file (--config) → environment variables → CLI flags. In Kubernetes the chart handles all of it; this page is the full map for when you need to reach in.
Environment variables¶
Every variable, with the compiled-in default when unset. (This table is generated by reading internal/platform/config/env.go, not by wishful thinking.)
HTTP¶
| Variable | Default | Notes |
|---|---|---|
NARAD_HTTP_ADDR |
:7942 |
Client API + node RPC (QUIC shares the port) |
NARAD_HTTP_READ_TIMEOUT |
10s |
|
NARAD_HTTP_WRITE_TIMEOUT |
30s |
Must exceed MAX_CONSUME_WAIT or long-polls die mid-wait |
NARAD_HTTP_IDLE_TIMEOUT |
60s |
|
NARAD_HTTP_SHUTDOWN_GRACE |
10s |
Drain window on SIGTERM |
NARAD_HTTP_MAX_CONSUME_WAIT |
10s |
Server-side ceiling on ?wait= long-polls |
NARAD_HTTP_PPROF_ADDR |
off | e.g. :6060; unauthenticated; keep it cluster-internal |
Cluster¶
| Variable | Default | Notes |
|---|---|---|
NARAD_NODE_ID |
pod/host name | The node's identity; must appear in the peer list |
NARAD_CLUSTER_ADDR |
:7943 |
Raft bind address |
NARAD_CLUSTER_PEERS |
(none) | id@host:7943,…: the full voter list, identical on every node |
NARAD_CLUSTER_INITIAL_MEMBERS |
empty | IDs allowed to bootstrap; everyone else joins. Empty = legacy "all bootstrap" |
NARAD_CLUSTER_SECRET |
(none) | Shared secret gating all node-to-node QUIC RPC |
NARAD_CLUSTER_TLS_CERT_FILE / _KEY_FILE / _CA_FILE |
off | Mutual TLS for Raft; all three or nothing |
Storage & data¶
| Variable | Default | Notes |
|---|---|---|
NARAD_DATA_DIR |
data |
Everything lives here: topics/, ingress/, metastore/ |
The interesting storage knobs (codec, fsync mode, flush/sync tuning, segment size) are file-only; see the config file section below.
Topic defaults¶
Applied when a topic-create omits the field; existing topics keep their values.
| Variable | Default |
|---|---|
NARAD_TOPIC_DEFAULT_PARTITIONS |
3 |
NARAD_TOPIC_MAX_PARTITIONS |
108 |
NARAD_TOPIC_DEFAULT_RETENTION_AGE_MS |
604800000 (7 days) |
NARAD_TOPIC_DEFAULT_VISIBILITY_TIMEOUT_MS |
30000 |
NARAD_TOPIC_DEFAULT_MAX_IN_FLIGHT_PER_PARTITION |
1024 |
NARAD_TOPIC_DEFAULT_MAX_ACKED_AHEAD_PER_PARTITION |
1024 |
Fan-out¶
| Variable | Default | Notes |
|---|---|---|
NARAD_FANOUT_MAX_BATCH_RECORDS |
4096 |
Records per cursor batch |
NARAD_FANOUT_MAX_BATCH_BYTES |
4194304 (4 MiB) |
|
NARAD_FANOUT_LINGER_MS |
25 |
How long a partial batch waits to fatten up |
Logging & security¶
| Variable | Default | Notes |
|---|---|---|
NARAD_LOG_LEVEL |
info |
debug is chatty, in a good way |
NARAD_LOG_FORMAT |
json |
or text for humans |
NARAD_SECURITY_ENABLED |
true |
Secure by default; local dev can opt out |
NARAD_ADMIN_PASSWORD |
random, logged once | Seeds the root admin on first cluster start |
The config file (--config narad.json)¶
For everything not worth an env var: mostly the storage engine. The chart renders narad.config values into this file. Full shape with defaults:
Storage accepts exactly four keys; everything else (fsync mode, flush/sync cadence, segment sizing) is an engine internal with production defaults, and the loader rejects any attempt to set it:
{
"storage": {
"data_dir": "data",
"codec": "none", // "none" | "zstd" (yes, OFF by default)
"compression_level": "fastest", // zstd: fastest | default | better | best
"idle_log_eviction_ms": 1800000 // close logs untouched this long; 0 disables
},
"http": { "...": "same knobs as the env vars" },
"topic": { "...": "same as env" },
"fanout": { "...": "same as env" },
"log": { "level": "info", "format": "json" },
"security": { "enabled": true }
}
Secrets (NARAD_CLUSTER_SECRET, NARAD_ADMIN_PASSWORD) are deliberately not file-configurable: files end up in git, and git ends up on the internet.
Idle topics cost (almost) nothing¶
An open partition log holds two goroutines, open file descriptors, and
buffers, and topics people create and abandon would hold them forever.
So Narad closes any partition log untouched for idle_log_eviction_ms
(default 30 minutes) and reopens it lazily, invisibly, on the next
produce, consume, or replay. Details that make this safe:
- A topic that was never used opens nothing anywhere: creation is just a metastore entry.
- Metrics polls never keep a log warm (they observe without opening), and a fan-out child that is attached but silent doesn't either: its cursor checks the durable high-watermark file instead of the log. Committed backlog always forces the log open: correctness first.
- Eviction waits until retention has finished deleting aged segments, so closing never strands data the reaper still owes you.
- Watch it:
narad_open_partition_logsandnarad_idle_logs_evicted_total.
The upshot: creating short-lived topics and forgetting to delete them is rude but free. Deleting them is still nicer: metastore entries and the last active segment on disk stay until you do.
The fsync knob, honestly explained¶
"fsync": "batched" (the default) does not weaken the durability contract you care about: a produce is fsynced in the ingress WAL before its 202, and a partition commit is fsynced + CRC-verified before it's acknowledged back or made visible, always, in both modes. The knob only controls how eagerly background flusher batches hit disk between those hard points. per_write syncs every flushed batch; it buys you almost nothing and costs you a lot of IOPS. Leave it.
Tuning cheat sheet¶
| You want | Change |
|---|---|
| Less disk | codec: zstd (seriously, just turn it on) |
| Longer long-polls | max_consume_wait (and raise write_timeout to match) |
| Fatter fan-out batches on slow disks | raise fanout.linger_ms |
| Faster delay-child metadata refresh | you don't; the engine self-paces (30s max wake) |
| More retention granularity | smaller segment_bytes: more files, finer reaping |