MQTT Fundamentals

MQTT Message Ordering Guarantees Explained

MQTT keeps messages in order, but only within narrow rules. Learn exactly what the spec guarantees, the real-world situations that reorder messages, and how sequence numbers make your consumers robust.

Numbered messages flowing through a broker in sequence, illustrating MQTT message ordering guarantees
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

MQTT message ordering is guaranteed, but only within specific limits that many developers never read. Messages from one publisher on one topic at one QoS level arrive in order; almost everything beyond that is not promised. This article explains exactly what the specification guarantees, the common situations where messages appear out of order, and how to add sequence numbers so your consumers can detect and handle it.

What the MQTT Specification Guarantees

Both MQTT 3.1.1 and MQTT 5 define an "Ordered Topic". By default a broker must treat every topic as ordered, which leads to this guarantee:

Messages published by one client to one topic, at the same QoS level, are delivered to each subscriber in the order they were published.

The spec also requires that when a client resends unacknowledged QoS 1 and QoS 2 messages after a reconnect, it does so in the original order, and that acknowledgements are sent in the order the packets were received.

Read that guarantee carefully and the gaps become obvious. It says nothing about:

  • Messages from different publishers on the same topic.
  • Messages from one publisher on different topics.
  • Messages from one publisher on the same topic but at different QoS levels.
  • What your application does after the client library hands over the message.

Where MQTT Message Ordering Breaks Down

Situation Ordering guaranteed? Why
One publisher, one topic, same QoS Yes The Ordered Topic rule
One publisher, two topics No Each topic is ordered independently
Two publishers, one topic No No global clock; arrival order at the broker wins
Same topic, mixed QoS 0 and QoS 1 No Different delivery paths and retries
QoS 0 over a dropped connection Order kept, but gaps Lost messages are not resent
QoS 1 with redelivery Duplicates can appear A resent message may follow newer ones it already preceded
Shared subscription No global order Messages are spread across consumers
Multithreaded consumer No Your worker pool reorders processing

Two of these deserve a closer look.

Shared subscriptions. With $share/group/topic, the broker distributes messages across several consumers for load balancing. Each consumer sees a subset, and they process in parallel, so there is no meaningful global order. If ordering per device matters, design so each device's messages always reach the same consumer, or reorder downstream. Our MQTT shared subscriptions guide covers the trade-off.

Redelivery and duplicates. QoS 1 is at-least-once. If an acknowledgement is lost, the sender retransmits with the DUP flag set. A subscriber may then see message 5 again after it has already processed 6 and 7. This is a duplicate rather than a true reorder, but to naive code it looks like time went backwards. The fix is covered in handling MQTT duplicate messages.

Demonstrating Ordering on a Public Broker

The quickest experiment uses the public MQTT test broker at broker.connectmq.com. Note its limit of 10 messages per second per client, so the publisher below throttles itself.

Subscriber:

mosquitto_sub -h broker.connectmq.com -p 1883 -i ord-sub-h5d3 -q 1 \
  -t 'connectmq/demo/h5d3/#' -v

Publisher, one topic, QoS 1, numbered messages:

for i in $(seq 1 20); do
  mosquitto_pub -h broker.connectmq.com -p 1883 -i ord-pub-h5d3 -q 1 \
    -t connectmq/demo/h5d3/seq -m "$i"
  sleep 0.2
done

You will see 1 to 20 in order. Note that each mosquitto_pub call is a separate connection, so the sequential loop is what keeps order here. Now run two loops in parallel on two different topics, seq-a and seq-b. Each topic stays in order, but lines from the two topics interleave unpredictably. Nothing is wrong: cross-topic order is simply not part of the contract.

Adding Sequence Numbers

When order matters to your application, carry it in the payload. A monotonic sequence number per publisher, plus a timestamp, lets consumers detect gaps, duplicates and stale messages. Here is a complete paho-mqtt 2.x publisher and consumer pair:

import json
import time
import uuid
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/h5d3/pump-07/telemetry"
last_seq = {}  # device -> highest sequence seen

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC, qos=1)

def on_message(client, userdata, msg):
    data = json.loads(msg.payload)
    dev, seq = data["device"], data["seq"]
    prev = last_seq.get(dev)
    if prev is not None and seq <= prev:
        print(f"stale or duplicate seq={seq} (last={prev}), dropped")
        return
    if prev is not None and seq > prev + 1:
        print(f"gap: missing {prev + 1}..{seq - 1}")
    last_seq[dev] = seq
    print(f"apply seq={seq} value={data['value']}")

sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"ord-c-{uuid.uuid4().hex[:6]}")
sub.on_connect = on_connect
sub.on_message = on_message
sub.connect(BROKER, 1883, keepalive=30)
sub.loop_start()
time.sleep(1)

pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"ord-p-{uuid.uuid4().hex[:6]}")
pub.connect(BROKER, 1883, keepalive=30)
pub.loop_start()
for seq in [1, 2, 3, 5, 4, 6]:  # 4 deliberately sent late
    body = {"device": "pump-07", "seq": seq, "ts": time.time(), "value": 20 + seq}
    pub.publish(TOPIC, json.dumps(body), qos=1).wait_for_publish()
    time.sleep(0.2)

time.sleep(2)
for c in (pub, sub):
    c.loop_stop()
    c.disconnect()

The consumer applies 1, 2, 3, reports a gap before 5, drops the late 4 as stale, then applies 6. Whether to drop, buffer or accept late messages depends on the data, which brings us to strategy.

Choosing an Ordering Strategy

  1. Last value wins (state). For readings such as temperature, drop anything older than what you have. The code above does this.
  2. Reorder buffer (event streams). Hold messages briefly and release them in sequence order, giving up on gaps after a timeout. Useful when every event matters, such as counters.
  3. Idempotent upserts (storage). Write each message keyed by device and sequence or timestamp. The database ends up correct regardless of arrival order.
  4. One topic per ordered stream. If commands must be applied in order, publish them on a single topic from a single client at a single QoS.
  5. Avoid mixing QoS on one stream. Pick one QoS per topic.

Ordering Across Reconnects and Offline Periods

Reconnects are where ordering assumptions are most often tested. When a subscriber with a persistent session comes back, the broker first resends any messages that were in flight and unacknowledged, then delivers the messages queued while the client was offline, then continues with live traffic. Within one topic from one publisher the order still holds, but the consumer suddenly receives a burst of older messages followed by current ones.

Two things help here. First, keep processing per device sequential, so a burst is applied in order rather than raced by parallel workers. Second, use timestamps in the payload to decide how to present catch-up data: a dashboard may want to skip straight to the latest value, while a historian should store every reading with its original time. If queued messages are only useful for a limited time, an MQTT 5 message expiry keeps very old ones out of the burst entirely.

On the publishing side, devices that buffer readings locally during a network outage should send the buffer in sequence order before resuming live publishing, and on the same topic and QoS as normal traffic.

Ordering Pitfalls and Troubleshooting

Sequence resets after reboot. A device that restarts at seq 1 looks like it is sending stale data. Include a boot ID or epoch counter with the sequence, or persist the counter in flash.

Clock-based ordering fails. Device clocks drift and sometimes reset to 1970. Prefer sequence numbers for ordering and timestamps for display.

Order lost inside your app. Async handlers, thread pools and queues in the consumer reorder freely. Process each device's stream on a single worker, or partition by device ID.

In-flight window effects. MQTT 5's receive maximum lets several QoS 1 messages be in flight at once. Ordering on the wire still holds, but retries under packet loss make duplicates more likely. See the MQTT receive maximum article for tuning.

Ordering on a Managed Broker

ConnectMQ follows the MQTT ordering rules per topic for MQTT 3.1.1 and 5.0 clients. Shared subscriptions on the paid Serverless plan give you horizontal scaling for consumers, with the usual ordering trade-off described above. Running a hosted MQTT broker does not change the protocol guarantees, but it removes the operational work so you can concentrate on consumer design. For the wider protocol context, see the MQTT tutorial.

Get Started with ConnectMQ

Create a broker on the free Serverless plan at app.connectmq.com, no credit card required. Run the sequence-number example against your own ConnectMQ credentials and watch the stream in the portal's live topic explorer, then move it to production on a cloud MQTT broker that scales with your fleet.

Frequently Asked Questions

Does MQTT guarantee message order?

Yes, for messages from one client on one topic at the same QoS. There is no guarantee across topics, publishers or mixed QoS levels.

Does QoS 2 guarantee ordering?

QoS 2 guarantees exactly-once delivery per hop. Ordering still follows the same per-topic, per-publisher rule.

Why do I see an older message after a newer one?

Usually a QoS 1 redelivery, a second publisher, or parallel processing in your consumer. Sequence numbers make it easy to tell which.

Are shared subscriptions ordered?

Not globally. Each message goes to one member of the group, and members process in parallel.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.