MQTT QoS Levels Explained

MQTT lets you choose, per message, how hard the protocol should work to deliver it. QoS 0 is fire-and-forget — the message goes out and the publisher moves on. QoS 1 ensures delivery but accepts duplicates. QoS 2 ensures exactly-once delivery via a four-step handshake. Each level has different overhead, different failure modes, and different correct use cases. The default QoS choice often gets made by mistake; this guide walks the three levels in detail so you can pick deliberately.

The three QoS levels at a glance

QoSGuaranteeHandshakeOverhead per messageDuplicates possible?
0At most once1 packet (PUBLISH)LowestNo (but can be lost)
1At least once2 packets (PUBLISH + PUBACK)2x QoS 0Yes
2Exactly once4 packets (PUBLISH + PUBREC + PUBREL + PUBCOMP)4x QoS 0No

QoS 0: at most once (fire and forget)

The publisher sends a PUBLISH packet and immediately considers the message handled. The broker delivers it once to each subscriber on a best-effort basis. No acknowledgments anywhere.

Wire format:

Publisher → Broker: PUBLISH [topic, payload, QoS=0]
Broker → Subscriber: PUBLISH [topic, payload, QoS=0]
(no acks, no retries)

Failure modes:

  • Network packet loss between publisher and broker: message lost.
  • Network packet loss between broker and subscriber: message lost for that subscriber.
  • Broker crash before delivering: message lost.
  • Subscriber offline: message lost (no offline storage at QoS 0).

Use cases where QoS 0 is correct:

  • High-frequency telemetry where new readings replace old ones. A vibration sensor publishing 100 readings per second. Losing 1 in 10,000 is fine.
  • Real-time updates where stale data is useless. Live position updates from a vehicle tracker — if you missed the position from 30 seconds ago, you've already received 10 newer positions.
  • Multicast-style fanout where any reasonable success rate is acceptable. Status updates to many dashboards.

QoS 1: at least once (with possible duplicates)

The publisher sends a PUBLISH packet with a unique packet identifier and stores it in its outbound queue. The broker acknowledges with PUBACK. Only on receiving PUBACK does the publisher remove the message from its queue. If no PUBACK arrives within a timeout, the publisher retransmits the same message (with the DUP flag set).

Wire format:

Publisher → Broker: PUBLISH [id=42, topic, payload, QoS=1]
Broker stores message, delivers to subscribers
Broker → Publisher: PUBACK [id=42]
Publisher removes message from queue

If PUBACK is lost or delayed:
Publisher → Broker: PUBLISH [id=42, topic, payload, QoS=1, DUP=1]
Broker may or may not detect the duplicate; subscribers may receive twice

Subscriber side mirrors this — broker sends PUBLISH, subscriber sends PUBACK. If subscriber's PUBACK is lost, broker resends.

Duplicates occur when an acknowledgment is lost. The publisher believes the broker didn't receive the message; the broker did receive it; both sides assume the worst and a duplicate happens.

Use cases where QoS 1 is correct (the most common choice):

  • Commands to devices. "Turn on the relay." Better to potentially execute twice (idempotent at application level) than to lose the command.
  • Aggregated sensor data. "Average temperature for last hour." Losing it means an hour of data lost; duplicates are merely redundant.
  • Most typical IoT messaging.

Application-level idempotency is the standard pattern: include a unique message ID in the payload, and have the subscriber deduplicate by ID. This makes QoS 1 effectively exactly-once at the application layer without paying the QoS 2 protocol overhead.

QoS 2: exactly once (the four-way handshake)

QoS 2 prevents duplicates via a more elaborate handshake. The cost is 4 network round trips per message and broker state to track in-flight packet IDs.

Wire format:

Publisher → Broker: PUBLISH [id=42, topic, payload, QoS=2]
Broker stores message (NOT delivered to subscribers yet)
Broker → Publisher: PUBREC [id=42]

Publisher → Broker: PUBREL [id=42]
Broker now delivers message to subscribers
Broker → Publisher: PUBCOMP [id=42]
Both sides clean up state for packet 42

How this prevents duplicates:

  • If PUBLISH is duplicated, the broker recognizes the packet ID is already in its "received but not yet released" state. It re-sends PUBREC but doesn't re-deliver.
  • If PUBREC is lost and publisher retransmits PUBLISH, broker still has the state and only acks; no duplicate delivery.
  • If PUBREL is lost and broker retransmits PUBREC, publisher knows it's been received but not released; resends PUBREL.
  • If PUBCOMP is lost, publisher retransmits PUBREL; broker sees ID is in "released" state and just re-sends PUBCOMP.

The exactly-once guarantee depends on persistent state on both sides. If a publisher crashes after PUBLISH but before PUBREC, the broker doesn't know whether to expect a retry. The MQTT spec requires both sides to persist in-flight QoS 2 messages so they can resume across restarts.

Use cases where QoS 2 is correct (rare):

  • Billing or financial events where duplicates cause real harm and the application cannot deduplicate at its own layer.
  • State changes with side effects like "send SMS notification" where running twice has user-visible consequences.
  • Industrial commands with hard physical consequences ("rotate valve to position X") where applying the change twice is meaningfully different from applying it once.

In practice, even these use cases are usually handled at QoS 1 + application-level idempotency, because QoS 2 has subtle interaction with broker restarts and clustered brokers that the application-level approach avoids.

The min(publish, subscribe) rule

QoS is set independently by publisher and subscriber. The effective QoS for any given delivery is:

effective_qos = min(publish_qos, subscribe_qos)

Examples:

  • Publisher uses QoS 2; subscriber subscribed at QoS 0 → broker delivers to subscriber at QoS 0. Publisher still gets QoS 2 ack from broker.
  • Publisher uses QoS 0; subscriber subscribed at QoS 2 → subscriber receives at QoS 0 (cannot upgrade what publisher sent).
  • Publisher uses QoS 1; subscriber subscribed at QoS 1 → QoS 1 end to end.

The decoupling lets subscribers choose their reliability independently. A dashboard subscriber may accept QoS 0 (lossy real-time views are fine); a database archiver may use QoS 1 (don't lose data); a billing processor may use QoS 2 (no duplicates ever).

QoS interactions with retain and offline subscribers

Retained messages and QoS

A retained message has its own retained QoS — set by the publisher's QoS at publish time. When a new subscriber connects, the retained message is delivered at min(retained_qos, subscribe_qos).

Pattern: device publishes its current state with QoS 1, retain=true. New dashboards subscribing at QoS 1 immediately receive the current state with delivery guarantee.

Offline subscribers (clean_session=false)

MQTT clients can persist subscriptions across reconnections (clean_session=false in MQTT 3.1.1, or session_expiry_interval > 0 in MQTT 5). The broker holds messages for offline subscribers up to the session lifetime.

QoS affects what gets held:

  • QoS 0: Most brokers do NOT hold QoS 0 messages for offline subscribers. They are dropped if the subscriber is offline at publish time.
  • QoS 1 and 2: Held in the broker's persistent storage and delivered when the subscriber reconnects.

This is the operational reason to use QoS 1 for command channels — even if the device is briefly offline (cellular handoff, network blip), pending commands are delivered when it reconnects.

Performance implications

Broker throughput numbers by QoS (typical EMQX or HiveMQ broker on commodity hardware):

QoSMessages/sec per broker nodeLatency P99Memory per in-flight
0500K-1M~5 msNone
1100K-300K~20 ms~200 bytes per message
220K-80K~80 ms~500 bytes per message

Roughly: QoS 1 is 5x more expensive than QoS 0 in throughput terms; QoS 2 is 10-20x more expensive. The numbers depend heavily on broker implementation, storage backend (memory vs disk), and message size, but the order-of-magnitude differences hold.

For large-scale deployments with millions of devices, QoS choice can be the difference between needing one broker node and needing twenty.

Ordering and QoS

Important subtlety: QoS does NOT preserve ordering across levels. A QoS 2 message takes longer to complete its handshake than a QoS 0 message published just after it; the QoS 0 message may be delivered to subscribers first.

Ordering is preserved within a single QoS level on a single TCP connection. For strict ordering of related messages, use one QoS level consistently for that stream.

Common QoS mistakes

  • Using QoS 2 everywhere "just to be safe." Wastes throughput. QoS 1 + idempotent subscribers achieves the same outcome at lower cost.
  • Using QoS 0 for commands. Commands lost during network blips are silently dropped. Use QoS 1 minimum.
  • Using QoS 1 for high-frequency telemetry. The per-message ack overhead reduces throughput meaningfully. QoS 0 is fine when individual readings are replaceable.
  • Assuming QoS 2 prevents application-level duplicates. If your application crashes after delivering a message but before persisting that it processed the message, on restart it will reprocess the same message. QoS prevents network-level duplicates, not application-level retries.
  • Mixing QoS levels in a stream. Ordering breaks. Pick one QoS per logical stream.

Decision rule

Default to QoS 1 unless you have a specific reason to deviate:

  • Lossy is OK and throughput matters more → QoS 0.
  • Exactly-once is critical AND application cannot deduplicate → QoS 2.
  • Anything else → QoS 1 with application-level idempotency.

Frequently Asked Questions

Which MQTT QoS level should I use?

Use QoS 1 by default. It guarantees delivery (with possible duplicates) at moderate overhead. Use QoS 0 only when message loss is acceptable — high-frequency telemetry where the next reading replaces the previous one. Use QoS 2 only when duplicates are catastrophic AND loss is unacceptable — financial transactions, billing events. For typical sensor data and commands, QoS 1 is the right balance.

What is the difference between QoS 1 and QoS 2?

QoS 1 (at least once) uses a 2-step handshake — PUBLISH + PUBACK. If the PUBACK is lost, the publisher retries, which can result in duplicates at the subscriber. QoS 2 (exactly once) uses a 4-step handshake — PUBLISH + PUBREC + PUBREL + PUBCOMP — designed so neither side commits the message until both have confirmed they've recorded it. The broker tracks message IDs to deduplicate retries. QoS 2 prevents duplicates at the cost of 2x the network round-trips and broker state.

What is the effective QoS when publisher and subscriber use different levels?

The effective QoS is min(publish QoS, subscribe QoS). If a publisher sends QoS 2 but a subscriber subscribed with QoS 0, the subscriber receives the message at QoS 0 (no acknowledgment from subscriber to broker). The broker still tracks QoS 2 semantics for its own ack to the publisher. This decoupling lets subscribers choose their own reliability without requiring publishers to coordinate.

Are MQTT QoS 1 messages always delivered exactly once if no duplicates occur?

Yes — in the lucky case where every PUBACK arrives, QoS 1 effectively delivers exactly once. But you cannot assume this; network failures can cause retries that produce duplicates at the subscriber. Subscriber applications must be idempotent at QoS 1 if duplicate processing would cause problems. Typically this means embedding a unique message ID in the application payload and tracking processed IDs.

Does QoS affect message ordering?

MQTT 3.1.1 does not guarantee ordering across QoS levels — a QoS 2 message can take longer to complete its handshake and arrive after a later QoS 0 message. Within a single QoS level on the same TCP connection, ordering is preserved. For strict ordering, use a single QoS level for related messages and avoid mixing levels for the same logical stream.

Related Guides

More From This Section