The MQTT will delay interval is an MQTT 5 property that tells the broker to wait a number of seconds before publishing a client's Last Will message, and to cancel it entirely if the client reconnects in time. In this article you will see how it suppresses noisy offline alerts caused by brief network blips, how it interacts with session expiry, and how to configure it in mosquitto, Python and Node.js.
The Problem: Flapping Offline Alerts
The Last Will and Testament is MQTT's built-in way to announce that a client vanished. The client registers a will message at connect time, typically a retained offline on a status topic, and the broker publishes it if the connection ends without a clean DISCONNECT.
In MQTT 3.1.1 the will is published as soon as the broker detects the failure. On real networks, that is noisy:
- A Wi-Fi access point roams a device to another AP and the TCP connection resets.
- A cellular modem renegotiates and the device is back within seconds.
- A gateway restarts its MQTT process after a configuration change.
Each case produces an offline message followed moments later by online. Dashboards flicker, alert rules fire, and operators learn to ignore the alerts, which is worse than having none.
How the MQTT Will Delay Interval Works
MQTT 5 adds the Will Delay Interval, a four-byte property in seconds attached to the will message in CONNECT. When the network connection closes abnormally:
- The broker starts a timer for the will delay interval.
- If the same client reconnects to the same session before the timer expires, the will is not published.
- If the timer expires first, the will is published as usual.
- If the session ends first (because the session expiry interval elapses), the will is published at that moment.
The default is 0, which reproduces the 3.1.1 behaviour of immediate publication.
The Rule Everyone Misses: Session Expiry
The will is published when the delay passes or the session ends, whichever comes first. If the client connects with a session expiry interval of 0, the session ends the instant the connection closes, so the will goes out immediately and your delay has no effect.
| Will delay | Session expiry | What you get |
|---|---|---|
| 0 | any | Immediate will on abnormal disconnect |
| 30 s | 0 | Immediate will (session ended first) |
| 30 s | 60 s | Will after 30 s unless the client reconnects |
| 30 s | 10 s | Will after 10 s (session ended first) |
| 300 s | 3600 s | Will after 5 minutes unless the client reconnects |
So to use will delay, always set a session expiry interval at least as long as the delay, and reconnect with Clean Start set to false so the broker treats the new connection as the same session. Details are in MQTT 5 clean start and session expiry interval.
Keep Alive Adds to the Total
The timer only starts once the broker notices the connection is gone. If a device loses power, detection takes up to 1.5 times the keep alive. With a 60 second keep alive and a 30 second will delay, worst-case time to an offline message is about two minutes. Budget both together; see choosing an MQTT keep alive interval.
Choosing a Will Delay Value
Pick the shortest delay that covers your normal reconnect time.
- Wi-Fi devices with fast reconnect logic: 10 to 30 seconds.
- Cellular devices: 30 to 120 seconds; modem re-registration is slower.
- Gateways that restart services during updates: slightly longer than a typical restart.
- Safety-relevant equipment: keep it short, or 0. A delayed alarm can cost more than a false one.
A practical test: log the time between disconnect and reconnect for a week of real operation and choose a value just above the bulk of those gaps.
Configuring Will Delay in Code
All examples use the public MQTT test broker at broker.connectmq.com. Replace w3d8 with your own random string, and remember the public area is readable by anyone.
mosquitto_sub as a Simulated Device
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i willdelay-dev-w3d8 -c -x 120 \
--will-topic 'connectmq/demo/w3d8/status' \
--will-payload 'offline' --will-qos 1 --will-retain \
-D will will-delay-interval 30 \
-t 'connectmq/demo/w3d8/cmd'
In another terminal, watch the status topic:
mosquitto_sub -h broker.connectmq.com -p 1883 -i willdelay-watch-w3d8 \
-v -t 'connectmq/demo/w3d8/status'
Ctrl+C on mosquitto_sub sends a clean DISCONNECT, which never triggers a will. To simulate a real failure, kill the process instead (for example kill -9 on its process ID, or unplug the network). Then try two cases: restart the device command within 30 seconds and no offline appears; wait longer and it does.
Python (paho-mqtt 2.x)
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
STATUS = "connectmq/demo/w3d8/status"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="willdelay-py-w3d8",
protocol=mqtt.MQTTv5)
will_props = Properties(PacketTypes.WILLMESSAGE)
will_props.WillDelayInterval = 30 # seconds
client.will_set(STATUS, payload="offline", qos=1, retain=True,
properties=will_props)
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
# Announce we are online (overwrites a retained "offline")
client.publish(STATUS, "online", qos=1, retain=True)
client.on_connect = on_connect
connect_props = Properties(PacketTypes.CONNECT)
connect_props.SessionExpiryInterval = 120 # must be >= will delay
client.connect("broker.connectmq.com", 1883, keepalive=30,
clean_start=False, properties=connect_props)
client.loop_forever()
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const STATUS = "connectmq/demo/w3d8/status";
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "willdelay-node-w3d8",
protocolVersion: 5,
clean: false,
keepalive: 30,
properties: { sessionExpiryInterval: 120 },
will: {
topic: STATUS,
payload: Buffer.from("offline"),
qos: 1,
retain: true,
properties: { willDelayInterval: 30 },
},
});
client.on("connect", () => {
client.publish(STATUS, "online", { qos: 1, retain: true });
});
A Complete Online/Offline Pattern
Will delay works best as part of a consistent presence design:
- Register a retained
offlinewill with a will delay and matching session expiry. - Publish a retained
onlineimmediately after every successful connect. - Before a planned shutdown, publish a retained
offlineyourself, then disconnect cleanly. - Let dashboards and alert rules read only the retained status topic.
- Optionally add a timestamp or reason in a JSON payload so operators can see why a device went offline.
More presence patterns are covered in MQTT device online status. On a ConnectMQ broker you can also cross-check with the $SYS/broker/connection/{clientId}/state topic, which reflects the broker's view of the raw connection rather than your application-level status.
Will Delay Versus Debouncing in the Application
Before MQTT 5, teams solved flapping by debouncing in the consumer: a dashboard or alerting service waited N seconds after an offline message and only raised an alarm if no online followed. That still works, but every consumer has to implement the same logic, and each one may pick a different window. Will delay moves the decision into the broker, so every subscriber sees the same, already-filtered status stream. The trade-off is that the delay is set per client at connect time, so changing it means updating device configuration rather than one backend service.
Troubleshooting Will Delay
- Will still fires immediately: session expiry is 0 or shorter than the will delay, or you are connecting with MQTT 3.1.1.
- Will fires even though the device reconnected quickly: the device reconnected with Clean Start true or a different client ID, so it was a new session.
- Will never fires: the client disconnected cleanly, or the delay is longer than you waited. Remember to add keep alive detection time.
- Stale
offlineshown after reconnect: publish a retainedonlineon every connect, not just the first one.
New to these concepts? The MQTT tutorial explains wills, sessions and retained messages from the start.
Build Reliable Device Status on ConnectMQ
ConnectMQ is a managed MQTT broker with full MQTT 5.0 support, including will properties, session expiry and retained messages. Its portal adds threshold alert rules and an alerts log, so cleaner status data at the protocol level means fewer noisy alerts from the rules you build on it. Start free on the Serverless plan, no credit card needed, connect with YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID, and test your will delay settings on your own broker.
Frequently Asked Questions
What is the default MQTT will delay interval?
The default is 0 seconds, which means the will message is published as soon as the broker detects an abnormal disconnect, just like in MQTT 3.1.1.
Why does my will message ignore the delay?
Most often the session expiry interval is 0 or shorter than the will delay. The broker publishes the will when the session ends, so the session must last at least as long as the delay.
Is will delay available in MQTT 3.1.1?
No. It is an MQTT 5 feature. With MQTT 3.1.1 you would have to debounce offline events in the consuming application instead.
Does a clean disconnect trigger the will message?
No. A normal DISCONNECT discards the will. In MQTT 5 a client can deliberately request the will by disconnecting with reason code 0x04 (Disconnect with Will Message).



