QoS and retained messages
Quality of Service (QoS) sets how hard MQTT tries to deliver a message. MQTT defines three levels, QoS 0, 1 and 2 (MQTT 5.0 §4.3, MQTT 3.1.1 §4.3).
QoS applies to each hop
Section titled “QoS applies to each hop”QoS isn’t an end-to-end guarantee. It applies separately to two hops:
- Publisher to broker: the QoS that the publisher puts in the PUBLISH packet.
- Broker to subscriber: the lower of the message QoS and the QoS granted to the subscription (§3.8.4).
For example, a message published at QoS 1 reaches a client that subscribed at QoS 0 as a QoS 0 message.
mosquitto_sub subscribes at QoS 0 unless you pass -q.
QoS 0: at most once
Section titled “QoS 0: at most once”The sender sends a single PUBLISH packet. There’s no acknowledgement and no retransmission, so a message can be lost when the connection drops.
Sender Receiver │ ── PUBLISH (QoS 0) ──────▶ │ deliverQoS 1: at least once
Section titled “QoS 1: at least once”The sender gives the PUBLISH a packet identifier and waits for a PUBACK. Until the PUBACK arrives, it keeps the message as unacknowledged.
Sender Receiver │ ── PUBLISH (QoS 1, id 1) ─▶ │ deliver │ ◀─────────── PUBACK (id 1) ─ │- When the client reconnects to the same session, unacknowledged messages are sent again with the DUP flag set (§4.4). MQTT 5.0 resends only on reconnect.
- A receiver can therefore get the same message more than once, and the application has to drop duplicates.
QoS 2: exactly once
Section titled “QoS 2: exactly once”Four packets guarantee delivery without duplicates.
Sender Receiver │ ── PUBLISH (QoS 2, id 1) ─▶ │ store id 1 │ ◀─────────── PUBREC (id 1) ─ │ │ ── PUBREL (id 1) ─────────▶ │ deliver once, forget id 1 │ ◀────────── PUBCOMP (id 1) ─ │The receiver remembers the packet identifier until the PUBREL arrives, so it never delivers the message twice. The price is a second round trip, which adds latency and broker load.
You can watch the real flow with mosquitto_pub -d. This is the output of one QoS 2 publication:
Client auto-DB1B2CB5-… sending PUBLISH (d0, q2, r0, m1, 'factory/line1/temperature', ... (4 bytes))Client auto-DB1B2CB5-… received PUBREC (Mid: 1)Client auto-DB1B2CB5-… sending PUBREL (m1)Client auto-DB1B2CB5-… received PUBCOMP (Mid: 1, RC:0)Choosing a QoS
Section titled “Choosing a QoS”| QoS | Fits | Watch out for |
|---|---|---|
| 0 | Frequently updated measurements where a lost sample doesn’t matter | Loss when the connection drops |
| 1 | Most equipment data and events | Duplicates; remove them with a sequence number or identifier |
| 2 | Commands and counts that must never be duplicated | Highest latency and load |
Message order
Section titled “Message order”The broker forwards the messages that one client publishes to one topic at one QoS in the order it received them (§4.6). The order between different publishers, or between different topics, isn’t defined.
Retained messages
Section titled “Retained messages”When the RETAIN flag of a PUBLISH is 1, the broker stores the message as the retained message of its topic (§3.3.1.3).
- Each topic keeps only its latest retained message.
- When a new subscription matches, the broker sends the retained message right away, with RETAIN set to 1.
- Publishing a retained message with an empty payload deletes the retained message of that topic.
- A retained message isn’t history. It only tells a new subscriber the last value.
MQTT 5.0 adds subscription options that control retained messages (§3.8.3.1):
| Option | Value | Behavior |
|---|---|---|
| Retain Handling | 0 | Send retained messages on every subscribe (default). |
| 1 | Send them only when the subscription is new. | |
| 2 | Don’t send them. | |
| Retain As Published | 0 | Clear RETAIN on normal forwarding (default). |
| 1 | Keep the RETAIN value the publisher sent. |
A broker without retained-message support sets the Retain Available property of CONNACK to 0 (§3.2.2.3.5).
What the PDComm simulator uses
Section titled “What the PDComm simulator uses”- The simulator publishes data at QoS 1 with RETAIN 0, and subscribes at QoS 1.
- Because it doesn’t retain data, a simulator subscriber doesn’t receive data that was published before it subscribed.
- With QoS 1, the same DataSetMessage can arrive twice. Use the Sequence of each received message to spot duplicates.