MQTT 5 Session & Message Expiry Intervals Explained

By | June 15, 2026

Two of the most practical additions in MQTT 5 are about time: how long the broker should hold on to a client’s session, and how long it should hold on to an individual message. In MQTT 3.1.1, neither was a protocol concern. A persistent session lived indefinitely on the broker until something explicitly wiped it, and a queued QoS 1 or 2 message waited forever for the offline subscriber it was meant for. That model worked in small deployments and fell over in large ones, where dead devices and stale messages accumulated faster than they could be cleaned up manually.

MQTT 5 fixes this with two related but independent mechanisms: the Session Expiry Interval, set on a client’s session at connect time, and the Message Expiry Interval, set on each PUBLISH individually. Together they give the protocol explicit, negotiated control over how long state lives. This article covers both in detail: what each expires, the values they can take, how they interact with persistent sessions and retained messages, the gotchas around broker-side maximums, and how to think about choosing values. It is a technical reference; persistent sessions, retained messages, and the broader MQTT 5 overview each have their own dedicated articles in this category.

Expiry intervals at a glance

MechanismScopeSet inField typeDefault if absentMaximum
Session Expiry IntervalA client’s stored sessionCONNECT (and DISCONNECT)seconds (4-byte)0 (no persistence)4,294,967,295 (≈136 years)
Message Expiry IntervalOne PUBLISHPUBLISHseconds (4-byte)absent = store indefinitely4,294,967,295 (≈136 years)

Both are MQTT 5 properties; neither exists in MQTT 3.1.1.

Why expiry exists

To see why these mechanisms were added, it helps to look at the failure modes of MQTT 3.1.1.

Sessions that never die. In MQTT 3.1.1, a persistent session is created when a client connects with cleanSession = false, and it lives on the broker until the same ClientId next connects with cleanSession = true and explicitly wipes it. A device that connects once, registers some subscriptions, and is then destroyed leaves its session on the broker forever. Multiply this by many short-lived clients, by test runs that never came back, by ClientId churn from misconfigured deployments, and the broker’s persistent-session storage accumulates indefinitely. Operators had to bolt on server-side cleanup tools to bound this, and there was no protocol-level way for a client to say “I’d like a session that lasts a day, no more.”

Queued messages that pile up forever. Once a client has a persistent session and matching subscriptions, QoS 1 and 2 messages published while it is offline are queued for it on the broker. Without expiry, every such message stays queued until the client comes back, or until the broker hits an operational limit. If the client never reconnects, the messages sit there indefinitely. If it reconnects after a long absence, it receives a backlog of messages, many of which may no longer be useful, before it can resume normal operation. There was no way to mark a message “deliver within an hour or drop it.”

Both of these are common in IoT deployments. Field devices can be offline for weeks at a time and still legitimately reconnect; field devices can also be destroyed without anyone updating the broker. Without bounded lifetimes, the broker has to choose between holding everything (running out of storage) and aggressive heuristics (potentially discarding things that mattered).

MQTT 5’s two expiry intervals address each problem directly and independently. They also do not replace persistent sessions or retained messages; they add a time dimension to behaviors that already existed.

Session Expiry Interval

The Session Expiry Interval is a property a client sets in its CONNECT to tell the broker how long, in seconds, to keep its session after the network connection closes. It pairs with the Clean Start flag (which replaced MQTT 3.1.1’s cleanSession) to give MQTT 5 a clean session model.

The two together define the full session story:

  • Clean Start = true, Session Expiry Interval = 0 — equivalent to MQTT 3.1.1’s cleanSession = true. The broker discards any existing session for this ClientId at connect, creates no new persistent state, and removes the session as soon as the connection closes.
  • Clean Start = false, Session Expiry Interval > 0 — equivalent to MQTT 3.1.1’s cleanSession = false, but with an explicit lifetime. The broker preserves any existing session (or creates one), and keeps it for the configured number of seconds after the connection closes. If the client returns within that window, it resumes; if not, the broker discards the session automatically.
  • Clean Start = true, Session Expiry Interval > 0 — start fresh on this connect, but keep the new session alive for the configured interval after disconnect. Useful when you want to throw away whatever state was there before but still benefit from session persistence going forward.
  • Clean Start = false, Session Expiry Interval = 0 — preserve any existing session for the duration of this connection, but discard it as soon as the connection closes. Effectively makes only the live connection persistent.

The interval is a 4-byte unsigned integer. A value of 0 means the session ends as soon as the connection closes. The maximum, 4,294,967,295 seconds (just over 136 years), is effectively “never expire,” which corresponds to the old MQTT 3.1.1 default behavior. Anything between is an explicit lifetime.

Changing the interval at disconnect

A useful subtlety: the Session Expiry Interval can be changed at disconnect. When a client sends a DISCONNECT in MQTT 5, it can include a Session Expiry Interval property of its own, and the broker uses that value instead of the one negotiated in CONNECT. This lets a client commit to a different lifetime depending on how it is leaving. A client that wants its session to be cleaned up immediately on a deliberate exit can send DISCONNECT with Session Expiry Interval = 0; the same client, after an unplanned drop, has the longer interval it set in CONNECT applied automatically.

One restriction: a client that connected with Session Expiry Interval = 0 cannot extend it via DISCONNECT, because at that point there is no session to retain. The session expiry can be shortened or kept the same on DISCONNECT, but not promoted from zero to nonzero.

Server-side maximum

A broker may impose its own maximum on the Session Expiry Interval, typically configured by the operator. If the client requests a value larger than the broker’s maximum, the broker may grant the maximum instead and advertise the actual value to the client. In MQTT 5 this advertisement is part of the CONNACK’s capability properties; the client should read it and act on whatever the broker actually granted, rather than assuming its requested value was used.

This server-side bound is valuable in multi-tenant or open deployments where the broker operator does not control every client’s configuration. Without it, a single client requesting a 100-year session expiry could pin broker resources for as long as it likes; with it, operators can cap stale-session lifetime regardless of what clients ask for.

What the session contains

The Session Expiry Interval bounds the lifetime of the same session state described in the persistent sessions article: the client’s subscriptions, in-flight QoS 1 and 2 message state, QoS 1 and 2 messages queued for the offline client. When the session expires, all of that state is discarded, regardless of the lifetime of any individual queued message. So a queued message with its own long Message Expiry Interval will still be deleted when its session expires; the session lifetime is the outer bound.

Message Expiry Interval

The Message Expiry Interval is a property a client sets on each PUBLISH to tell the broker how long, in seconds, the message remains valid. If a matching subscriber is offline (in a persistent session) and the configured interval elapses before delivery, the broker discards the message instead of delivering it on reconnect.

The interval is set per message, not per topic or per client. The publisher decides on a per-message basis whether and how long this particular message should be kept. If no Message Expiry Interval is included, the message has no expiry; the broker keeps it for the offline subscriber indefinitely, or until the subscriber’s session expires, whichever comes first.

How it applies

The expiry is measured from when the broker receives the PUBLISH. When the broker later delivers the message (on the subscriber’s reconnect, for example), it reduces the Message Expiry Interval by the time the message spent waiting in the broker, and the subscriber receives the message with the remaining time as the property value. So a subscriber that sees a Message Expiry Interval of 30 on a delivered PUBLISH knows the message still has 30 seconds of life left from its perspective — useful for application logic that wants to make timeliness decisions.

If the interval reaches zero while the message is still queued, the broker discards it. The subscriber learns nothing about that particular message; from its perspective, it simply never arrived. There is no expiry notification to the subscriber, and no expiry confirmation to the publisher. The message is silently dropped.

Retained messages and expiry

The Message Expiry Interval also applies to retained messages. When a retained message is stored on a topic with a Message Expiry Interval set, the broker counts down from that value. If the interval elapses before the retained message is replaced or cleared, the broker discards the retained message on its own. From that moment, new subscribers to a matching topic filter no longer receive the retained value.

This is one of the simpler answers to the “stale retained messages from defunct devices” problem covered in the retained messages article. A publisher that knows its values are only useful for a limited time can include a Message Expiry Interval on its retained publishes, and the broker will clean up on its own when the value has aged out.

Examples of where it matters

The Message Expiry Interval is most useful when message timeliness is part of the message’s meaning. Two patterns recur:

Commands with a deadline. A “start the pump” command sent to a controller that is currently offline may only be safe to act on for the next few minutes. After that, the operational state may have changed and the command no longer makes sense. Setting a Message Expiry Interval of, say, 60 seconds on this command ensures it is only delivered if the controller comes back within a minute; if not, the broker drops it, and the command does not get acted on hours later under unrelated conditions.

Status updates with a freshness window. A traffic-status update for a navigation system is only useful for a short time. Setting a short expiry on the publish ensures that a navigation client which has been offline for an hour does not receive a flood of stale traffic data on reconnect, only the most recent message that is still within its expiry window.

Firmware updates without a deadline. Conversely, a firmware update message sent to a device that may be offline for weeks should have no Message Expiry Interval at all (or a very long one). The publisher does not want the broker to drop the update just because the device is taking a while to come back. The decision is per-message, and it sits in the publisher’s hands.

Two expiries, one rule

The two intervals interact in one specific way: when a session expires, every message queued in it expires with it, regardless of each message’s own Message Expiry Interval. The session lifetime is the outer bound.

This means a publisher cannot use a long Message Expiry Interval to keep a message alive longer than the subscriber’s session. If a subscriber has a Session Expiry Interval of one hour and is offline for two hours, every message queued for it during that absence is gone when the session is discarded, even if individual messages were marked with Message Expiry Intervals of a week. To keep messages alive longer, the subscriber’s session has to be long enough to hold them.

The two intervals are otherwise independent. A short Message Expiry Interval can be used on a publish to a client with a long Session Expiry Interval, in which case the message is dropped after its expiry but the session continues. A long Message Expiry Interval can be used on a publish to a client with a short Session Expiry Interval, in which case the session limit is what actually applies.

A worked example: both expiries in action

To make the interaction concrete, trace a single message through a queue with both intervals active.

A subscriber connects with Clean Start = false and Session Expiry Interval = 3600 (one hour) and subscribes to commands/device-7/+ at QoS 1. It then disconnects. The broker preserves its session for an hour.

At T+0s after the disconnect. A publisher sends a message to commands/device-7/reboot at QoS 1, with Message Expiry Interval = 600 (ten minutes). The broker matches it against the subscriber’s stored subscription, sees the subscriber is offline, and queues the message in its session. The broker records the message’s remaining lifetime: 600 seconds.

At T+300s. The publisher sends another message to the same topic with Message Expiry Interval = 60 (one minute). The broker queues this one too, alongside the first.

At T+400s. The second message’s countdown reaches zero (it was published at T+300s with a 60-second expiry, so it expires at T+360s — by T+400s the broker has discarded it). The first message is still alive; it has roughly 200 seconds left on its 600-second expiry.

At T+550s. The first message has now also expired. The broker discards it. The subscriber’s queue is empty.

At T+580s. A third message is published with no Message Expiry Interval set. The broker queues it indefinitely as far as message-level expiry is concerned.

At T+3500s. The subscriber reconnects with Clean Start = false and the same ClientId. The session is still alive (its one-hour expiry has not yet elapsed). The CONNACK reports Session Present = true. The broker delivers the third message; the first two are gone. The third message arrives with a Message Expiry Interval property whose value reflects what was left of its lifetime, although since it was published without an expiry, the property is absent in this case.

If the subscriber had instead come back at T+3700s, after the session’s one-hour expiry, the entire session would be gone: subscriptions, queued messages, all of it. The third message would have been discarded along with the session, regardless of the fact that it had no message-level expiry of its own. The session’s lifetime is the outer bound, and that is the rule that overrides everything else when the two intervals disagree.

This walkthrough shows the two expiry intervals doing distinct jobs: per-message expiry discards individual messages whose value has aged out, while session expiry discards the session as a whole (and everything in it) when the subscriber stays away too long.

Mapping MQTT 3.1.1 behavior to MQTT 5 expiry

A useful exercise when migrating from MQTT 3.1.1 is to express the old behavior in MQTT 5 terms:

  • cleanSession = true (MQTT 3) → Clean Start = true, Session Expiry Interval = 0 (or absent) in MQTT 5.
  • cleanSession = false (MQTT 3) → Clean Start = false, Session Expiry Interval = 4,294,967,295 in MQTT 5, to match the “session lives forever” MQTT 3 behavior. In practice, the better default in MQTT 5 is to pick a sensible finite value that matches your operational reality (a day, a week, a year), but the unbounded value is available when you really mean it.
  • Per-message TTL: there is no MQTT 3.1.1 equivalent for the Message Expiry Interval. It is genuinely new.

Migrating a deployment is largely a matter of going through each kind of client and deciding what session lifetime is actually appropriate, rather than blindly translating “false” into “forever.” Many MQTT 3.1.1 deployments had unbounded sessions only because there was no alternative.

Operational considerations

A few practical things to know when working with these intervals.

Server-side maximums override client requests. A broker may cap both the Session Expiry Interval and (in some configurations) what individual clients can do. If you request a Session Expiry Interval of 100 years and the broker is configured for a one-month maximum, the actual lifetime is one month. Always check the CONNACK in MQTT 5 to see what the broker actually granted.

Expiry is silent. Neither session expiry nor message expiry generates a notification. A session that expires while its client is offline simply disappears; when the client returns, it gets a fresh session (and reads the CONNACK’s session present flag to learn that nothing was preserved). A message that expires in the queue is simply not delivered; neither the publisher nor the subscriber is told. Application logic that needs to know about expirations has to be designed around this, for example by attaching publisher-side identifiers and checking which messages eventually arrived.

Expiry interacts with persistence durability. Whether a session and its queued messages survive a broker restart is a broker-implementation question, covered in the persistent sessions article. Expiry intervals control how long the broker is willing to keep state; whether it actually keeps that state across its own restarts depends on broker configuration. The two are orthogonal.

Pick session lifetimes that fit your devices. A common mistake is using either the MQTT 3.1.1 default (effectively unbounded) or a very short value because nothing else seemed obvious. The right value usually matches the device’s expected offline pattern: a connected car might have a session expiry of a week to handle long parking; a backend service might have a session expiry of zero because if it disconnects, something is genuinely wrong and a fresh start is appropriate. Match the value to the expected pattern.

Pick message expiries to reflect message meaning. Some messages are valuable for hours, some for minutes, some not at all if they arrive late. The Message Expiry Interval is a way to encode that meaning at the protocol level rather than ad-hoc in the application. For messages whose meaning genuinely does not decay (firmware updates, configuration changes), no expiry is the right choice.

How session and message expiry connect to the rest of MQTT

The two expiry mechanisms touch several other features:

  • Persistent sessions are the storage that the Session Expiry Interval bounds. See the persistent sessions article for what the broker actually stores in a session.
  • Retained messages can carry a Message Expiry Interval, providing automatic cleanup of stale retained values. See the retained messages article.
  • Quality of Service governs which messages get queued for offline clients (QoS 1 and 2 with a persistent session) and which do not; the Message Expiry Interval applies to the queued-message storage that QoS 1 and 2 create.
  • The CONNACK capability properties advertise the broker’s actual maximum Session Expiry Interval, which a client should read in case it is shorter than requested. See the improved client feedback article.
  • Will Delay Interval is a related but separate MQTT 5 mechanism, applied to LWT and covered in the Last Will and Testament article.

Used deliberately, these intervals turn what was effectively unbounded broker state in MQTT 3.1.1 into a manageable resource with explicit, negotiated lifetimes.

Frequently asked questions

What is the Session Expiry Interval in MQTT 5?

A property a client sets in CONNECT (and optionally in DISCONNECT) telling the broker how long, in seconds, to keep the session after the network connection closes. A value of 0 means the session ends with the connection; the maximum is 4,294,967,295 seconds (about 136 years), effectively unbounded.

What is the Message Expiry Interval in MQTT 5?

A property a publisher sets on a PUBLISH telling the broker how long, in seconds, the message remains valid. If a matching subscriber is offline and the interval elapses before delivery, the broker drops the message. The same property also applies to retained messages.

How does Session Expiry interact with Clean Start?

Clean Start says whether to discard any existing session on this connect; Session Expiry Interval says how long to keep the session after disconnect. Together they replace MQTT 3.1.1’s single cleanSession flag with finer-grained control.

Can a client extend its session expiry by sending DISCONNECT?

A client can change the Session Expiry Interval in its DISCONNECT, but it can shorten or keep the existing value, not promote it from zero to nonzero. A client that connected with Session Expiry Interval = 0 cannot retain its session after disconnect by setting a different value at exit.

What happens to queued messages when a session expires?

They are discarded along with the session, regardless of each message’s own Message Expiry Interval. The session lifetime is the outer bound; a message cannot outlive the session it is queued in.

Does Message Expiry apply to retained messages?

Yes. A retained message with a Message Expiry Interval is automatically discarded by the broker when the interval elapses, even if nothing else has replaced or cleared it. This is a clean way to avoid accumulating stale retained values from devices that stop publishing.

Can the broker override the values I request?

Yes. A broker may impose its own maximum on the Session Expiry Interval (and may also enforce limits on Message Expiry Interval in some configurations). When the broker grants a different value than the client requested, the actual value is advertised in the CONNACK. Clients should read it rather than assuming the requested value was honored.

Is there an MQTT 3.1.1 equivalent?

Not really. MQTT 3.1.1 has no protocol-level expiry for either sessions or messages. Persistent sessions live indefinitely until explicitly wiped; queued messages wait until delivery or operational limits force them out. Both expiry intervals are genuine MQTT 5 additions.

Author: Zakaria El Intissar

I'm an automation and industrial computing engineer with 12 years of experience in power system automation, SCADA communication protocols, and electrical protection. I build tools and write guides for Modbus, DNP3, IEC 101/103/104, and IEC 61850 on ScadaProtocols.com to help engineers decode, analyze, and troubleshoot real industrial communication systems.