MQTT Fundamentals

MQTT Session Expiry Interval and Clean Start in MQTT 5 Explained

MQTT 5 split the old clean session flag into Clean Start and Session Expiry Interval. Here is how the two combine, which values to choose and how to verify them on a live broker.

MQTT session expiry interval illustration with a countdown timer on a broker session after an IoT client disconnects
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

The MQTT session expiry interval tells the broker how long to keep a client's session after it disconnects, and together with the Clean Start flag it replaces the all-or-nothing clean session flag from MQTT 3.1.1. In this article you will learn how the two settings combine, how the interval can be changed at disconnect time, and how to choose sensible values for sensors, gateways and backend services.

Why MQTT 5 Replaced the Clean Session Flag

In MQTT 3.1.1, cleanSession did two jobs with one bit. Setting it to false meant "resume my old session and keep this one forever". There was no way to say "resume my session, but forget it if I am gone for more than an hour". Brokers worked around this with server-side expiry settings, which meant behaviour differed between brokers and abandoned sessions piled up.

MQTT 5 separates the two concerns:

  • Clean Start (a CONNECT flag) answers: should the broker discard any existing session right now?
  • Session Expiry Interval (a CONNECT property, in seconds) answers: how long should the session live after the network connection closes?

The design is described in the OASIS MQTT 5.0 specification, and it is one of the most practical improvements covered in our overview of MQTT 5 features.

How Clean Start and Session Expiry Combine

Clean Start Session Expiry Interval Behaviour 3.1.1 equivalent
true 0 (or absent) Fresh session, deleted on disconnect cleanSession = true
false 0 (or absent) Resume existing session if any, deleted on disconnect No direct equivalent
true > 0 Discard old session, start new one that survives disconnect for N seconds No direct equivalent
false > 0 Resume if present, survive disconnect for N seconds cleanSession = false (with a timeout)
false 0xFFFFFFFF Resume if present, never expire cleanSession = false

A few details matter in practice:

  • The timer starts when the network connection closes, not when the client last published.
  • If the client reconnects before the timer runs out, the session is resumed and the timer is cancelled.
  • When the interval elapses, the broker deletes the subscriptions and any queued messages.
  • If the session expires, the will message is sent no later than that moment, even if the will delay interval is longer.

Changing the Interval at Disconnect

MQTT 5 lets a client update the expiry in its DISCONNECT packet. A device going into planned maintenance can say "keep my session for a day" as it leaves, while normally running with a shorter value. There is one restriction: if the client connected with an expiry of 0, it may not set a non-zero value at disconnect; the broker treats that as a protocol error.

The Broker Can Override You

A broker may refuse to keep sessions as long as you ask. In that case it returns a Session Expiry Interval property in CONNACK, and that value is the one in effect. Always log CONNACK properties during development so you know the real number. On the free test broker at broker.connectmq.com, session expiry is capped at 30 minutes.

Choosing an MQTT Session Expiry Interval

Think about two questions: how long can this client realistically be offline, and how stale can a queued message be before it is useless?

Client type Suggested expiry Reasoning
Live dashboard, browser tab 0 Only current data matters; retained messages fill the screen on connect
Mains-powered sensor that only publishes 0 to 300 s Little session state; short expiry tidies up quickly
Device that receives commands 1 to 24 h Commands should survive Wi-Fi drops and short outages
Sleepy battery device waking hourly Slightly longer than the wake interval Session must outlive the sleep period
Backend worker processing alarms Hours, matched to your recovery time Missed alarms must be processed after redeploys
Decommissioned or test client 0 Avoid leaving orphan sessions behind

Pair long expiry intervals with the MQTT 5 Message Expiry Interval property on time-sensitive publications. A "start pump" command that sits in a queue for six hours can be more dangerous than one that is never delivered.

A Worked Example: Sleepy Sensor With Commands

Imagine a battery sensor that wakes every 15 minutes, publishes a reading, checks for configuration commands and sleeps again. If its session expiry were 0, any command sent while it slept would be dropped, because there would be no session to queue it in. If the expiry were infinite, a sensor that died in the field would leave a session that keeps collecting commands nobody will read. A value of around 20 to 30 minutes covers one missed wake-up plus some network jitter, and the session disappears on its own once the device is truly gone. Send commands with QoS 1 and a message expiry of a few hours, and the device receives everything that is still relevant when it next connects.

Setting Session Expiry in Code

All examples use a unique demo prefix on the free public MQTT broker. Replace s4e8 with your own random string; the public area is visible to everyone.

mosquitto_sub

# Resume (or create) a session that survives 10 minutes after disconnect
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
  -i expiry-demo-s4e8 -c -x 600 -q 1 -v -d \
  -t 'connectmq/demo/s4e8/commands/#'

-c sets Clean Start to false, and -x 600 sets the Session Expiry Interval. The -d flag prints the CONNACK, so you can see whether the broker granted the value.

Python (paho-mqtt 2.x)

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

def on_connect(client, userdata, flags, reason_code, properties):
    granted = getattr(properties, "SessionExpiryInterval", None)
    print("Connected:", reason_code,
          "| session present:", flags.session_present,
          "| broker override:", granted)
    if not flags.session_present:
        client.subscribe("connectmq/demo/s4e8/commands/#", qos=1)

def on_message(client, userdata, msg):
    print("Command:", msg.topic, msg.payload.decode())

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id="expiry-py-s4e8",
                     protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message

connect_props = Properties(PacketTypes.CONNECT)
connect_props.SessionExpiryInterval = 600

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

try:
    input("Press Enter to disconnect with a 30 minute expiry...\n")
finally:
    bye = Properties(PacketTypes.DISCONNECT)
    bye.SessionExpiryInterval = 1800
    client.disconnect(properties=bye)
    client.loop_stop()

Node.js (MQTT.js v5)

const mqtt = require("mqtt");

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  clientId: "expiry-node-s4e8",
  protocolVersion: 5,
  clean: false, // Clean Start = false
  properties: { sessionExpiryInterval: 600 },
});

client.on("connect", (connack) => {
  console.log("session present:", connack.sessionPresent,
              "props:", connack.properties);
  if (!connack.sessionPresent) {
    client.subscribe("connectmq/demo/s4e8/commands/#", { qos: 1 });
  }
});

client.on("message", (topic, payload) => {
  console.log(topic, payload.toString());
});

Common Mistakes and How to Spot Them

  1. Clean Start false, expiry 0. Developers port 3.1.1 code, set clean: false and forget the expiry property. In MQTT 5 that session is deleted the moment the connection drops, so nothing is ever queued. If queued messages never appear, check this first.
  2. Infinite expiry on test clients. Every throwaway script with a unique ID and 0xFFFFFFFF leaves a session behind. Use 0 for experiments unless you are testing sessions.
  3. Ignoring Session Present. If the broker restarted or the session expired, Session Present is 0 and your subscriptions are gone. Always re-subscribe in that case.
  4. Assuming durability across broker restarts. Session expiry describes how long a session may live, not a storage guarantee. ConnectMQ keeps session state in memory: it survives client disconnects for the granted interval but is cleared if the broker itself restarts.
  5. Changing client IDs. Expiry is tied to the client ID. A new ID is a new session; see MQTT client ID best practices.

How This Fits the Bigger Picture

Session expiry is the MQTT 5 way to run persistent sessions safely: you get offline queuing where you need it and automatic clean-up where you do not. If you are still learning the protocol's moving parts, the complete MQTT tutorial is a good companion read.

Use MQTT 5 Sessions on ConnectMQ

ConnectMQ is a managed MQTT broker with full MQTT 5.0 support, including session expiry, message expiry, topic aliases and reason codes. You can test Clean Start and expiry combinations with the portal's in-browser MQTT client or your own code, then move the same settings to production. Start on the free Serverless plan, no credit card required, and connect with your generated YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID.

Frequently Asked Questions

What is the default MQTT session expiry interval?

If the property is absent, the session expiry interval is 0, which means the session ends when the network connection closes. You must set it explicitly to get a persistent session in MQTT 5.

What is the difference between Clean Start and clean session?

Clean session in MQTT 3.1.1 controlled both discarding the old session and keeping the new one. Clean Start in MQTT 5 only controls discarding at connect time, while the Session Expiry Interval controls how long the session lives afterwards.

What is the maximum session expiry interval?

The protocol maximum is 4,294,967,295 seconds (0xFFFFFFFF), which means the session never expires. Brokers can impose lower limits and report them in CONNACK; the ConnectMQ public test broker allows up to 30 minutes.

Can I change the session expiry when disconnecting?

Yes, by sending a Session Expiry Interval property in DISCONNECT, unless you originally connected with an expiry of 0.

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.