Skip to content

Environment Variables Reference

Complete reference for all Felix environment variables, organized by category.

Felix uses environment variables prefixed with FELIX_ for configuration. These variables provide quick overrides without modifying config files.

Priority: Environment variables override built-in defaults but are overridden by YAML config files.

Description: QUIC listener bind address and port (UDP).

Type: SocketAddr format

Default: 0.0.0.0:5000

Example:

Terminal window
export FELIX_QUIC_BIND="0.0.0.0:5000"
export FELIX_QUIC_BIND="127.0.0.1:5001" # Localhost only
export FELIX_QUIC_BIND="10.0.1.5:5000" # Specific interface

Notes:

  • Must be a valid IP:Port combination
  • UDP port for QUIC transport
  • Use 0.0.0.0 to bind all interfaces
  • With FELIX_QUIC_LISTENERS above 1, this is the first port of a consecutive run

Description: How many client-facing QUIC listeners to bind, on consecutive ports starting at FELIX_QUIC_BIND.

Type: Integer (at least 1)

Default: 1

Example:

Terminal window
export FELIX_QUIC_BIND="0.0.0.0:5000"
export FELIX_QUIC_LISTENERS=4 # binds 5000, 5001, 5002, 5003

Notes:

  • Why it exists: one UDP socket is one QUIC endpoint, and that endpoint’s driver is a single task that reads every inbound datagram and routes it by connection id. It cannot use more than one core, and it is the per-broker throughput ceiling — measured at ~88% of one core while the rest of the machine idled. Separate ports are separate sockets, which are separate drivers.
  • The broker advertises the port set during authentication, and a client spreads its connection pools across it. A client that predates this ignores the advertisement and keeps using the single address it dialled.
  • Every port in the range must be open in firewalls, security groups and service definitions — not just FELIX_QUIC_BIND.
  • FELIX_INTERNAL_BIND must sit outside the range. Startup fails if it does not, since peer traffic and client traffic must not share a listener.
  • Startup also fails if the range would run past port 65535, rather than binding fewer listeners than asked for.
  • Adding brokers remains the horizontal lever; this raises what one broker can do before you need another.
  • Do not pin FELIX_IO_RUNTIME_THREADS below one runtime per listener plus one. It is derived from this setting, and a pool too small puts every listener’s driver back on a single thread. Startup refuses that combination.

Description: Write the broker’s generated self-signed certificate to this path (PEM) at startup, so clients can trust it explicitly.

Type: File path

Default: unset — no certificate is written

Example:

Terminal window
export FELIX_TLS_CERT_EXPORT="/tmp/felix-dev-ca.pem"

Notes:

  • Development only. The broker generates a self-signed certificate at startup; without exporting it, the only way for a non-Rust client to connect is to skip verification entirely, which is a habit worth not forming.
  • Point a client at the file: the Python client takes ca_file=, and other clients take whatever their TLS stack calls a CA bundle.
  • Startup fails if the file cannot be written. A deployment that asked for the export has clients configured to read it, and coming up without it turns into connection failures far from their cause.
  • Not a substitute for real certificates. Operator-supplied broker certificates are not wired up yet.

Description: HTTP metrics and health endpoint bind address.

Type: SocketAddr format

Default: 0.0.0.0:8080

Example:

Terminal window
export FELIX_BROKER_METRICS_BIND="0.0.0.0:8080"

Exposed endpoints:

  • /healthz: Health check
  • /metrics: Prometheus metrics (when telemetry enabled)

Description: Control plane base URL for metadata synchronization.

Type: String (URL)

Default: None

Example:

Terminal window
export FELIX_CONTROLPLANE_URL="http://felix-controlplane:8443"
export FELIX_CONTROLPLANE_URL="https://cp.example.com:8443"

Usage:

  • Optional for single-node deployments
  • Required for multi-broker clusters
  • Include scheme (http:// or https://)

Description: Control plane polling interval in milliseconds.

Type: Unsigned integer

Default: 2000

Example:

Terminal window
export FELIX_CONTROLPLANE_SYNC_INTERVAL_MS="2000"
export FELIX_CONTROLPLANE_SYNC_INTERVAL_MS="500" # Fast polling
export FELIX_CONTROLPLANE_SYNC_INTERVAL_MS="10000" # Slow polling

Description: Enable publish acknowledgements after commit.

Type: Boolean

Default: false

Accepted values: 1, true, yes (case-insensitive) = enabled

Example:

Terminal window
export FELIX_ACK_ON_COMMIT="true"
export FELIX_ACK_ON_COMMIT="1"
export FELIX_ACK_ON_COMMIT="yes"

Trade-off:

  • false: Fire-and-forget, lower latency
  • true: Explicit acks, higher latency guarantee

Description: Maximum frame size accepted on QUIC streams.

Type: Positive integer (bytes)

Default: 16777216 (16 MiB)

Example:

Terminal window
export FELIX_MAX_FRAME_BYTES="16777216" # 16 MiB
export FELIX_MAX_FRAME_BYTES="33554432" # 32 MiB
export FELIX_MAX_FRAME_BYTES="8388608" # 8 MiB

Notes:

  • Value of 0 uses default
  • Affects max message size
  • Must align with client configuration

Description: Maximum wait time when publish queue is full.

Type: Positive integer (milliseconds)

Default: 2000

Example:

Terminal window
export FELIX_PUBLISH_QUEUE_WAIT_MS="2000"
export FELIX_PUBLISH_QUEUE_WAIT_MS="5000" # More patient
export FELIX_PUBLISH_QUEUE_WAIT_MS="500" # Fail fast

Behavior:

  • Publisher blocks if queue full
  • Returns error after timeout
  • Backpressure mechanism

Description: Maximum wait time for ack-on-commit completion.

Type: Positive integer (milliseconds)

Default: 2000

Example:

Terminal window
export FELIX_ACK_WAIT_TIMEOUT_MS="2000"

Notes:

  • Only relevant when FELIX_ACK_ON_COMMIT=true
  • Publisher gets error if timeout exceeded

Description: Maximum events per subscription batch frame.

Type: Positive integer (count)

Default: 64

Example:

Terminal window
export FELIX_EVENT_BATCH_MAX_EVENTS="64"
export FELIX_EVENT_BATCH_MAX_EVENTS="1" # No batching
export FELIX_EVENT_BATCH_MAX_EVENTS="256" # Large batches

Tuning:

  • Small values (1-16): Low latency
  • Medium values (32-64): Balanced
  • Large values (128-256): High throughput

Description: Maximum bytes per subscription batch frame.

Type: Positive integer (bytes)

Default: 65536 (64 KiB)

Example:

Terminal window
export FELIX_EVENT_BATCH_MAX_BYTES="65536" # 64 KiB (default)
export FELIX_EVENT_BATCH_MAX_BYTES="524288" # 512 KiB
export FELIX_EVENT_BATCH_MAX_BYTES="1048576" # 1 MiB

Notes:

  • Batch sent when event count OR byte limit reached
  • Adjust based on typical message size

Description: Maximum delay before flushing batch (microseconds).

Type: Unsigned integer

Default: 250

Example:

Terminal window
export FELIX_EVENT_BATCH_MAX_DELAY_US="250"
export FELIX_EVENT_BATCH_MAX_DELAY_US="50" # Ultra-low latency
export FELIX_EVENT_BATCH_MAX_DELAY_US="1000" # Prioritize batching
export FELIX_EVENT_BATCH_MAX_DELAY_US="5000" # Maximum batching

Critical tuning parameter:

  • Lower: Reduced latency, more frequent sends
  • Higher: Better batching, higher latency
  • Typical range: 50-1000 microseconds

Description: Subscribers to process in parallel during fanout.

Type: Positive integer (count)

Default: 64

Example:

Terminal window
export FELIX_FANOUT_BATCH="64"
export FELIX_FANOUT_BATCH="128" # High fanout
export FELIX_FANOUT_BATCH="16" # Low fanout

Recommendations:

  • Match to typical subscriber count
  • Higher values for high-fanout streams
  • Lower values reduce concurrency overhead

Subscription event delivery uses binary EventBatch frames by default.

Description: Per-subscriber queue capacity in broker core.

Type: Positive integer (count)

Default: 512

Terminal window
export FELIX_SUBSCRIBER_QUEUE_CAPACITY="512"
# Alias (same behavior):
export FELIX_SUB_QUEUE_CAPACITY="512"

Description: Max concurrent subscriptions a single QUIC connection may hold. Independent of FELIX_SUBSCRIBER_QUEUE_CAPACITY (which bounds one subscription’s buffer size) — this bounds how many subscriptions a connection can open in total, protecting broker memory from a connection that opens unbounded subscriptions.

Type: Positive integer (count)

Default: 4096

Terminal window
export FELIX_MAX_SUBSCRIPTIONS_PER_CONN="4096"

Description: Backpressure policy when broker subscriber queues are full.

Type: Enum (block, drop_new, drop_old)

Default: drop_new

Terminal window
export FELIX_SUB_QUEUE_POLICY="drop_new"

Policy notes:

  • block: await queue space (strongest delivery guarantee, may reduce publish throughput).
  • drop_new: drop incoming item when queue is full.
  • drop_old: currently emulated with drop_new semantics and tracked separately.

Description: Keep all subscribers on the same QUIC connection on one writer lane.

Type: Boolean (1|true|yes to enable)

Default: false

Terminal window
export FELIX_SUB_SINGLE_WRITER_PER_CONN="true"

Description: Requested outbound subscriber writer lanes.

Type: Positive integer (count)

Default: 4

Terminal window
export FELIX_SUB_WRITER_LANES="4"
# Alias (checked first, same behavior):
export FELIX_SUB_EGRESS_LANES="4"

Description: Queue depth per outbound writer lane.

Type: Positive integer (count)

Default: 64

Terminal window
export FELIX_SUB_LANE_QUEUE_DEPTH="64"
# Alias (same behavior):
export FELIX_SUB_QUEUE_BOUND="64"

Description: Backpressure policy for the writer-lane command queue (downstream of FELIX_SUB_QUEUE_POLICY, which gates the earlier broker-core fanout enqueue).

Type: Enum (block, drop_new, drop_old)

Default: drop_new

Terminal window
export FELIX_SUB_QUEUE_MODE="drop_new"
# Alias (same behavior):
export FELIX_SUB_LANE_QUEUE_POLICY="drop_new"

Description: Safety clamp for writer lanes.

Type: Positive integer (count)

Default: 8

Terminal window
export FELIX_MAX_SUB_WRITER_LANES="8"

Description: Outbound lane sharding policy.

Type: Enum (auto, subscriber_id_hash, connection_id_hash, round_robin_pin)

Default: auto

Terminal window
export FELIX_SUB_LANE_SHARD="auto"

Policy notes:

  • auto: prefers connection-aware routing when connection id is known.
  • subscriber_id_hash: stable by subscriber id.
  • connection_id_hash: stable by connection id.
  • round_robin_pin: pinned RR assignment per subscriber.

Description: Maximum queued lane commands drained per flush before a write is issued.

Type: Positive integer (count)

Default: 16

Terminal window
export FELIX_SUB_FLUSH_MAX_ITEMS="16"

Description: Maximum time spent waiting to fill a lane flush buffer before writing what’s accumulated.

Type: Unsigned integer (microseconds)

Default: 50

Terminal window
export FELIX_SUB_FLUSH_MAX_DELAY_US="50"

Description: Upper bound on coalesced bytes per QUIC write call to a subscriber stream.

Type: Positive integer (bytes)

Default: 65536 (64 KiB)

Terminal window
export FELIX_SUB_MAX_BYTES_PER_WRITE="65536"

Description: Number of delivery streams per connection in hashed-pool mode.

Type: Positive integer (count)

Default: 4

Terminal window
export FELIX_SUB_STREAMS_PER_CONN="4"

Description: Strategy for mapping subscribers to event streams. hashed_pool is not yet enabled — the broker falls back to per_subscriber and logs a debug warning if requested.

Type: Enum (per_subscriber, hashed_pool)

Default: per_subscriber

Terminal window
export FELIX_SUB_STREAM_MODE="per_subscriber"

Description: Number of QUIC connections in cache pool (client-side).

Type: Positive integer (count)

Default: 8

Example:

Terminal window
export FELIX_CACHE_CONN_POOL="8"
export FELIX_CACHE_CONN_POOL="16" # High concurrency
export FELIX_CACHE_CONN_POOL="4" # Low concurrency

Notes:

  • Client-side setting
  • Affects concurrent request capacity
  • Each connection can have multiple streams

Description: Cache request streams per connection (client-side).

Type: Positive integer (count)

Default: 4

Example:

Terminal window
export FELIX_CACHE_STREAMS_PER_CONN="4"
export FELIX_CACHE_STREAMS_PER_CONN="8" # More parallelism
export FELIX_CACHE_STREAMS_PER_CONN="2" # Less overhead

Tuning:

  • Total cache parallelism = pool × streams_per_conn
  • Higher values for high-concurrency workloads

Description: Cache connection flow-control receive window (broker).

Type: Positive integer (bytes)

Default: 268435456 (256 MiB)

Example:

Terminal window
export FELIX_CACHE_CONN_RECV_WINDOW="268435456" # 256 MiB
export FELIX_CACHE_CONN_RECV_WINDOW="536870912" # 512 MiB
export FELIX_CACHE_CONN_RECV_WINDOW="134217728" # 128 MiB

Memory impact:

  • Per-connection credit
  • Multiplied by connection pool size
  • Affects burst tolerance

Description: Cache stream flow-control receive window (broker).

Type: Positive integer (bytes)

Default: 67108864 (64 MiB)

Example:

Terminal window
export FELIX_CACHE_STREAM_RECV_WINDOW="67108864" # 64 MiB
export FELIX_CACHE_STREAM_RECV_WINDOW="134217728" # 128 MiB
export FELIX_CACHE_STREAM_RECV_WINDOW="33554432" # 32 MiB

Notes:

  • Per-stream credit
  • Total: stream_window × streams_per_conn × conn_pool

Description: Cache connection send window (broker).

Type: Positive integer (bytes)

Default: 268435456 (256 MiB)

Example:

Terminal window
export FELIX_CACHE_SEND_WINDOW="268435456"

Description: Concurrency level for cache benchmark (demo only).

Type: Positive integer

Default: 32

Example:

Terminal window
export FELIX_CACHE_BENCH_CONCURRENCY="32"
export FELIX_CACHE_BENCH_CONCURRENCY="64" # Stress test

Description: Number of keys for cache benchmark (demo only).

Type: Positive integer

Default: 1024

Example:

Terminal window
export FELIX_CACHE_BENCH_KEYS="1024"

Description: Number of QUIC connections for event delivery (client).

Type: Positive integer (count)

Default: 8

Example:

Terminal window
export FELIX_EVENT_CONN_POOL="8"
export FELIX_EVENT_CONN_POOL="4" # Lower overhead
export FELIX_EVENT_CONN_POOL="16" # More parallelism
# Alias used by perf scripts:
export FELIX_SUB_CONNS="8"

Description: Event connection receive window (client).

Type: Positive integer (bytes)

Default: 268435456 (256 MiB)

Example:

Terminal window
export FELIX_EVENT_CONN_RECV_WINDOW="268435456"

Description: Event stream receive window (client).

Type: Positive integer (bytes)

Default: 67108864 (64 MiB)

Example:

Terminal window
export FELIX_EVENT_STREAM_RECV_WINDOW="67108864"

Description: Event connection send window (client).

Type: Positive integer (bytes)

Default: 268435456 (256 MiB)

Example:

Terminal window
export FELIX_EVENT_SEND_WINDOW="268435456"

Description: Bounded queue capacity between client subscription IO and dispatch stages.

Type: Positive integer (count)

Default: 256

Terminal window
export FELIX_CLIENT_SUB_QUEUE_CAPACITY="256"

Description: Client-side backpressure policy for subscription pipeline queues.

Type: Enum (block, drop_new, drop_old)

Default: drop_new

Terminal window
export FELIX_CLIENT_SUB_QUEUE_POLICY="drop_new"

Description: Number of publishing QUIC connections (client).

Type: Positive integer (count)

Default: 4

Example:

Terminal window
export FELIX_PUB_CONN_POOL="4"
export FELIX_PUB_CONN_POOL="8" # More publishers

Description: Publishing streams per connection (client).

Type: Positive integer (count)

Default: 2

Example:

Terminal window
export FELIX_PUB_STREAMS_PER_CONN="2"
export FELIX_PUB_STREAMS_PER_CONN="4" # More concurrency

Description: Chunk size for publishing large messages (client).

Type: Positive integer (bytes)

Default: 16384 (16 KiB)

Example:

Terminal window
export FELIX_PUBLISH_CHUNK_BYTES="16384" # 16 KiB
export FELIX_PUBLISH_CHUNK_BYTES="32768" # 32 KiB
export FELIX_PUBLISH_CHUNK_BYTES="8192" # 8 KiB

Description: Bounded request queue depth per client publish worker.

Type: Positive integer (count)

Default: 64

Terminal window
export FELIX_PUBLISH_QUEUE_DEPTH="64"

Description: Shared queued and in-flight publish byte budget across client workers.

Type: Positive integer (bytes)

Default: 4194304 (4 MiB)

Terminal window
export FELIX_PUBLISH_INFLIGHT_BYTES="4194304"

Description: Publish workers in the broker’s pool. Despite the name the pool is process-wide, not per connection — it is built once, before the accept loop, because per-connection pools multiplied concurrent callers into shared broker state. A stream-shard handle maps to one worker (handle.id() % count), so raising this spreads different shards across more workers; it cannot give one shard more than one. The name is misleading and is tracked for a rename in #535.

Type: Positive integer (count)

Default: 4

Example:

Terminal window
export FELIX_BROKER_PUB_WORKERS_PER_CONN="4"
export FELIX_BROKER_PUB_WORKERS_PER_CONN="8" # High concurrency
export FELIX_BROKER_PUB_WORKERS_PER_CONN="2" # Lower overhead

Description: Durable publishes one publish worker may have awaiting their device flush at once (broker). Offsets are still claimed serially, in arrival order, so this does not affect the order records land in — it decides how many flushes group commit gets to coalesce. 1 restores the pre-0.4.1 behaviour of one flush at a time, which capped a shard at roughly one batch per flush (#535).

Type: Positive integer (count)

Default: 32

Example:

Terminal window
export FELIX_BROKER_PUB_FLUSH_CONCURRENCY="32"
export FELIX_BROKER_PUB_FLUSH_CONCURRENCY="64" # Deeper coalescing on fast devices
export FELIX_BROKER_PUB_FLUSH_CONCURRENCY="1" # Serialise, as before 0.4.1

Description: Per-worker publish queue depth (broker).

Type: Positive integer (count)

Default: 64

Example:

Terminal window
export FELIX_BROKER_PUB_QUEUE_DEPTH="64"
export FELIX_BROKER_PUB_QUEUE_DEPTH="256" # More buffering
export FELIX_BROKER_PUB_QUEUE_DEPTH="32" # Less memory

Description: Shared in-flight publish byte budget across all publish workers (process-wide). Bounds queued-or-processing bytes independent of FELIX_BROKER_PUB_QUEUE_DEPTH’s item count, so a handful of large payloads/batches can’t blow past the intended ingress memory budget.

Type: Positive integer (bytes)

Default: 67108864 (64 MiB)

Terminal window
export FELIX_BROKER_PUBLISH_INFLIGHT_BYTES="67108864"

Description: Per-connection share of FELIX_BROKER_PUBLISH_INFLIGHT_BYTES. Bounds how much of the shared, process-wide publish byte budget a single connection can occupy at once, so one connection publishing large batches can’t starve every other connection’s admission into the shared budget.

Type: Positive integer (bytes)

Default: 16777216 (16 MiB)

Terminal window
export FELIX_BROKER_PUBLISH_CONN_INFLIGHT_BYTES="16777216"

Description: When enabled, un-acked (fire-and-forget) publishes wait — bounded by FELIX_PUBLISH_QUEUE_WAIT_MS — for ingress capacity instead of being shed when the publish queue or byte budget is full. Backpressure then propagates through QUIC flow control to the publisher. Leave off in production for visible shedding under overload; turn on for lossless pipelines and sustainable-throughput benchmarking.

Type: Boolean (1, true, yes = enabled)

Default: disabled

Terminal window
export FELIX_PUB_INGRESS_WAIT="1"

Description: Number of core-pinned shard executors owning stream work (thread-per-core, shared-nothing). Each stream is owned by one shard: its publish worker and its subscriptions’ lane feeders run on that shard’s dedicated single-threaded runtime, pinned to a CPU core on Linux. Benefits scale with stream count; single-stream workloads serialize on one shard by design.

Type: Positive integer (count; 0 = disabled)

Default: 0

Terminal window
export FELIX_CORE_SHARDS="4"

Process-wide levers read by every Felix QUIC endpoint (broker, client, demos). See Benchmarks for measured impact.

Description: Upper bound for QUIC path-MTU discovery. Discovery converges to the real path MTU at or below it, so on a 1500-byte network the bound never binds.

Raising it above 6,553 on Linux will stall delivery. Linux UDP GSO packs a whole sendmsg batch into one IP datagram, so MTU × segments must stay under 65,535, and quinn batches up to 10. Above that the kernel rejects every batch with EMSGSIZE, which quinn does not recognise as a GSO failure — it falls back only on EIO/EINVAL — so the transmit is dropped after quinn has counted it as sent, and the stall is permanent rather than degrading. This is not a throughput preference; it is the difference between working and not.

The default was 16384 until 0.5.0, which is above that ceiling. It was harmless on a 1500-byte path and fatal on a jumbo-frame one, where discovery climbs past 6,553 — which is exactly the network you would buy for throughput. 4096 rather than the exact 6,553 because quinn’s batch size is private to it and 6,553 breaks the moment it rises; 4,096 also measured fastest on Linux. macOS has no GSO and no such limit.

Type: Positive integer (bytes, clamped to 1200–65527)

Default: 4096 (16384 on macOS)

Terminal window
export FELIX_MTU_UPPER_BOUND="4096"
export FELIX_MTU_UPPER_BOUND="16384" # macOS, or any path with no GSO

Description: Starting datagram size before path-MTU discovery completes. The RFC-safe default works everywhere; raising it on known-good paths (jumbo-frame LAN) skips the discovery ramp. Connections to a loopback peer automatically start at the loopback MTU and guarantee it, which makes the path immune to spurious black-hole collapse (see FELIX_MTU_BLACK_HOLE_COOLDOWN_MS) and, because the guarantee also freezes the discovery bound, removes probe traffic entirely.

The guaranteed size is 16,336 bytes on macOS and 4,096 elsewhere (both capped by FELIX_MTU_UPPER_BOUND). The lower cap off macOS is not conservatism. Linux UDP GSO packs a whole sendmsg batch into a single IP datagram, so MTU × segments must stay under 65,535; quinn batches up to 10, putting the real ceiling at 6,553 bytes. Above it the kernel rejects every batch and delivery stalls outright — measured as a total stall at both 8,192 and 16,336. macOS has no GSO (one syscall per datagram) and no such limit. 4,096 also measured fastest on Linux; see the performance case study.

The loopback path additionally requires the socket’s granted UDP buffers to reach ~1 MiB. That threshold is a proxy for “this host has been tuned”, not a burst-headroom calculation — hosts where Linux silently clamps SO_RCVBUF to a stock net.core.rmem_max (~208 KB) keep the RFC-safe default; raise rmem_max/wmem_max to enable it. Setting FELIX_INITIAL_MTU explicitly disables the loopback special case and applies to every path.

Type: Positive integer (bytes, clamped to 1200–65527)

Default: 1200 (loopback peers: 16336 on macOS, 4096 elsewhere)

Terminal window
export FELIX_INITIAL_MTU="1200"

Description: How long a connection waits after an MTU black-hole verdict before re-probing for a larger MTU. Quinn’s black-hole detector cannot distinguish a path that silently drops large packets from a congestive loss burst that happened to contain only full-MTU packets (which is what overflowing the peer’s UDP socket buffer looks like at high rate). A false verdict collapses the path MTU to the initial value, multiplying datagram and syscall counts per byte by ~13× on a 16 KiB-MTU path; quinn’s stock 60-second cooldown then pins that state. Felix shortens the cooldown so a spurious collapse re-probes at the connection’s next idle gap. Note that quinn only sends recovery probes when the connection has nothing else to transmit, so a sender with a continuous backlog cannot recover until its load has a gap regardless of this setting — which is why loopback connections start at full MTU instead (see FELIX_INITIAL_MTU).

Type: Positive integer (milliseconds, minimum 100)

Default: 2000

Terminal window
export FELIX_MTU_BLACK_HOLE_COOLDOWN_MS="2000"

Description: Optional initial congestion window override in bytes. By default Felix keeps Quinn’s RFC 9002 behavior, including raising the minimum window to two datagrams after path-MTU discovery. Increase only on trusted low-loss paths where a larger initial burst is acceptable.

Type: Positive integer (bytes)

Default: Quinn’s RFC 9002 default

Terminal window
export FELIX_INITIAL_CWND="1048576"

FELIX_UDP_SEND_BUFFER / FELIX_UDP_RECV_BUFFER

Section titled “FELIX_UDP_SEND_BUFFER / FELIX_UDP_RECV_BUFFER”

Description: Requested UDP socket buffer sizes (SO_SNDBUF / SO_RCVBUF). Applied best-effort: halved until the OS accepts. Kernel-level datagram drops surface as QUIC retransmits and tail-latency spikes, so large buffers matter at high message rates.

Type: Positive integer (bytes)

Default: 8388608 (8 MiB)

Terminal window
export FELIX_UDP_SEND_BUFFER="8388608"
export FELIX_UDP_RECV_BUFFER="8388608"

Description: Largest UDP datagram the endpoint accepts (receive side). Must be at least the peer’s discovered MTU or large datagrams are rejected.

Type: Positive integer (bytes, clamped to 1200–65527)

Default: 65527

Terminal window
export FELIX_MAX_UDP_PAYLOAD="65527"

Description: Size of the dedicated QUIC I/O runtime pool. Quinn’s driver tasks (endpoint receive loop, per-connection transmit/ACK loops) do a bounded slice of work per poll and reschedule themselves, so their scheduler re-poll latency is the transport’s throughput ceiling. Felix therefore runs them on a pool of single-threaded runtimes isolated from application tasks, assigned by role: server endpoints spread across every runtime but the last, client endpoints share the last. 0 disables the isolation and runs drivers on the application runtime (the pre-fix behavior). A larger pool cannot make a single endpoint faster — an endpoint’s driver is one task on one runtime — and it splits endpoints that talk to each other onto separate threads, which measured 5–6× slower.

Type: Non-negative integer

Default: derived on macOS — one runtime per server endpoint plus one for clients, so a broker with one client listener gets 2, and one with FELIX_QUIC_LISTENERS=4 gets 6 (four client listeners, the internal listener, and the client runtime). 0 elsewhere.

Terminal window
export FELIX_IO_RUNTIME_THREADS="2"
export FELIX_IO_RUNTIME_THREADS="0" # disable driver isolation

Description: How many ack-eliciting packets a peer may receive before it must send an ACK (QUIC ACK-frequency extension; applies between quinn peers). The RFC default of every other packet costs a reverse-path datagram — plus its wakeup chain — per 2 datagrams of data; the higher default trades a little loss-detection latency (bounded by the 2 ms max_ack_delay Felix also negotiates) for measurably less per-byte wakeup traffic (+15% throughput on loopback).

Type: Positive integer (packets)

Default: 20

Terminal window
export FELIX_ACK_ELICITING_THRESHOLD="20"
export FELIX_ACK_ELICITING_THRESHOLD="1" # RFC-like cadence

Description: Set to any value to skip negotiating the ACK-frequency extension entirely, restoring stock quinn ACK behavior (25 ms max ack delay, ACK every other packet).

Type: Presence toggle

Default: unset (extension negotiated)

Terminal window
export FELIX_ACK_FREQ_DISABLE="1"

Description: Log live quinn::ConnectionStats (path MTU, cwnd, rtt, loss, congestion events, blocked-frame counters, UDP datagram/byte/io counts) for every connection on this interval, on both the broker and the client. The client-side log is the only place the publish path’s sender-side congestion state is visible. Diagnostic; off unless set.

Type: Positive integer (milliseconds)

Default: unset (disabled)

Terminal window
export FELIX_CONN_STATS_MS="1000"

Description: Disable per-stage timing collection.

Type: Boolean

Default: false

Accepted values: 1, true, yes = disabled

Example:

Terminal window
export FELIX_DISABLE_TIMINGS="false" # Enable timings
export FELIX_DISABLE_TIMINGS="true" # Disable for performance
export FELIX_DISABLE_TIMINGS="1"

Trade-off:

  • false: Detailed metrics, slight overhead
  • true: Maximum performance, no timing data

Description: Timeout for control stream drain (broker).

Type: Positive integer (milliseconds)

Default: 50

Example:

Terminal window
export FELIX_CONTROL_STREAM_DRAIN_TIMEOUT_MS="50"
export FELIX_CONTROL_STREAM_DRAIN_TIMEOUT_MS="100" # More graceful
export FELIX_CONTROL_STREAM_DRAIN_TIMEOUT_MS="20" # Faster shutdown

Description: Total budget for draining in-flight work after SIGTERM or SIGINT, before remaining tasks are force-cancelled. Applies to both the broker and the control plane. This is a single budget shared by every subsystem, not a per-subsystem timeout, so total shutdown time stays bounded by this value.

Type: Positive integer (milliseconds)

Default: 25000

Example:

Terminal window
export FELIX_SHUTDOWN_DRAIN_TIMEOUT_MS="25000"
export FELIX_SHUTDOWN_DRAIN_TIMEOUT_MS="55000" # With terminationGracePeriodSeconds: 60
export FELIX_SHUTDOWN_DRAIN_TIMEOUT_MS="5000" # Fast rollouts, short-lived requests

Note: Keep this below the platform’s kill deadline — Kubernetes’ terminationGracePeriodSeconds (default 30) — so the drain finishes and logs its outcome before SIGKILL. See Graceful Shutdown.

Description: How long the control plane keeps serving after it starts reporting unready, before it stops accepting connections. Readiness-first shutdown only helps if something has time to act on it: a load balancer learns an instance is draining by polling, so closing the listener the moment readiness flips leaves requests still being routed to a socket that is gone.

Applies to: Control plane.

Type: Non-negative integer (milliseconds); 0 skips the wait.

Default: 5000

Example:

Terminal window
export FELIX_SHUTDOWN_PREDRAIN_MS="5000"
export FELIX_SHUTDOWN_PREDRAIN_MS="15000" # readinessProbe periodSeconds 5 x failureThreshold 3
export FELIX_SHUTDOWN_PREDRAIN_MS="0" # single instance, nothing routing to it

Note: Size it above the prober’s periodSeconds multiplied by its failureThreshold, so the load balancer has actually removed this instance before the listener closes. It is spent inside the platform’s kill deadline, so terminationGracePeriodSeconds must cover this plus FELIX_SHUTDOWN_DRAIN_TIMEOUT_MS. A second SIGTERM ends the wait early.

Description: Inbound peer connections this broker holds at once, across all peers. Over the limit, a connection is refused rather than queued — a peer told no can back off, where one left waiting cannot tell a busy broker from a stuck one.

Type: Integer

Default: 512

Description: Inbound peer connections from any one address. The total alone does not stop one peer consuming the whole allowance, which is the case that matters: a peer looping on a reconnect bug starves the rest of the cluster before anyone notices.

Startup refuses a value above FELIX_INTERNAL_MAX_INBOUND_CONNECTIONS, since the per-source limit would then never be the one that applies.

Type: Integer

Default: 16

Description: Path to YAML configuration file.

Type: String (file path)

Default: /usr/local/felix/config.yml (optional)

Example:

Terminal window
export FELIX_BROKER_CONFIG="/etc/felix/broker.yml"
export FELIX_BROKER_CONFIG="/tmp/felix-dev.yml"

Behavior:

  • If set and file missing: error
  • If not set and default missing: continue with defaults
  • A key the broker does not know is an error, naming the key. A setting that looks like it is in effect and is not is worse than a refusal, so a misspelled key stops startup rather than silently leaving the default in place. The same is true of the control plane’s config file, nested sections included.
Terminal window
felix-broker --print-config

Prints the configuration the broker would run with — defaults, the config file, and the environment already folded together — as YAML, and exits. Nothing is bound, so it is safe to run on a node that is already serving.

It doubles as a pre-flight check. The config is loaded exactly as startup loads it, so a file that will not parse, or a key the broker does not know, fails here with the same message it would have produced on the node — before a rollout rather than during one.

The node credential is shown as <redacted>, or <unset> when there is none: this output is meant to be pasted into an issue, and whether a token is set is exactly what someone debugging a registration failure needs to see. Warnings about unrecognised variables go to stderr, so --print-config > current.yml gives a clean document and still shows them.

Each variable validates its own value where it is parsed. Some pairs are each fine alone and wrong in combination, and those are refused at startup too — because the failure they produce otherwise is behaviour nobody configured, with no error to explain it:

Refused when Why
event_batch_max_bytes > max_frame_bytes The broker would send subscribers frames larger than it will itself accept, and a client applying the same limit drops them
pub_conn_inflight_bytes > pub_inflight_bytes The per-connection limit could never be the one that applies, so one connection may take the whole broker-wide allowance
cache_stream_recv_window > cache_conn_recv_window A single stream can never reach its own window, because the connection’s runs out first
FELIX_INTERNAL_BIND shares a port with FELIX_QUIC_BIND The internal and client-facing listeners must be separate

Equal is allowed everywhere: these bound each other and do not have to differ.

felix-broker --print-config runs the same checks without starting anything, so a bad combination fails before a rollout rather than on the node.

A misspelled variable cannot be refused the same way — the process cannot tell a typo from a variable meant for something else sharing the container. So both binaries warn instead, at startup, naming every FELIX_* variable that is set and that nothing reads:

WARN FELIX_METRICS_BIND is set and nothing reads it — did you mean one of
FELIX_BROKER_METRICS_BIND, FELIX_CONTROLPLANE_METRICS_BIND? Those
settings are using their defaults

The suggestion looks for a missing segment first and a misspelling second, so a plausible-but-wrong shorter name — the mistake someone makes without noticing — is matched to the real one. Nothing close enough means no suggestion rather than the nearest arbitrary name.

Description: Rust logging filter (not Felix-specific but commonly used).

Type: String (filter expression)

Default: Varies by build

Example:

Terminal window
export RUST_LOG="info"
export RUST_LOG="debug"
export RUST_LOG="felix_broker=debug,felix_wire=trace"
export RUST_LOG="warn"

Levels: error, warn, info, debug, trace

Terminal window
export FELIX_EVENT_CONN_POOL="8"
export FELIX_EVENT_CONN_RECV_WINDOW="268435456"
export FELIX_EVENT_STREAM_RECV_WINDOW="67108864"
export FELIX_EVENT_SEND_WINDOW="268435456"
export FELIX_EVENT_BATCH_MAX_DELAY_US="250"
export FELIX_CACHE_CONN_POOL="8"
export FELIX_CACHE_STREAMS_PER_CONN="4"
export FELIX_DISABLE_TIMINGS="0"
Terminal window
export FELIX_EVENT_CONN_POOL="8"
export FELIX_EVENT_CONN_RECV_WINDOW="536870912"
export FELIX_EVENT_STREAM_RECV_WINDOW="134217728"
export FELIX_EVENT_SEND_WINDOW="536870912"
export FELIX_EVENT_BATCH_MAX_DELAY_US="250"
export FELIX_CACHE_CONN_POOL="8"
export FELIX_CACHE_STREAMS_PER_CONN="4"
export FELIX_DISABLE_TIMINGS="1"
Terminal window
export FELIX_EVENT_BATCH_MAX_EVENTS="1"
export FELIX_EVENT_BATCH_MAX_DELAY_US="50"
export FELIX_FANOUT_BATCH="16"
export FELIX_DISABLE_TIMINGS="1"
Terminal window
export FELIX_EVENT_BATCH_MAX_EVENTS="256"
export FELIX_EVENT_BATCH_MAX_BYTES="1048576"
export FELIX_EVENT_BATCH_MAX_DELAY_US="1000"
export FELIX_FANOUT_BATCH="128"
export FELIX_DISABLE_TIMINGS="1"

Durable stream storage is opt-in. With FELIX_DURABLE_STORAGE_DIR unset the broker is in-memory only, and any stream the control plane marks durable: true is rejected at registration rather than silently downgraded to a guarantee the broker cannot keep.

See Durable Storage for what each policy guarantees and what it costs.

Description: Root directory for durable stream segments. Setting it enables durable streams; one subdirectory is created per stream shard.

Type: Path

Default: unset (durable storage disabled)

Example:

Terminal window
export FELIX_DURABLE_STORAGE_DIR="/var/lib/felix/streams"

Description: When written bytes are pushed to the storage device.

Type: One of none, periodic, on_commit

Default: periodic

Value Acknowledged when Loss window
none bytes reach the page cache unbounded — survives a process crash, not a power loss
periodic bytes reach the page cache one flush interval
on_commit bytes reach the device none

Example:

Terminal window
export FELIX_DURABLE_FSYNC_MODE="on_commit"

Trade-off: on_commit costs one device flush per commit (~4ms on typical NVMe), amortised across concurrent publishers by group commit. periodic adds no measurable append latency at all.

Description: Flush interval for FELIX_DURABLE_FSYNC_MODE=periodic. Bounds how much acknowledged data a machine crash can lose.

Type: Positive integer (milliseconds)

Default: 250

Example:

Terminal window
export FELIX_DURABLE_FSYNC_INTERVAL_MS="100"

Note: Setting this without setting the mode implies periodic. Zero is rejected at startup — it is a busy loop, not “always sync”; use on_commit for per-commit durability.

Description: Size at which the active segment rolls over to a new file.

Type: Positive integer (bytes)

Default: 268435456 (256 MiB)

Example:

Terminal window
export FELIX_DURABLE_SEGMENT_BYTES="67108864" # 64 MiB

Trade-off: Smaller segments bound recovery time (only the active segment is fully scanned at startup) at the cost of more files and more rollovers.

Description: Delete the oldest sealed segments once a stream’s log exceeds this size. Unset means the log grows without bound, which is the default and the pre-retention behaviour.

Type: Positive integer (bytes)

Default: unset (no size bound)

Example:

Terminal window
export FELIX_DURABLE_RETENTION_BYTES="10737418240" # 10 GiB per stream shard

Note: The active segment is never deleted, so a log settles at roughly this size and never below FELIX_DURABLE_SEGMENT_BYTES regardless of how small this is set. Records below the retained range report CursorTooOld to a resuming subscriber, naming the oldest offset still available.

Description: Delete sealed segments whose newest record is older than this. Combines with FELIX_DURABLE_RETENTION_BYTES; either bound alone is enough to trigger a deletion.

Type: Positive integer (seconds)

Default: unset (no age bound)

Example:

Terminal window
export FELIX_DURABLE_RETENTION_SECONDS="604800" # 7 days

Note: Age comes from the records’ own timestamps rather than file mtime, so restoring a backup does not reset it. A segment survives until its newest record has expired, so nothing younger than the bound is ever deleted.

Description: How often retention is evaluated. Ignored unless a retention bound is set.

Type: Positive integer (seconds)

Default: 60

Example:

Terminal window
export FELIX_DURABLE_RETENTION_INTERVAL_SECONDS="300"

Trade-off: Retention is bulk file deletion and runs on its own timer so it never lands on a publish. A longer interval means disk usage overshoots the bound for longer between passes.

Description: Bytes of segment data between sparse index entries. A read binary-searches the index, then scans forward at most one interval.

Type: Positive integer (bytes)

Default: 4096

Example:

Terminal window
export FELIX_DURABLE_INDEX_SPACING_BYTES="8192"

Trade-off: Smaller spacing means faster seeks and larger index files.

Description: Ceiling on records returned by a single range read, on top of the caller’s byte budget. Payload bytes alone do not bound a response made of empty records.

Type: Positive integer

Default: 10000

Example:

Terminal window
export FELIX_DURABLE_MAX_RECORDS_PER_READ="5000"

Description: Reserve a segment’s blocks when it is created, keeping block allocation off the append path.

Type: Boolean

Default: true

Example:

Terminal window
export FELIX_DURABLE_PREALLOCATE="false"

Note: Disable on filesystems where reservations are expensive or where thin provisioning makes them counter-productive.

Description: Submit device flushes through io_uring (IORING_OP_FSYNC) on one process-wide ring, instead of handing each one to the blocking thread pool. Linux only.

Type: Boolean (1 to enable)

Default: 0

Example:

Terminal window
export FELIX_STORAGE_IO_URING="1"

Note: A kernel too old for the opcode, or a container that forbids the syscall, falls back to the blocking pool rather than failing — durability must not depend on an optimisation being available. A perf session measured 956.7 MB/s with it on against 917.2 without, every run better and no overlap between the distributions.

Description: Checksum every record of every segment at startup.

Type: Boolean

Default: false

Example:

Terminal window
export FELIX_DURABLE_VERIFY_ALL_ON_OPEN="true"

Trade-off: Off by default because startup would otherwise cost one full pass over all data on disk. The active segment is always fully scanned regardless, and every read verifies the records it returns — so bit rot in cold data is still caught, just when it is read rather than at boot.

Check current configuration:

Terminal window
# Print effective configuration
cargo run --release -p broker -- --dump-config
# Validate without starting
cargo run --release -p broker -- --validate-config

The sections above cover the variables most deployments touch, each with an example and the reasoning. What follows is the rest, in brief, so the page is completescripts/check_env_reference.py fails the build if a variable exists in the code and is not named here.

Variables used only by benchmarks, demos and the test harness are deliberately absent; they are listed in that script rather than here.

Variable Default Purpose
FELIX_CONTROLPLANE_BIND 0.0.0.0:8443 Address the control-plane API listens on.
FELIX_CONTROLPLANE_METRICS_BIND Separate address for the metrics endpoint.
FELIX_CONTROLPLANE_CONFIG Path to a config file; environment variables override it.
FELIX_CONTROLPLANE_STORAGE_BACKEND memory memory or postgres. memory loses everything on restart.
FELIX_CONTROLPLANE_POSTGRES_URL Connection string. Required when the backend is postgres.
FELIX_CONTROLPLANE_POSTGRES_MAX_CONNECTIONS 10 Pool size. Caps concurrent database work.
FELIX_CONTROLPLANE_POSTGRES_CONNECT_TIMEOUT_MS 5000 Bounds establishing a new physical connection.
FELIX_CONTROLPLANE_POSTGRES_ACQUIRE_TIMEOUT_MS 5000 Bounds waiting for a pooled connection before failing fast.
FELIX_CONTROLPLANE_CHANGES_LIMIT 1000 Maximum changes returned by one changefeed page.
FELIX_CONTROLPLANE_CHANGE_RETENTION_MAX_ROWS 10000 Bounds the append-only change tables. Smaller means a watcher can fall behind sooner and need a fresh snapshot.
FELIX_CONTROLPLANE_OIDC_ALLOWED_ALGORITHMS Comma-separated JWS algorithms accepted from an upstream IdP.
FELIX_EXCHANGE_TOKEN_TTL_SECONDS 900 Lifetime of a Felix access token minted by the token exchange. The default is short to limit blast radius if a token leaks. Prefer refresh over raising it: a long-running process should refresh rather than hold one long-lived bearer token.
FELIX_REFRESH_TOKEN_TTL_SECONDS 2592000 Lifetime of a refresh token (30 days). This is how a long-running process stays authenticated without standing IdP credentials. Refresh tokens are single-use and rotate on every refresh, so this bounds a stolen and never used token — one that is used produces a replay, which revokes its whole chain immediately.
FELIX_RAFT_NODE_ID This instance’s id in the metadata Raft group (see Metadata Raft). All three raft variables together select the raft backend, or startup fails on a partial set.
FELIX_RAFT_DATA_DIR Where the Raft log, vote, and snapshots live. Must survive restarts: it is what makes a restart a rejoin rather than a fresh member.
FELIX_RAFT_PEERS The initial group as id=host:port,... of every member’s main listener. Identical on every member.
FELIX_RAFT_HEARTBEAT_MS 150 Leader heartbeat interval within the metadata group.
FELIX_RAFT_ELECTION_TIMEOUT_MIN_MS 600 Lower edge of the election window. Must exceed the heartbeat — a window at or below it elects against healthy leaders, and startup refuses it.
FELIX_RAFT_ELECTION_TIMEOUT_MAX_MS 1200 Upper edge of the election window. Must exceed the minimum.
FELIX_RAFT_SNAPSHOT_LOGS_SINCE_LAST 500 Snapshot after this many log entries; metadata state is small, so snapshots are cheap and the log stays short.
FELIX_RAFT_LOGS_KEPT_BEHIND_SNAPSHOT 100 Entries kept behind the snapshot so a briefly-lagging member catches up from the log rather than a snapshot install.
FELIX_RAFT_WRITE_TIMEOUT_MS 10000 Overall budget for one proposal, elections and forwarding included. “No quorum” becomes an error at this bound rather than a hang.
FELIX_READINESS_TIMEOUT_MS 2000 Longest a readiness check may take before it counts as a failure. Keep it below the prober’s own timeout so the reason is reported rather than lost.
FELIX_READINESS_CACHE_TTL_MS 1000 How long a readiness answer is reused. Bounds probe cost regardless of how many probers there are, and bounds how long recovery takes to become visible.
FELIX_SHUTDOWN_DRAIN_TIMEOUT_MS 25000 Budget for draining in-flight requests after SIGTERM before tasks are cancelled.
FELIX_SHUTDOWN_PREDRAIN_MS 5000 How long to keep serving after readiness flips to draining, so load balancers remove this instance before the listener closes. 0 skips it.
FELIX_REGION_ID local Region this instance reports.
FELIX_BOOTSTRAP_ENABLED false Enables the first-run bootstrap endpoints. Leave off once credentials exist.
FELIX_BOOTSTRAP_TOKEN Token the bootstrap endpoints require.
FELIX_BOOTSTRAP_TOKEN_PREVIOUS The token being rotated out, still accepted alongside the current one so a rotation is a rolling deploy rather than an outage. Requires FELIX_BOOTSTRAP_TOKEN.
FELIX_BOOTSTRAP_BIND_ADDR 127.0.0.1:9095 Restricts bootstrap to a separate listener.
FELIX_BOOTSTRAP_TLS_CERT PEM certificate chain the bootstrap listener presents. All three TLS variables together, or startup fails — a partial set is a misconfiguration, not “TLS off”.
FELIX_BOOTSTRAP_TLS_KEY PEM private key for the bootstrap listener’s certificate.
FELIX_BOOTSTRAP_TLS_CLIENT_CA PEM CA bundle; only clients presenting a certificate signed by it can complete the TLS handshake with the bootstrap listener.
Variable Default Purpose
FELIX_NODE_ID This broker’s identity in the cluster. Must be stable across restarts.
FELIX_NODE_ADVERTISE_ADDR Address peers should reach this broker on.
FELIX_CLIENT_ADVERTISE_ADDR Address clients should reach it on, when it differs from the peer address.
FELIX_NODE_TOKEN / FELIX_NODE_TOKEN_FILE Access credential this broker presents to the control plane, on every call including the metadata feeds it seeds from (which require node.view:cluster:*). Required with FELIX_NODE_ID; a standalone broker may omit it, but then its sync is refused and it says so at startup. The file form is re-read every 30s, so whatever mints the credential — a Vault agent, SPIRE, a sidecar — can rotate it without a restart; a replacement that is already expired is declined rather than adopted. A broker joining a cluster refuses to start with an expiring token supplied by value and no refresh file, because nothing could then renew it.
FELIX_NODE_REFRESH_TOKEN_FILE Path to this broker’s refresh token. With it the broker re-mints its access token before expiry and stays registered indefinitely. A path, not a value: refreshing spends the token and mints a replacement, so the broker writes the replacement back here — a restart that presented a spent one would be read as a replay and revoke the whole chain. The path must be writable. Setting FELIX_NODE_REFRESH_TOKEN instead fails startup, rather than locking the broker out at its first restart. Either this or FELIX_NODE_TOKEN_FILE is required when the credential carries an exp and FELIX_NODE_ID is set.
FELIX_NODE_HEARTBEAT_INTERVAL_MS 5000 How often a broker reports itself alive.
FELIX_NODE_EXPIRY_TIMEOUT_MS 15000 Silence after which a node is considered gone. Placement will not promote a replica whose last report is older than roughly twice this.
FELIX_NODE_EXPIRY_SWEEP_INTERVAL_MS 2000 How often expiry is evaluated.
FELIX_SHARD_RECONCILE_INTERVAL_MS 5000 How often placement re-plans. Bounds how quickly a failover happens.
FELIX_CP_URL, FELIX_CP_SYNC_INTERVAL_MS Short aliases used by the demos and cluster harness.
Variable Default Purpose
FELIX_INTERNAL_BIND Address for the broker-to-broker QUIC endpoint. Separate from the client one.
FELIX_INTERNAL_TLS_CERT PEM certificate chain this broker presents to peers, leaf first. Its DNS name must be the broker’s FELIX_NODE_ID. Set with the two below, or none of the three.
FELIX_INTERNAL_TLS_KEY PEM private key for that certificate. Re-read with the certificate every 30s, so a renewal on disk is picked up by the next handshake without a restart.
FELIX_INTERNAL_TLS_CA PEM bundle every peer’s certificate must chain to. With all three set, every peer connection is mutually authenticated and the certificate’s name is checked against the node id in both directions. Without them the peer link is encrypted but unauthenticated, and startup warns.
FELIX_INTERNAL_CONNS_PER_PEER 1 Connections held to each peer.
FELIX_INTERNAL_STREAMS_PER_CONN 4 Multiplexed streams per peer connection, so one large forwarded batch does not block smaller requests.
FELIX_INTERNAL_MAX_INFLIGHT 1024 Outstanding requests allowed per peer.
FELIX_INTERNAL_REQUEST_TIMEOUT_MS 5000 Bounds one forwarded request.
FELIX_INTERNAL_HANDSHAKE_TIMEOUT_MS 2000 Bounds dialling a peer that is gone.
FELIX_INTERNAL_IDLE_TIMEOUT_MS 60000 Idle timeout on a peer connection.
FELIX_INTERNAL_RECONNECT_BASE_MS 50 First reconnect backoff after losing a peer.
FELIX_INTERNAL_RECONNECT_MAX_MS 5000 Backoff ceiling.
FELIX_PUBLISH_QUORUM_TIMEOUT_MS 5000 Longest a Quorum publish waits for a majority before failing.
FELIX_REPLICATION_REBUILD_MAX_CONCURRENT 1 Halted followers this broker rebuilds at once, across every shard it leads. 0 rebuilds nothing and leaves every halt to an operator.
FELIX_REPLICATION_REBUILD_BYTES_PER_SEC 0 Bytes per second a rebuilding follower is shipped at. 0 is unlimited.
Variable Default Purpose
FELIX_GROUP_VISIBILITY_TIMEOUT_MS 30000 How long a consumer’s claim on a record stands.
FELIX_GROUP_MAX_ATTEMPTS 5 Deliveries before a record is dead-lettered.
FELIX_GROUP_MAX_WAIT_MS 30000 Cap on a long-polling client’s requested wait.
Variable Default Purpose
FELIX_DURABLE_ROLLOVER_THRESHOLD_PERCENT How full a segment gets before a rollover is prepared.
FELIX_DURABLE_MAX_OVERSHOOT_PERCENT How far a segment may exceed its target rather than splitting a batch.
FELIX_DURABLE_REPAIR_CHECKSUM_TAIL Whether recovery re-verifies checksums over the tail as well as the structure.
Variable Default Purpose
FELIX_CLIENT_CONFIG Path to a client config file.
FELIX_AUTH_TENANT, FELIX_AUTH_TOKEN, FELIX_TOKEN Credentials a client presents.
FELIX_KEEPALIVE_MS QUIC keep-alive interval.
FELIX_MAX_IDLE_TIMEOUT_MS 60000 QUIC idle timeout before a connection is dropped.
FELIX_EVENT_ROUTER_MAX_PENDING 16384 Events buffered by the client’s router before it applies backpressure.
FELIX_SUB_DEDICATED_THREAD, FELIX_SUB_DEDICATED_QUEUE_CAPACITY Give a subscription its own thread and queue.
FELIX_SUB_DELIVERY_SHAPING, FELIX_SUB_EGRESS_CONNS Delivery shaping and egress fan-out on the broker.
FELIX_PUBLISH_SHARDING / FELIX_PUB_SHARDING Route publishes to a worker by stream rather than round-robin.
FELIX_PUMP_COLOCATE Colocate the delivery pump with the publish worker for a stream.
FELIX_WORKER_THREADS Tokio worker threads. Defaults to the core count.
FELIX_TIMING_SAMPLE_EVERY Sample rate for timing histograms.
FELIX_SERVICE_INSTANCE_ID Instance identity reported in telemetry.