# QoS and sessions (/connectors/mqtt/concepts/qos-and-sessions)



The KubeMQ MQTT connector supports **QoS 0, 1, and 2**. The effective QoS interacts with KubeMQ
pattern semantics in non-obvious ways — Queue consumption requires QoS ≥ 1 and uses PUBACK as its
acknowledgement signal. Sessions are **in-memory and node-local**: a `clean_session=false`
reconnect to the **same** broker node restores subscriptions.

<Callout type="warn">
  **Retain is silently dropped, and there are no durable subscriptions.** A runtime PUBLISH with the
  retain flag returns a success PUBACK (`0x00`) but the retain is ignored — no retained message is
  stored. A CONNECT that sets a **Will with retain** is refused with CONNACK `0x9A`
  (retain-not-supported). Do not design around retained messages or durable (cross-restart)
  subscriptions on this connector; use the **Events-Store** pattern when you need persistence.
</Callout>

## QoS levels [#qos-levels]

| QoS                   | Wire semantics                                               | KubeMQ pattern constraints                                     |
| --------------------- | ------------------------------------------------------------ | -------------------------------------------------------------- |
| **0** — at-most-once  | Fire-and-forget; no PUBACK                                   | Events / Events-Store publish and subscribe: allowed           |
| **1** — at-least-once | Publish: PUBACK required; subscribe: PUBACK on each delivery | **Required for Queue consume** (`$share`); recommended for RPC |
| **2** — exactly-once  | Full PUBREC / PUBREL / PUBCOMP handshake                     | Supported at the MQTT layer; routed identically to QoS 1       |

The broker advertises &#x2A;*`MaxQos = 2`** (configurable via `CONNECTORSMQTT_CAPABILITIES_MAX_QOS`).
If a subscriber requests a higher QoS than the publisher used, delivery QoS is
`min(subscribe QoS, publish QoS)` per standard MQTT semantics — a pure MQTT-layer operation that
does not affect KubeMQ routing.

## QoS per pattern [#qos-per-pattern]

### Events and Events-Store [#events-and-events-store]

Any QoS is permitted. QoS 0 is common for high-throughput telemetry where occasional loss is
acceptable; QoS 1 is recommended for reliable delivery. Events-Store always stores messages durably
on the KubeMQ side regardless of MQTT QoS — but over MQTT, Events-Store subscriptions are &#x2A;*always
`StartNewOnly`**: there is **no historical replay**, regardless of QoS or session state.

### Queues [#queues]

| Operation                          | Required QoS | Notes                                                              |
| ---------------------------------- | ------------ | ------------------------------------------------------------------ |
| Produce (publish to `queues/<ch>`) | Any          | QoS 0 produce is accepted; no MQTT-layer acknowledgement guarantee |
| Consume (`$share/<g>/queues/<ch>`) | **≥ 1**      | A QoS 0 shared-queue subscribe is rejected with SUBACK `0x83`      |

**Ack-on-PUBACK model.** A Queue message is acknowledged to KubeMQ when the broker receives the
**PUBACK** from the consuming client. Until the PUBACK arrives, the message stays in-flight. See
[Queues](/connectors/mqtt/how-to/queues) for the full consume model.

### Commands and Queries (RPC) [#commands-and-queries-rpc]

RPC is **MQTT 5.0 only**. QoS 1 is required on both the `$reply/<clientID>/...` subscribe and the
`commands/` or `queries/` publish. See [Commands](/connectors/mqtt/how-to/commands) and
[Queries](/connectors/mqtt/how-to/queries).

## Queue ack timeout and redelivery [#queue-ack-timeout-and-redelivery]

The connector tracks one in-flight Queue message per MQTT client. If no PUBACK arrives within
`QueueAckTimeoutSeconds` (default **30 s**, via `CONNECTORSMQTT_QUEUE_ACK_TIMEOUT_SECONDS`), it
negatively acknowledges the message to KubeMQ and the message is redelivered to the next available
consumer.

| Event                                      | KubeMQ action                                      |
| ------------------------------------------ | -------------------------------------------------- |
| PUBACK received within timeout             | Acknowledge — message removed from the queue       |
| No PUBACK within `QueueAckTimeoutSeconds`  | Negative-ack → redeliver to another consumer       |
| Client disconnects with a pending delivery | Immediate negative-ack → requeue (no message loss) |

The disconnect-with-pending behaviour means a **clean shutdown without PUBACK causes immediate
requeue** — the correct durability guarantee.

### Graceful shutdown [#graceful-shutdown]

When the KubeMQ server shuts down, the connector sends DISCONNECT reason code `0x8B`
(server-shutting-down) to all connected clients, and any in-flight Queue messages are immediately
negatively acknowledged and requeued.

## Sessions [#sessions]

### In-memory and node-local [#in-memory-and-node-local]

Sessions are stored **in memory only**, on the node that holds the connection. There is **no
cross-node session replication**.

| Property           | Value                                                              |
| ------------------ | ------------------------------------------------------------------ |
| Storage            | In-memory                                                          |
| Scope              | Node-local                                                         |
| Max session expiry | 3600 s (`CONNECTORSMQTT_CAPABILITIES_MAX_SESSION_EXPIRY_SECONDS`)  |
| Max message expiry | 86400 s (`CONNECTORSMQTT_CAPABILITIES_MAX_MESSAGE_EXPIRY_SECONDS`) |

### `clean_session=false` (3.1.1) / `CleanStart=false` (5.0) [#clean_sessionfalse-311--cleanstartfalse-50]

When a client reconnects with `clean_session=false` to the **same node**, the broker restores its
active subscriptions and any pending (undelivered) messages for those subscriptions.

<Callout type="warn">
  Reconnecting to a **different node** in a multi-node cluster does **not** restore the session —
  sessions are node-local. Clients that need durable sessions in a clustered environment should pin
  to the same node or use the **Events-Store** pattern for replay.
</Callout>

### `clean_session=true` / `CleanStart=true` [#clean_sessiontrue--cleanstarttrue]

The broker discards the previous session on connect — all subscriptions and pending deliveries are
cleared. This is the recommended setting for stateless consumers and is what the examples use.

### Session takeover [#session-takeover]

If a client reconnects with the &#x2A;*same `ClientID`** while the previous connection is still active,
the broker performs a takeover: the old connection is closed and the new one inherits the session
state (when `clean_session=false`).

## ReceiveMaximum and inflight limits [#receivemaximum-and-inflight-limits]

| Setting          | Default | Env var                                       |
| ---------------- | ------- | --------------------------------------------- |
| `ReceiveMaximum` | 1024    | `CONNECTORSMQTT_CAPABILITIES_RECEIVE_MAXIMUM` |
| `MaxInflight`    | 8192    | `CONNECTORSMQTT_CAPABILITIES_MAX_INFLIGHT`    |

These caps apply **per broker**, not per client. When `ReceiveMaximum` is reached on a connection,
the broker stops delivering new messages until outstanding PUBACKs are received.

## Related [#related]

<Cards>
  <Card title="Protocol versions" href="/connectors/mqtt/concepts/protocol-versions" description="The 3.1.1 vs 5.0 feature matrix — including which capabilities require QoS ≥ 1 and MQTT 5.0." />

  <Card title="Topic mapping" href="/connectors/mqtt/concepts/topic-mapping" description="$share Queue consume, the $reply RPC namespace, and the prefix grammar QoS rules attach to." />

  <Card title="Reason codes" href="/connectors/mqtt/reference/reason-codes" description="The reason-code reference — including 0x83 (QoS 0 queue), 0x8B (shutdown), and 0x9A (Will-retain)." />
</Cards>
