NATS workers
Scale Qyra query processing with dedicated NATS worker pods using the Helm chart
This page is for engineering teams self-hosting their own Qyra instance.
By default, Qyra processes all queries on the main API server. NATS workers move query execution onto dedicated pods, improving responsiveness under load and letting you scale query capacity independently.
Qyra uses NATS — a lightweight, high-performance messaging system — with JetStream, its built-in persistent streaming layer, to distribute work between the API server and worker pods.
NATS powers two opt-in features in Qyra: warehouse workers, which process interactive and background warehouse queries on dedicated pods, and pre-aggregate workers, which materialize pre-aggregates and serve queries from DuckDB.
Requirements
- Helm chart version 2.7.2 or later
- Qyra version 0.2675.0 or later. Older images will fail with
MODULE_NOT_FOUND.
Upgrading the Helm chart alone does not change how Qyra works. NATS features are entirely opt-in — your existing deployment will behave exactly the same until you explicitly enable the new Helm values described below.
Architecture
flowchart LR
API[Qyra API] -->|publish job| NATS[NATS JetStream]
NATS -->|deliver message| Worker[Worker pod<br/>concurrency: 100]
Worker -->|return result| APIThe Qyra API publishes jobs to NATS JetStream. Worker pods consume messages from their stream and process them concurrently (default 100 concurrent jobs per pod).
Enabling NATS
nats:
enabled: true
config:
cluster:
enabled: false
jetstream:
enabled: true
fileStore:
enabled: false
memoryStore:
enabled: true
maxSize: 1GiThe JetStream configuration shown above reflects the defaults when nats.enabled is set to true. This deploys a NATS StatefulSet and sets NATS_ENABLED=true on the backend, which means the backend will start routing queries through NATS. You should always enable at least a warehouse worker alongside NATS to process those queries — otherwise queries will be enqueued with no worker to pick them up.
Do not enable nats.enabled: true without also enabling warehouseNatsWorker.enabled: true. The backend routes queries to NATS when NATS_ENABLED is set, so queries will stall if no worker is running to process them.
Auto-configured environment variables
The chart automatically sets these environment variables in the shared ConfigMap — you do not need to set them manually:
| Variable | Set when | Value |
|---|---|---|
NATS_ENABLED | nats.enabled: true | "true" |
NATS_URL | nats.enabled: true | nats://<release>-nats:4222 |
Additional environment variables are auto-configured per worker deployment — see Warehouse workers and Pre-aggregate workers for details.
NATS JetStream configuration
JetStream supports two storage backends — we recommend memory store, but you can use file store depending on your needs.
Memory store vs file store
| Memory store (recommended) | File store | |
|---|---|---|
| How it works | Messages are held in RAM | Messages are persisted to disk |
| Performance | Faster — no disk I/O overhead | Slower — writes go through disk |
| Persistence | Messages are lost if NATS restarts | Messages survive NATS restarts |
| Infrastructure | No PersistentVolumeClaim needed | Requires a PersistentVolumeClaim |
| When to use | Most deployments. Qyra messages are small (just a query UUID) and are deleted once processed. | High message volume exceeding available RAM, or if you need messages to survive NATS pod restarts. |
For more details, see the NATS documentation on JetStream and stream storage types.
Configuration reference
nats:
enabled: true
config:
cluster:
enabled: false # single-node NATS, no clustering
jetstream:
enabled: true
fileStore:
enabled: false # no disk persistence
memoryStore:
enabled: true
maxSize: 1Gi # max memory for message storage| Setting | Recommended | Description |
|---|---|---|
nats.config.jetstream.memoryStore.enabled | true | Enable memory-backed storage |
nats.config.jetstream.memoryStore.maxSize | 1Gi | Maximum memory for JetStream message storage |
nats.config.jetstream.fileStore.enabled | false | Enable disk-backed storage |
nats.config.cluster.enabled | false | Single-node NATS (no clustering) |
Pod disruption
NATS is a stateful component — if the NATS pod restarts, in-flight messages are lost (queries will be retried by users). The chart protects against unplanned eviction with:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"annotationPodDisruptionBudgetwithmaxUnavailable: 0