MQTT QoS, retained messages, and sessions, explained
The three MQTT features that decide whether your telemetry is lossy, stale, or exactly-once — and when each setting is correct.
MQTT is easy to start and easy to misconfigure, because the defaults quietly choose "at most once, no history, forget me on disconnect" — reasonable for sensor streams, wrong for commands and state. Three mechanisms control delivery behavior: quality of service, retained messages, and session persistence.
QoS 0, 1, 2: the delivery contract
- QoS 0 (at most once) — fire and forget. Fastest, no broker acknowledgment. Correct for high-rate telemetry where the next reading supersedes the last (temperatures, vibration streams).
- QoS 1 (at least once) — broker acknowledges; the publisher retries until it hears back, so subscribers may receive duplicates. Correct for events that must arrive but tolerate repeats (alarms, counters with idempotent handling).
- QoS 2 (exactly once) — four-step handshake, no duplicates, highest overhead. Correct for billing-grade events and commands where a duplicate would double-actuate. Rarely needed on the plant floor; reach for it deliberately, not by default.
Note the subtlety: QoS is negotiated per hop (publisher→broker, broker→subscriber) and downgraded to the subscription's maximum. Publishing at QoS 2 to a QoS 0 subscriber still delivers at most once.
Retained messages: state on subscribe
A retained message is stored by the broker and delivered immediately to every new subscriber — the topic's "last known value." This is how dashboards show current state without waiting for the next publish cycle, and it is the mechanism Sparkplug B builds its birth certificates on. Retain setpoints, modes, and status; do not retain high-rate streams or event logs (a new subscriber would get one stale event presented as current state). A retained message with an empty payload and the retain flag clears the stored value.
Sessions: surviving disconnects
With a persistent session (clean-start flag off, MQTT v5), the broker keeps a client's subscriptions and queues QoS 1/2 messages while it is offline, up to configured limits. This is store-and-forward for subscribers: a historian that reboots overnight can catch up on missed alarms. But queues are bounded — size them, monitor them, and set message expiry so a week-long outage does not replay ancient commands. For publishers, Sparkplug's birth/death flow on top of sessions is what turns reconnection into a well-defined state resync instead of a silent gap.
Defaults that work
Telemetry: QoS 0, no retain, persistent sessions on critical subscribers. State and setpoints: QoS 1, retained. Alarms: QoS 1, no retain, persistent subscriber sessions with expiry. Commands: QoS 1 with command-echo verification (or QoS 2 where duplication is dangerous). Document the policy per topic family — the next integrator will thank you.
References
- MQTT: the standard for IoT messaging
- HiveMQ Blog — deep MQTT feature guides
- Eclipse Sparkplug — specification and resources
Cite this page: MQTT QoS, retained messages, and sessions, explained
, Shopfloor, 2026-09-21. https://shopfloorspace.yunusemrevurgun.com/articles/mqtt-qos-retained-sessions-explained/