Queue Backend
OpenObserve uses an internal message queue for coordinating cluster events, rate limiting, and synchronizing state across nodes. You can choose between two queue backends by setting ZO_QUEUE_STORE.
Choosing a backend
| Backend | Best for |
|---|---|
NATS (nats) |
Cluster (HA) deployments with multiple nodes. Requires a running NATS server. |
In-memory (memory) |
Local single-process deployments where you want to avoid running a NATS server. Process-local and non-durable. |
When ZO_QUEUE_STORE is left unset, OpenObserve selects the backend automatically:
- Local mode (
ZO_LOCAL_MODE=true): defaults tomemory - Cluster mode (
ZO_LOCAL_MODE=false): defaults tonats
Explicit values are always honored, and unknown values cause a startup error.
Configuration
ZO_QUEUE_STORE
| Value | Description |
|---|---|
nats |
Use a NATS JetStream server for queuing. Requires the NATS server to be configured (see NATS configuration). |
memory |
Use a bounded, process-local, in-memory queue. Works only in local mode. |
The memory backend is rejected at startup if ZO_LOCAL_MODE is false, since an in-memory queue cannot coordinate across nodes in a cluster deployment.
ZO_MEMORY_QUEUE_MAX_SIZE_MB
| Default | Description |
|---|---|
64 |
Aggregate accounted memory limit in megabytes across all queue topics when ZO_QUEUE_STORE=memory. |
Set this to control the maximum memory the in-memory queue can consume across all topics. A value of 0 falls back to the default of 64 MB. The value is validated at startup and converted to bytes internally.
In-memory queue behavior
When ZO_QUEUE_STORE=memory, the queue backend operates with the following characteristics:
- Process scope: All state is held in memory and lost when the process exits. No durability across restarts.
- At-least-once delivery: Messages are guaranteed to be delivered at least once within the process lifetime. FIFO ordering for first delivery.
- Single global budget: The
ZO_MEMORY_QUEUE_MAX_SIZE_MBlimit is shared across all queue topics. Both topic metadata and message payloads count toward the budget. - Publication rejection: Publishing a message that would exceed the budget returns a
QueueFullerror. A message larger than the entire budget returns aMessageTooLargeerror. Unacknowledged messages are never evicted to make room. - Lazy expiration sweep: When the budget is full, expired messages (past their
max_age) are reclaimed from all topics to make room before rejecting the publication. Routine expiration is handled by a background maintenance loop. - One active consumer per topic: A topic supports one active consumer at a time. Re-subscribing after the previous consumer closes is supported.
- Visibility timeout: Unacknowledged messages are redelivered after 30 seconds, matching the default NATS
ack_wait. A stale ack (e.g., from a timed-out delivery) does not affect the newer delivery attempt. - Deliver policies:
All,Last, andNewdeliver policies are supported.Allreplays the full backlog;Lastkeeps only the newest unacknowledged message;Newstarts with messages published after the subscription. max_agesupport: Messages are expired based on their original publication time. Expiration applies to both pending and in-flight messages.- Redelivery: Messages dropped without acknowledgment (e.g., the receiver is dropped) are requeued for redelivery. Messages exceeding the visibility timeout are also requeued.
Limitations
- No durability: all queued messages are lost on process exit.
- Not supported in cluster mode.
- Total message accounting includes per-entry and per-topic overhead (roughly 128 bytes per message and 256 bytes per topic), so empty messages still consume budget.
Metrics
When ZO_QUEUE_STORE=memory, the following Prometheus metrics are exposed on the /metrics endpoint:
| Metric | Labels | Description |
|---|---|---|
queue_memory_used_bytes |
backend |
Current aggregate accounted bytes across all queue topics |
queue_memory_limit_bytes |
backend |
Configured aggregate byte limit |
queue_messages |
topic, state |
Message counts by topic and state (pending, in_flight) |
queue_publish_total |
topic, result |
Publications by topic and result (accepted, rejected_full, rejected_too_large) |
queue_redelivery_total |
topic, reason |
Redeliveries by topic and reason (dropped, timeout) |
queue_message_expired_total |
topic, state |
Messages expired by max_age, by topic and state (pending, in_flight) |
queue_oldest_message_age_seconds |
topic |
Age in seconds of the oldest pending or in-flight message |
Rate limiting with the in-memory queue
Enterprise rate limiting requires ZO_QUEUE_STORE=nats. Setting ZO_QUEUE_STORE=memory while rate limiting is enabled causes a startup error.
Example: local development setup
# Run in local mode with the in-memory queue (default behavior)
ZO_LOCAL_MODE=true ./openobserve
# Equivalent, with explicit values
ZO_LOCAL_MODE=true ZO_QUEUE_STORE=memory ZO_MEMORY_QUEUE_MAX_SIZE_MB=128 ./openobserve
# Run in local mode but use a local NATS server for the queue
ZO_LOCAL_MODE=true ZO_QUEUE_STORE=nats ZO_NATS_ADDR=localhost:4222 ./openobserve