In MQTT, a publishing client has no guarantee that a subscribing client will actually receive its message, because pub/sub decouples them. The publisher’s only guarantee is that the message arrives at the broker; from there, delivery to subscribers depends on who is currently subscribed. Similarly, a client that has just connected and subscribed has no guarantee about when the next message on a topic will arrive. It might be seconds away, or it might be hours. Until the next publish, the new subscriber is in the dark about the current state of the topic.
Retained messages are MQTT’s answer to that gap. They let a publisher mark a message as the topic’s current state, the broker stores that latest value, and any client that subscribes to a matching topic later immediately receives it without having to wait. This article covers how retained messages work, how to set, replace and clear them, how they interact with wildcards and Quality of Service, and the common pattern of using them with Last Will and Testament to track device status. It is a technical reference; related mechanisms such as the publish operation, topics, and LWT each have their own dedicated articles in this category.
Table of Contents
Retained messages at a glance
| Aspect | Detail |
|---|---|
| Set by | Publisher, via the retain flag on PUBLISH |
| Stored by | Broker, against the topic |
| Delivered to | Any client that later subscribes to a matching topic filter |
| One per topic? | Yes — each new retained message overwrites the previous one |
| Cleared by | Publishing a zero-length payload with retain = true to the same topic |
| Survives client disconnect? | Yes — retention is per topic, not per client session |
| Affected by wildcards in subscriptions? | Yes — delivered on any matching subscription, with or without wildcards |
| QoS on delivery | min(stored publish QoS, subscription QoS) |
The problem retained messages solve
Suppose a sensor publishes its current temperature once a minute to home/livingroom/temperature. A dashboard client connects, subscribes to that topic, and waits. Until the sensor’s next minute is up, the dashboard has no idea what the temperature is. It is fully connected, fully subscribed, and fully blind. Worse, if the dashboard connects right after a publish, it might wait nearly the full interval before showing the first reading.
The same problem appears anywhere a subscriber needs to know the current state of something rather than wait for the next change. Device status, configuration values, the last reading from a slow-changing sensor, the current state of a switch: in every case, the subscriber needs the latest value now, not when the next event happens to fire.
Retained messages solve this directly by making the broker remember the latest value for a topic and hand it to every new matching subscriber. The publisher does not have to know who is listening or when they connected; it simply marks the message as retained, and the broker takes care of giving newcomers an immediate snapshot of the current state.
What a retained message is
A retained message is an ordinary MQTT PUBLISH with one extra bit of information set: the retain flag in the publish header is set to true. When the broker receives such a message, it does two things. First, it delivers the message to currently subscribed clients exactly as it would any other publish. Second, it stores the message as the retained value for that topic, replacing whatever (if anything) was stored there before.
After that, any client that later subscribes to a topic filter that matches this topic immediately receives the stored retained message as part of becoming subscribed, before any live messages arrive. The broker stores exactly one retained message per topic; there is no list of retained messages, only the most recent one. Each new retained PUBLISH overwrites the previous retained value for that topic.
The retain flag is also set on the delivered message, so the subscribing client can tell the difference between a freshly published live message and the broker handing it the stored value. The client can decide whether to treat retained messages differently from live ones, for example by knowing that a retained message represents historical state rather than a current event.
It is important to be clear that retained messages have no semantic meaning to the protocol beyond “this is the most recent value stored for the topic.” MQTT does not interpret payload contents, validate them, or judge whether a value is fresh or stale. The retained message is simply the most recent message that was published with the retain flag set; whether that value is still useful is up to the publisher and the application.
Retained messages and wildcards
Retained-message delivery to new subscribers uses the same wildcard matching as live-message delivery. When a client subscribes with a wildcard, the broker checks every stored retained message against the subscription’s topic filter, and sends a copy of each retained message whose topic matches the filter. This is exactly the same matching used for live messages, so the same + and # rules apply.
A concrete example. Suppose three retained messages are stored on the broker:
home/livingroom/temperature→21.5home/kitchen/temperature→19.0home/garage/door→closed
Now a new client subscribes to home/+/temperature. Immediately after the SUBACK, the broker sends both retained temperature messages, because both topics match the wildcard filter. The garage door retained message is not delivered, because its topic does not match. A different new client subscribing to home/# would receive all three retained messages, because the multi-level wildcard matches every topic beneath home.
This interaction is what makes retained messages especially powerful in well-designed topic hierarchies. A single wildcard subscription can pull the entire current state of an entire branch of the hierarchy at the moment of subscribing, with no further publishes required. A monitoring client subscribing to factory/line1/# immediately receives the last known state of every machine, sensor, and value beneath line1, courtesy of the retained messages they have all been publishing.
Retained messages and Quality of Service
A common point of confusion is which QoS level governs a retained message when it is delivered to a new subscriber. The rule is the same one that applies to live messages, with one extra piece of state stored.
When a publisher sends a retained PUBLISH, the broker stores the retained message together with its original publish QoS. Call that the stored QoS. When the retained message is later delivered to a new subscriber, the broker delivers it at the minimum of the stored QoS and the subscriber’s subscription QoS, exactly as it would downgrade a live message. The retain flag changes when the message is delivered (immediately on subscribe rather than when a live publish arrives), not how the per-hop QoS rule applies.
So if a publisher sends a retained message at QoS 2 and a new client subscribes at QoS 1, that client receives the retained message at QoS 1, with the duplicates-possible behavior of QoS 1. If the same client had subscribed at QoS 0, it would receive the retained message at QoS 0, with no acknowledgement. In both cases the broker still stores the retained message at its original QoS; only the delivery to that subscriber is downgraded, so a different subscriber arriving later with a higher subscription QoS will receive the same retained message at the higher level. The retain flag does not bypass or override QoS negotiation; it just decides whether a stored copy is delivered at all.
Sending a retained message
From a developer’s point of view, sending a retained message is straightforward: set the retain flag on the PUBLISH to true. Almost every client library exposes this as a simple boolean parameter on its publish API. The broker handles everything else, storing the message against the topic and delivering it both to current subscribers and to future ones.
There are a few things worth understanding about how the broker treats retained messages once stored. First, the storage is keyed by topic, not by publisher; the broker does not remember which client published a given retained message, only that this topic has this stored value. Second, the storage is per topic and not per session; retained messages survive client disconnections, client reconnects with clean sessions, and clients leaving the system entirely. They are not part of any client’s session state, which makes them very different from the queued messages stored in a persistent session. Third, only one retained message exists per topic at any time; publishing a new retained message to the same topic replaces the previous one atomically from the broker’s perspective.
Replacing and clearing retained messages
Because each new retained PUBLISH overwrites the previous one, replacing a retained message is the default and easiest case. A publisher that updates its reading once a minute and publishes each reading as retained does not need to delete the old retained message; the new publish simply takes its place. Most retained-message workflows in practice are just “keep publishing your current state with retain = true and the broker always has the latest.”
Deleting a retained message is a deliberate operation, and it has its own specific protocol convention: publish a zero-length (empty) payload to the topic with the retain flag set to true. The broker receives this, stores it just like any other retained message, recognizes that the payload is empty, and removes the retained value for that topic. New subscribers to a matching filter after this point no longer receive a retained message for that topic.
Two practical notes about clearing. First, the cleared retained message is itself delivered to current subscribers as a normal publish, so subscribers will see one final message with an empty payload. Applications need to handle the empty payload sensibly if they are subscribed to a topic that may be cleared. Second, clearing is essentially never necessary unless you want the topic to genuinely have no retained value going forward; in normal operation, publishing the next real value as retained is the right pattern, not deleting and republishing.
When to use retained messages
Retained messages make the most sense when newly connected subscribers need the current state of something immediately, without waiting for the next live publish. They are especially useful for status and slowly-changing data, where the gap between publishes can be long.
Status topics for components or devices are the canonical example. Take the topic myhome/devices/device1/status. When retained messages are used, every new subscriber to that topic receives the device’s status (online or offline, say) immediately on subscribing. Without retained messages, the new subscriber would have to wait until the device itself happened to publish its next status change, which could be much later. The same pattern applies to clients that publish data periodically: a temperature, a GPS coordinate, the current value of a counter. Anything where there is a meaningful “current value” benefits from being retained.
Retained messages are less appropriate when each message is genuinely an event, distinct from any other and not representative of a current state. A “button pressed” event, an alarm trigger, or a discrete command should usually not be retained, because there is no current value to expose to a new subscriber; the event happened, and re-delivering it to anyone who subscribes much later would be misleading. Use retained messages for state, not for events.
A retained message over time, step by step
It helps to trace one retained value through a full lifecycle to see how it behaves at each step. Suppose a thermostat publishes its current setpoint with retain = true.
- First publish. The thermostat sends a PUBLISH to
home/livingroom/setpointwith payload20.0and retain = true. The broker delivers this to any currently subscribed clients normally, and also stores it as the retained message for that topic. - A new subscriber connects. Some minutes later, a dashboard client connects and subscribes to
home/livingroom/setpoint. Immediately after the SUBACK, before any new publish has happened, the broker hands it the stored retained message20.0, with the retain flag set on the delivered packet so the dashboard knows this is the retained value rather than a fresh event. - The thermostat updates. Someone changes the setpoint, and the thermostat publishes
21.5to the same topic with retain = true. The broker delivers it to current subscribers (the dashboard sees the change live) and replaces the stored retained value. The previous20.0is gone from the broker’s store. - Another new subscriber connects. A second client subscribes to
home/+/setpoint, using a wildcard. The broker checks every stored retained message against this filter and findshome/livingroom/setpointmatches, so it immediately delivers the retained value21.5to the new client. Any other retainedsetpointtopics underhomewould be delivered too. - The thermostat is decommissioned. Before going offline, the thermostat publishes a zero-length payload to
home/livingroom/setpointwith retain = true. Current subscribers receive this empty publish (and need to handle it sensibly). The broker recognizes the empty retained payload as a clear request and removes the stored retained value. - A late subscriber arrives. A third client now subscribes to
home/livingroom/setpoint. There is no retained message stored for the topic anymore, so it receives nothing on subscribe; it will only get a value when, and if, something publishes one. From this client’s point of view, the topic is silent until further notice.
The lifecycle shows the retained value behaving exactly like a piece of broker-managed state for the topic: writeable by anyone allowed to publish retained, readable on demand by anyone allowed to subscribe, replaced atomically by each new retained publish, and explicitly clearable by an empty retained payload.
Common gotchas with retained messages
A few practical pitfalls recur. None are exotic; each follows directly from how retained messages work, but each can surprise developers who have not run into it before.
The empty-payload delivery surprise. Because clearing a retained message is itself a retained publish (with an empty payload), every currently-subscribed client receives that empty publish as a real delivery. Application code subscribing to topics that may be cleared must handle a zero-length payload sensibly rather than crashing or interpreting it as a real value. A common pattern is to treat an empty payload as “no value” or “device removed,” depending on the topic’s semantics.
Retained does not mean fresh. The broker stores whatever was last published as retained; it does not check whether that value is still meaningful. A device that crashed three hours ago with a retained Online status still has that status on the broker until something updates it. This is exactly why the LWT-plus-retained pattern exists: it ensures the offline transition is published even when the device cannot publish anything itself. If your design relies on retained values reflecting current reality, build in the mechanism that updates them on every state change, including the failure cases.
Retained behavior on republish from the broker. When the broker delivers a retained message to a new subscriber, it sets the retain flag on the delivered packet so the receiver can distinguish it from a live publish. By default, when a broker forwards a live retained publish to existing subscribers, the RETAIN flag on the forwarded packet is cleared, even if the same message is also being stored as the retained value. MQTT 5 adds the Retain As Published subscription option, which allows subscribers to receive the RETAIN flag exactly as the publisher originally sent it. The practical upshot, with the defaults, is that “this delivery has retain = true” reliably means “this is a retained-on-subscribe delivery,” and the receiver can use that flag to tell the two cases apart.
ACLs and retained messages. Access-control rules are checked when a retained message is published just like any other publish: a client without permission to publish to a topic cannot store a retained value there, regardless of retain = true. Permission rules also apply on subscribe; if a subscriber is not authorized for a topic, the broker will not deliver its retained message to them. None of this is special to retained messages, but it is worth remembering, because a permissions misconfiguration can silently prevent the retained-message mechanism from working as intended.
One retained message per topic, not per publisher. Multiple publishers writing retained values to the same topic compete: each retained publish replaces whatever was there before, regardless of who published it. There is no per-publisher retained slot. If two publishers update the same retained topic, the most recent retained publish wins, and earlier values are lost. Design topic structure so that each retained topic has one logical owner.
Stale retained values from defunct devices. A retained value persists on the broker until something overwrites or clears it. If a device that owns a retained topic is removed permanently and nothing else publishes there, the retained value stays forever, and every new subscriber gets it as the “current state” of something that no longer exists. Operational cleanup or MQTT 5’s message expiry (covered in the session and message expiry article) is how you avoid retained-message clutter accumulating over time.
The retained-plus-LWT status pattern
In real-world deployments, retained messages are very commonly combined with Last Will and Testament to track whether a device is currently online or offline. The pattern is one of the most useful idioms in MQTT, and it works as follows.
When a client connects, it includes a will message in its CONNECT. The will message has the same topic as the client’s status topic, say client1/status, with payload Offline and the will retain flag set to true. Right after connecting, the client itself publishes a message with payload Online and the retain flag set to true to the same topic. Now the topic has a retained Online value, and anyone subscribing learns the client is currently online.
While the client stays connected, every new subscriber to client1/status receives the retained Online message. If the client disconnects ungracefully (its connection drops, its battery dies, it crashes), the broker publishes its will message to client1/status with payload Offline and retain = true. That replaces the previous retained value, and from that moment forward any subscriber to the topic learns the client is currently offline.
The result is a topic that always reflects the device’s current state without any of the subscribers needing to track which connect or disconnect event happened when. They simply subscribe and read. The pattern relies on three MQTT features working together: the retain flag making the value available immediately on subscribe, LWT making sure the offline transition is published even when the device cannot publish anything itself, and the broker’s per-topic retained storage carrying state across all comings and goings of subscribers. The Last Will and Testament article covers the LWT side of this pattern in detail.
Retained messages are not the same as persistent sessions
A point worth being explicit about, because the two features are sometimes conflated: retained messages and persistent sessions are different storage mechanisms with different scopes and lifetimes.
A retained message is stored against a topic. It survives independently of any client; clients can come and go, sessions can be cleaned, the original publisher can disconnect forever, and the retained message remains on the topic until something explicitly publishes a new retained value or an empty retained payload to clear it. Any client subscribing to a matching filter, today or in a year, receives it.
A persistent session is stored against a ClientId, on the broker side. It holds that specific client’s subscriptions and any QoS 1 and 2 messages that were undelivered when the client was offline. It belongs to one client, and it can be discarded by the broker, by an expiry, or by that same client connecting with a clean session.
These two are often used together (a device with a persistent session and a retained status topic, say), but they are not the same storage, and clearing one does not clear the other. Confusing them leads to surprises: a developer who assumes clearing a session also clears retained messages, or vice versa, will find their assumption violated.
How retained messages connect to the rest of MQTT
Retained messages sit at the intersection of several MQTT features:
- The retain flag is part of the PUBLISH packet, set by the publisher and respected on delivery as covered in the publish/subscribe operations article.
- Topics and wildcards govern which retained messages are delivered to a new subscription, exactly as for live messages.
- Quality of Service still applies on delivery: subscribers receive a retained message at the minimum of the stored publish QoS and their subscription QoS.
- Last Will and Testament combines with retained messages to produce the standard device-status pattern.
- MQTT 5 message expiry lets a publisher set an expiry on a retained message, after which the broker discards it even if it has not been overwritten or cleared; this is covered in the session and message expiry article.
Used deliberately for state rather than events, retained messages are one of the cheapest ways to make an MQTT-based system feel responsive to newly connected clients.
Frequently asked questions
What is a retained message in MQTT?
A retained message is an ordinary PUBLISH that the broker stores against its topic, in addition to delivering it to current subscribers. Any client that later subscribes to a matching topic filter immediately receives the stored retained message as part of subscribing.
How do I create a retained message?
Set the retain flag on the PUBLISH to true. Most client libraries expose this as a boolean parameter on their publish API. The broker handles storage and future delivery automatically.
How do I delete a retained message?
Publish a zero-length (empty) payload to the topic with the retain flag set to true. The broker recognizes this as a request to clear the retained value for that topic and removes it. New subscribers thereafter receive no retained message for that topic.
Do retained messages survive a client disconnect?
Yes. Retained messages are stored against the topic on the broker, not against any client’s session. The publisher can disconnect, change ClientId, or never reconnect, and the retained message remains until explicitly replaced or cleared.
Do wildcard subscriptions receive retained messages?
Yes. The broker checks every stored retained message against a new subscription’s topic filter using the same wildcard matching used for live messages, and delivers every retained message whose topic matches.
What QoS is a retained message delivered at?
At the minimum of the QoS the publisher originally used and the QoS of the subscriber’s subscription, exactly as for a live message. The retain flag changes only whether and when the message is delivered, not how QoS is negotiated.
Are retained messages the same as a persistent session?
No. Retained messages are stored per topic and apply to anyone who subscribes; persistent sessions are stored per ClientId and apply to one specific client’s subscriptions and queued messages. They are independent storage with independent lifetimes.
