# Storage & Queues (/configure/reference/storage-queues)



KubeMQ persists messages through an embedded **persistent store** and serves pull-based
delivery through its **queues**. The store controls how much is retained and for how long;
the queue settings control visibility, wait, delay, expiration, and retry behavior. Each
setting is shown for both deployment targets — Docker single-node (`config.yaml` key · env
var) and Kubernetes/Helm (`spec.*` path). A dash (`—`) in the Helm/CRD column means the
setting is not available on that surface.

Store and queue tuning is **opt-out**: the `store` and `queue` blocks are internal
server sections (not wire connectors), so they are always active with the defaults below —
there is no `enable`/`disabled` toggle. Override only the fields you need.

## Persistent store [#persistent-store]

Limits and retention for the persistent store. The **persistence engine** is chosen once
at cluster creation — see [Storage Engines](/configure/reference/storage-engines) for
the full engine model, durability guarantees, and clustering. Several store fields carry
**name divergences** between the Docker `config.yaml` key and the Helm/CRD field — the
Notes column flags each one. The ten fields below are all CRD-settable under
`spec.store.*`; the store config carries **five more** — the `next`-engine settings
(`nextackpolicy`, `nextraftaddress`, `nextsegmentsize`, `nextshards`,
`nextbalanceleaders`), which are `config.yaml`/env-only and documented on
[Storage Engines](/configure/reference/storage-engines#next-engine-settings).

| Setting                  | Type   | Default                                                      | Valid values                 | Docker (config.yaml key · env var)                             | Helm/CRD path                         | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------ | ------ | ------------------------------------------------------------ | ---------------------------- | -------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Persistence engine       | string | *unset* → resolved from the store directory (clean ⇒ `next`) | `legacy` \| `next` \| `auto` | `store.engine` · `STORE_ENGINE`                                | `spec.store.engine` (typed CRD enum)  | Chosen **once, at cluster creation** — a cluster is born one mode and stays there; no in-place migration. &#x2A;*Unset (or the explicit `auto`) does not mean `legacy`:** the server probes the store directory and resolves the engine that wrote it, or `next` for a clean one. To get `legacy` you must name it. See [Zero-config engine selection](/configure/reference/storage-engines#zero-config-engine-selection).                                                                                                                                                |
| Clean store on start     | bool   | `false`                                                      | true / false                 | `store.cleanstore` · `STORE_CLEAN_STORE`                       | `spec.store.clean`                    | ⚠️ &#x2A;*Destructive — wipes the store on every boot.** See the warning below. Name divergence: `cleanstore` ↔ `clean`. CRD emits `STORE_CLEAN_STORE=true` only when `clean: true`; otherwise unset.                                                                                                                                                                                                                                                                                                                                                                     |
| Store path               | string | `./store`                                                    | non-empty file path          | `store.storepath` · `STORE_STORE_PATH`                         | `spec.store.path`                     | Name divergence: `storepath` ↔ `path`. Empty string is rejected. &#x2A;*On the `legacy` engine, absolute paths are rewritten to relative:** a leading `/` is prefixed with `.` (so `/data` becomes `./data`). **On the `next` engine, an absolute `StorePath` is honored verbatim** (e.g. a mounted PVC path like `/store`) — no rewrite. This is server-process behavior; on Kubernetes the operator supplies the mount and rejects a leading `/` in `spec.store.path` outright (see [Deployment](/configure/reference/deployment)) — the two layers aren't in conflict. |
| Max channels             | int    | `0` (∞)                                                      | ≥ 0; `0` = unlimited         | `store.maxqueues` · `STORE_MAX_QUEUES`                         | `spec.store.maxChannels`              | **Name divergence:** `store.maxqueues` ↔ `spec.store.maxChannels`. Negative rejected. `0` logs a stderr WARNING (unbounded).                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| Max channel size (bytes) | int64  | `0` (∞)                                                      | ≥ 0; `0` = unlimited         | `store.maxqueuesize` · `STORE_MAX_QUEUE_SIZE`                  | `spec.store.maxChannelSize`           | **Name divergence:** `store.maxqueuesize` ↔ `spec.store.maxChannelSize`. &#x2A;*Type divergence:** server field is `int64`; the CRD field is `*int32`, so via Helm the max is \~2.1 GB — set larger caps through `config.yaml`/env. `0` logs a stderr WARNING.                                                                                                                                                                                                                                                                                                            |
| Max messages / channel   | int    | `0` (∞)                                                      | ≥ 0; `0` = unlimited         | `store.maxmessages` · `STORE_MAX_MESSAGES`                     | `spec.store.maxMessages`              | Negative rejected. `0` logs a stderr WARNING.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| Max subscribers          | int    | `0` (∞)                                                      | ≥ 0; `0` = unlimited         | `store.maxsubscribers` · `STORE_MAX_SUBSCRIBERS`               | `spec.store.maxSubscribers`           | Negative rejected. `0` logs a stderr WARNING.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| Message retention (min)  | int    | `1440`                                                       | ≥ 0 (minutes)                | `store.maxretention` · `STORE_MAX_RETENTION`                   | `spec.store.messagesRetentionMinutes` | **Name divergence:** `store.maxretention` ↔ `spec.store.messagesRetentionMinutes`. Negative rejected.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| Purge inactive (min)     | int    | `1440`                                                       | ≥ 0 (minutes)                | `store.maxpurgeinactive` · `STORE_MAX_PURGE_INACTIVE`          | `spec.store.purgeInactiveMinutes`     | **Name divergence:** `store.maxpurgeinactive` ↔ `spec.store.purgeInactiveMinutes`. Negative rejected.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| Idle prune cutoff (hrs)  | int    | `24`                                                         | ≥ 1                          | `store.idleprunecutoffhours` · `STORE_IDLE_PRUNE_CUTOFF_HOURS` | `spec.store.idlePruneCutoffHours`     | Must be **at least 1** (server rejects `0`; CRD schema enforces `minimum: 1`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

<Callout type="error">
  **`store.cleanstore: true` deletes the persistent store on every single boot — not once.**
  It is not a one-shot reset. Left in a `values.yaml` or a `config.yaml`, every pod roll,
  node drain, crash-restart, and routine upgrade wipes production data — **and every restart
  looks completely healthy**, because deleting the store is exactly what you asked for.

  Use it for a deliberate, supervised reset and **take it back out immediately**. There is
  no confirmation, no dry-run, and no undo.

  Two things bound the damage, neither of which is a safety net you should rely on: the
  engine-mode guard runs **before** the wipe, so a mode-mismatched or unrecognized directory
  fails fast and is never deleted; and on a clustered `next` cluster the server **refuses**
  to wipe a member's datadir outright, telling you to remove the member from the cluster
  first. A standalone node has neither protection.
</Callout>

<Callout type="warn">
  Leaving any of **Max channels / Max channel size / Max messages / Max subscribers** at
  its default of `0` means *unlimited* and prints a startup WARNING to stderr — the store
  places no bound on that dimension, which can drive unbounded memory/disk use in
  production. Set explicit ceilings for production workloads.
</Callout>

<Callout type="warn">
  **These limits are enforced by the `legacy` engine only — and `next` is what a new
  cluster gets.** A fresh install on a clean store resolves to `next`, so **the default
  deployment enforces no retention at all**, including the `1440`-minute Message retention
  and Purge inactive values printed in the table above. Those two rows describe `legacy`.

  On the `next` engine, **Max channels / Max channel size / Max messages / Message retention
  / Purge inactive** are not consumed — a native Events Store or Queues channel has no age, size, or
  count cap and grows unbounded under a slow or absent consumer. Size `next` deployments by
  disk capacity, or use Kafka topic channels (whose `retention.ms` *is* age-enforced on
  `next`) where eviction matters. See
  [Storage Engines](/configure/reference/storage-engines#native-retention-scope).
</Callout>

## Queues [#queues]

Delivery defaults and ceilings for pull-based queues. Two Docker keys diverge from the
Helm/CRD field names — `queue.maxreceivecount` ↔ `spec.queue.maxReQueues` (the retry
ceiling) and `queue.maxnumberofmessages` ↔ `spec.queue.maxReceiveMessagesRequest` (the
per-request batch). All ten queue fields are fully CRD-settable under `spec.queue.*`.

| Setting                    | Type  | Default | Valid values | Docker (config.yaml key · env var)                                       | Helm/CRD path                          | Notes                                                                                                                 |
| -------------------------- | ----- | ------- | ------------ | ------------------------------------------------------------------------ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Default visibility (s)     | int32 | `60`    | > 0          | `queue.defaultvisibilityseconds` · `QUEUE_DEFAULT_VISIBILITY_SECONDS`    | `spec.queue.defaultVisibilitySeconds`  |                                                                                                                       |
| Default wait timeout (s)   | int32 | `1`     | > 0          | `queue.defaultwaittimeoutseconds` · `QUEUE_DEFAULT_WAIT_TIMEOUT_SECONDS` | `spec.queue.defaultWaitTimeoutSeconds` |                                                                                                                       |
| Max visibility (s)         | int32 | `43200` | > 0          | `queue.maxvisibilityseconds` · `QUEUE_MAX_VISIBILITY_SECONDS`            | `spec.queue.maxVisibilitySeconds`      | 43200 s = 12 h.                                                                                                       |
| Max wait timeout (s)       | int32 | `3600`  | > 0          | `queue.maxwaittimeoutseconds` · `QUEUE_MAX_WAIT_TIMEOUT_SECONDS`         | `spec.queue.maxWaitTimeoutSeconds`     |                                                                                                                       |
| Max delay (s)              | int32 | `43200` | > 0          | `queue.maxdelayseconds` · `QUEUE_MAX_DELAY_SECONDS`                      | `spec.queue.maxDelaySeconds`           | 43200 s = 12 h.                                                                                                       |
| Max expiration (s)         | int32 | `43200` | > 0          | `queue.maxexpirationseconds` · `QUEUE_MAX_EXPIRATION_SECONDS`            | `spec.queue.maxExpirationSeconds`      | 43200 s = 12 h.                                                                                                       |
| Retry ceiling (→ DLQ)      | int32 | `1024`  | > 0          | `queue.maxreceivecount` · `QUEUE_MAX_RECEIVE_COUNT`                      | `spec.queue.maxReQueues`               | **Name divergence:** `queue.maxreceivecount` ↔ `spec.queue.maxReQueues`. Max redeliveries before dead-lettering.      |
| Max messages / receive req | int32 | `1024`  | > 0          | `queue.maxnumberofmessages` · `QUEUE_MAX_NUMBER_OF_MESSAGES`             | `spec.queue.maxReceiveMessagesRequest` | **Name divergence:** `queue.maxnumberofmessages` ↔ `spec.queue.maxReceiveMessagesRequest`. Per-request batch ceiling. |
| Max inflight               | int32 | `2048`  | > 0          | `queue.maxinflight` · `QUEUE_MAX_INFLIGHT`                               | `spec.queue.maxInflight`               | Max unacked in-flight messages per queue.                                                                             |
| Pub-ack wait (s)           | int32 | `60`    | > 0          | `queue.pubackwaitseconds` · `QUEUE_PUB_ACK_WAIT_SECONDS`                 | `spec.queue.pubAckWaitSeconds`         | Publish-ack wait before the send is considered failed.                                                                |

<Callout type="warn">
  **Every queue field must be greater than `0` — the server rejects `0` at startup.** The
  CRD schema already enforces this with `minimum: 1` on all ten queue fields, so a Helm
  value of `0` is rejected by the API server *and&#x2A; would fail server validation. Treat the
  effective minimum for every queue setting as &#x2A;*`1`**.
</Callout>

## Example [#example]

Set message retention on each target. This is a single-setting snippet — see the
[Docker guide](/configure/docker) and the
[Kubernetes guide](/configure/kubernetes) for complete, runnable configurations.

<Tabs items="[&#x22;Docker&#x22;, &#x22;Helm&#x22;]">
  <Tab value="Docker">
    ```yaml title="config.yaml"
    store:
      maxretention: 1440
    ```
  </Tab>

  <Tab value="Helm">
    ```yaml title="values.yaml"
    store:
      messagesRetentionMinutes: 1440
    ```
  </Tab>
</Tabs>

For the full Docker delivery methods (env vars, mounted `config.yaml`, the `CONFIG`
variable) see the [Docker guide](/configure/docker); for `values.yaml` mapped to
the `KubemqCluster` spec see the [Kubernetes guide](/configure/kubernetes).
