MQTT Fundamentals

MQTT Offline Messages: How Brokers Queue Messages for Disconnected Clients

MQTT brokers can hold messages for clients that are temporarily offline, but only under specific conditions. Learn the rules, the limits and a reproducible test you can run in five minutes.

MQTT offline messages illustration showing a broker queue holding QoS 1 messages for a disconnected IoT device until it reconnects
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 offline messages are the publications a broker holds for a subscriber that is temporarily disconnected and delivers once it comes back. This article explains exactly which messages qualify, what limits apply on real brokers, and walks you through a reproducible test so you can watch queuing and delivery happen yourself.

The Four Conditions for MQTT Offline Messages

Queuing is not automatic. A broker queues a message for an offline client only when all of these are true:

Condition What to check
The client has a persistent session MQTT 3.1.1: cleanSession = false. MQTT 5: Clean Start = false and Session Expiry Interval > 0
The session has not expired MQTT 5 expiry not yet elapsed, broker limits not exceeded
The client was already subscribed to a matching topic filter The subscription must exist before it went offline
The effective QoS is 1 or 2 Delivery QoS is the lower of the publish QoS and the subscription QoS

If any condition fails, the message is simply not delivered to that client. There is no error for the publisher, because MQTT decouples publishers from subscribers by design. That is why "my device missed a command" bugs are so common: the publisher sees a successful PUBACK from the broker, but the broker had no reason to keep the message for that particular subscriber.

What About QoS 0?

QoS 0 is "at most once". The specification allows a broker to queue QoS 0 messages for offline sessions, but it does not require it, and most brokers do not. Treat QoS 0 as live-only. The differences between levels are covered in detail in MQTT QoS levels explained.

What About Retained Messages?

Retained messages are a separate mechanism. The broker keeps the last retained message per topic and sends it to anyone who subscribes, whether or not they have a session. They solve "what is the current value?", not "what did I miss?". A reconnecting client with a clean session still gets the latest retained value on subscribe, but none of the intermediate updates. A good design often uses both: retained messages for state, queued QoS 1 messages for events and commands.

Limits That Apply to Offline Queues

Every broker puts bounds on queues, because a disconnected client that never returns would otherwise consume memory forever.

  • Session lifetime. In MQTT 5, the session expiry interval decides how long the queue survives. When it expires, queued messages are discarded. On the public MQTT test broker broker.connectmq.com, session expiry is capped at 30 minutes. Choosing values is covered in MQTT session expiry interval.
  • Queue depth. Brokers cap the number of queued messages or bytes per session. When the cap is hit, new messages are dropped or old ones evicted, depending on the broker.
  • Message expiry. An MQTT 5 publisher can set a Message Expiry Interval. If a message sits in a queue longer than that, the broker discards it instead of delivering stale data.
  • Message size. Plan and broker limits on message size apply to queued messages too (64 KB on the public broker, 256 KB on the ConnectMQ Serverless Free plan, 1 MB on the paid Serverless plan).
  • Broker restarts. Where queues live depends on the broker. ConnectMQ keeps sessions and their queues in memory: they survive client disconnects for the granted session lifetime, but they are not kept across a broker restart. Clients should detect a lost session (Session Present = 0 in CONNACK), re-subscribe, and rely on retained messages or application-level resync for state.

Reproducible Test: Watch Offline Delivery Happen

This test takes about five minutes with the mosquitto command-line tools. Replace q5t9 with your own random string, and keep payloads harmless: the public area is readable by everyone.

  1. Create the session. Connect a subscriber with a fixed ID, Clean Start false, a 15 minute session expiry and QoS 1:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
  -i offline-sub-q5t9 -c -x 900 -q 1 -v \
  -t 'connectmq/demo/q5t9/cmd/#'
  1. Go offline. Press Ctrl+C.

  2. Publish a mix of QoS levels while the subscriber is away:

H="-h broker.connectmq.com -p 1883 -V mqttv5 -i offline-pub-q5t9"
mosquitto_pub $H -q 0 -t 'connectmq/demo/q5t9/cmd/a' -m 'qos0 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/b' -m 'qos1 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/c' -m 'short-lived' \
  -D publish message-expiry-interval 5
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/other' -m 'not subscribed'
  1. Wait at least 10 seconds, then rerun the command from step 1.

You should see only connectmq/demo/q5t9/cmd/b qos1 message. Here is why each of the others is missing:

  • cmd/a was QoS 0, so it was not queued.
  • cmd/c had a 5 second message expiry and was discarded in the queue.
  • other did not match the subscription filter.
  1. Repeat without -c in step 1. Nothing is delivered on reconnect, because the session was discarded when the connection closed.

The Same Pattern in Python

A device that reconnects and processes everything it missed:

import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes

TOPIC = "connectmq/demo/q5t9/cmd/#"

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("Connect failed:", reason_code)
        return
    print("Connected. Session present:", flags.session_present)
    if not flags.session_present:
        # New or lost session: subscribe and fetch state another way
        client.subscribe(TOPIC, qos=1)

def on_message(client, userdata, msg):
    print(f"Processing {msg.topic}: {msg.payload.decode()}")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id="offline-py-q5t9",
                     protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.reconnect_delay_set(min_delay=1, max_delay=60)

props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 900

client.connect("broker.connectmq.com", 1883, keepalive=60,
               clean_start=False, properties=props)
client.loop_forever()

Designing Around Offline Clients

Queuing helps with short gaps, but it should not be your only reliability tool. A robust design combines several mechanisms:

  1. Use QoS 1 for commands and events, QoS 0 for high-frequency telemetry where the next reading replaces the last.
  2. Keep session expiry realistic. Long enough to cover typical outages, short enough that dead devices do not accumulate queues.
  3. Set message expiry on time-sensitive commands, so a device that returns after an hour does not execute an outdated instruction.
  4. Publish state as retained messages, so any client can resync instantly without depending on a queue.
  5. Make handlers idempotent. QoS 1 can deliver duplicates after reconnects; include a message ID or sequence number in the payload.
  6. Handle Session Present = 0 as a normal event, not an error.
  7. Expect a burst on reconnect. Clients that were offline may receive many messages at once; MQTT 5 Receive Maximum limits how many unacknowledged messages arrive in parallel.

For background on sessions themselves, read MQTT persistent sessions and the clean session flag. If you are just getting started, the MQTT tutorial covers the publish/subscribe basics first.

Troubleshooting Missing Offline Messages

  • Nothing arrives after reconnect: confirm the client ID is identical, Clean Start is false and, in MQTT 5, the session expiry is non-zero.
  • Some messages arrive, others do not: compare QoS of each publish and the subscription, and check for message expiry.
  • Messages arrive twice: expected with QoS 1 after an interrupted acknowledgement; deduplicate in the application.
  • Session Present is 0 unexpectedly: the session expired, the client ID changed, or the broker restarted.

Queue Offline Messages with ConnectMQ

ConnectMQ is a hosted MQTT broker that supports persistent sessions, session expiry and message expiry on MQTT 5.0 as well as MQTT 3.1.1 clients. The Serverless Free plan supports QoS up to 1, which is all offline queuing requires, and QoS 2 is available on paid plans. Create your free broker with no credit card, connect with YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID, and run the test above against your own private endpoint. If you are comparing providers, see what to look for in a cloud MQTT broker.

Frequently Asked Questions

Does MQTT store messages when the subscriber is offline?

Only if the subscriber has a persistent session, was already subscribed to a matching topic, and the effective QoS is 1 or 2. Otherwise the message is not kept for that client.

Why are my QoS 0 messages lost while the device is offline?

QoS 0 messages are normally not queued for offline sessions; the specification makes it optional. Publish and subscribe with QoS 1 if the message must survive a disconnect.

How long does a broker keep offline messages?

Until the session expires, a queue limit is hit, the message's own expiry passes, or the broker restarts if sessions are held in memory. On ConnectMQ sessions live in memory, and the public test broker caps session expiry at 30 minutes.

Do retained messages replace offline queuing?

No. A retained message gives a new subscriber the latest value on a topic, while offline queuing delivers every QoS 1 or 2 message the client missed. Most systems use both.

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.