MQTT has strong and well-defined delivery semantics.
QoS 0 provides at-most-once delivery.
QoS 1 provides at-least-once delivery, so duplicates can occur.
QoS 2 provides exactly-once delivery for the MQTT protocol flow between the relevant MQTT sender and receiver.
Those guarantees are valuable.
They are also frequently interpreted more broadly than the protocol defines them.
An MQTT QoS guarantee does not automatically mean that the business event completed its entire journey.
QoS has a boundary
Consider:
Sensor → MQTT broker → consumer → HTTP API → cloud application
The sensor publishes with QoS 2.
MQTT completes that delivery correctly.
The consumer now has the message exactly once through that MQTT exchange.
Then the consumer sends the event to an HTTP API.
The API is unavailable.
The overall business event did not reach the cloud application, even though MQTT provided exactly the delivery semantics requested of it.
There is no contradiction here.
MQTT completed its part.
The next delivery boundary failed.
The protocol is precise about its scope
MQTT QoS describes the delivery of an Application Message between one MQTT sender and one MQTT receiver.
That is a very useful guarantee because the sender and receiver know how to coordinate the MQTT protocol flow.
But the broker cannot guarantee what arbitrary application code does afterwards.
It does not know whether a subscriber writes to a database successfully.
It cannot guarantee that a later REST call succeeds.
It cannot make a third-party SaaS service available.
Those are different systems with their own semantics.
QoS 1 and duplicates
QoS 1 is often a sensible choice for IoT.
The message is delivered at least once, which means repeated delivery is possible.
That is not a flaw.
It is the consequence of preferring a duplicate over silent loss when the protocol cannot be certain whether a previous attempt completed.
Applications using QoS 1 should therefore be prepared for duplicate messages where it matters.
For many sensor streams, duplicates may be harmless.
For a command such as “open valve” or an event that creates a financial transaction, idempotent processing may be essential.
QoS 2 solves a different problem than many people think
QoS 2 adds protocol work to provide exactly-once delivery within the MQTT exchange.
That can be important.
But it does not turn the complete architecture into an exactly-once business process.
If the MQTT consumer later calls an external system, that call still has its own failure and retry semantics.
The correct question is therefore not:
“Are we using QoS 2?”
It is:
“Which part of our path is protected by QoS 2, and what happens after that boundary?”
Persistence and session configuration matter too
QoS is not the only MQTT setting that determines behaviour during disconnects.
Session state, subscriptions and broker/client persistence affect what happens while participants are unavailable.
A system can choose good QoS semantics and still have unexpected behaviour if the surrounding session and persistence configuration does not match the reliability requirement.
This is another reason to think in terms of the complete path rather than one setting.
MQTT can stay exactly where it is
None of this is an argument against MQTT.
MQTT is excellent at what it does.
In many IoT architectures, it should remain the local messaging backbone.
The useful design step is simply to mark the boundary around it.
For example:
Device → MQTT → Edge delivery layer → Cloud → Destination
MQTT handles efficient local pub/sub and its defined QoS semantics.
The next layer takes responsibility for the next delivery problem.
Clear boundaries are easier to operate than asking one technology to provide guarantees outside its scope.
For a more practical look at what “delivered” means depending on where you stand, continue with MQTT Says Delivered. But Was It?