MQTT Last Will and Testament (LWT) is the feature that lets a broker speak for a client that can no longer speak for itself. A device registers a "will" message when it connects, and the broker publishes it if that device disappears without a clean disconnect. By the end of this article you will know every will field, the precise conditions that trigger it, and how to test it yourself by killing a client connected to a live broker.
Why MQTT Needs a Last Will
MQTT is designed for unreliable networks. Devices lose power, firmware hangs, cellular links drop for minutes. In a pure publish/subscribe system, the publisher and subscriber never talk to each other directly, so nobody would notice that a sensor went silent until a human looked at a stale dashboard.
The MQTT broker is the only party that sees every connection. LWT turns that position into a feature: the client tells the broker in advance "if I vanish, publish this for me". Subscribers then learn about the failure through the same topics they already watch.
The Fields of MQTT Last Will and Testament
The will is set inside the CONNECT packet, so it must be configured before the client connects. It cannot be changed during a session; to change it, reconnect.
| Field | Purpose | Typical value |
|---|---|---|
| Will topic | Where the broker publishes the will | site/line1/pump-07/status |
| Will payload | Message body | offline or {"state":"offline"} |
| Will QoS | Delivery guarantee for the will | 1 |
| Will retain | Whether the will becomes the retained value | true |
| Will delay interval (MQTT 5) | Seconds to wait before publishing | 0 to a few seconds |
| Will properties (MQTT 5) | Message expiry, content type, user properties | Optional |
Most production setups use QoS 1 and retain true. Retain is what makes the status useful to clients that connect later; without it, a dashboard that opens after the failure never learns the device is down. Retained behaviour is explained in detail in MQTT retained messages.
When the Broker Publishes the Will
The broker publishes the will when the network connection closes without a DISCONNECT packet. In practice that covers:
- The TCP connection is closed or reset by the client OS, for example a killed process.
- The keepalive timer expires. The broker waits roughly 1.5 times the keepalive interval with no packets before declaring the client dead.
- The client violates the protocol and the broker closes the connection.
- A network failure the broker detects through an I/O error.
It does not publish the will when:
- The client sends a normal DISCONNECT. The will is discarded.
- In MQTT 5, the client reconnects before the will delay interval expires, resuming its session.
That second rule is important: a clean shutdown produces no will. If you want "offline" on graceful shutdown too, publish it yourself before disconnecting. MQTT 5 also lets a client send DISCONNECT with reason code 0x04 (disconnect with will message) to ask the broker to publish the will anyway.
Testing LWT by Killing a Client
The fastest way to build intuition is to watch a will fire. We use the public MQTT test broker at broker.connectmq.com with a unique topic prefix.
Terminal 1, the watcher:
mosquitto_sub -h broker.connectmq.com -p 1883 -i lwt-watch-q81m \
-t 'connectmq/demo/q81m/#' -v
Terminal 2, the "device". It registers a retained will, uses a 10-second keepalive, and stays subscribed so the process keeps running:
mosquitto_sub -h broker.connectmq.com -p 1883 -i lwt-device-q81m -k 10 \
--will-topic connectmq/demo/q81m/device1/status \
--will-payload offline --will-qos 1 --will-retain \
-t connectmq/demo/q81m/device1/cmd
Terminal 3, announce the device as online with a retained birth message:
mosquitto_pub -h broker.connectmq.com -p 1883 -i lwt-birth-q81m \
-t connectmq/demo/q81m/device1/status -m online -q 1 -r
Now kill the device abruptly. On Linux or macOS:
pkill -9 -f lwt-device-q81m
Terminal 1 prints connectmq/demo/q81m/device1/status offline almost immediately, because the operating system closed the socket and the broker saw it drop without DISCONNECT. If you instead pull the network cable or suspend a laptop, the will arrives only after the keepalive window passes, about 15 seconds with -k 10.
Try one more experiment: restart the device process and stop it with Ctrl+C. The Mosquitto tools send a proper DISCONNECT on Ctrl+C, so no will is published and the retained status stays whatever it was. That is the gap you close with an explicit offline publish on shutdown.
LWT in Python with paho-mqtt
Here is a complete device skeleton using paho-mqtt 2.x. It sets the will before connecting, publishes a retained birth message in on_connect (so it is repeated after every reconnect), and publishes "offline" on graceful exit:
import signal
import sys
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
CLIENT_ID = "lwt-py-device-q81m"
STATUS = "connectmq/demo/q81m/device2/status"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.will_set(STATUS, payload="offline", qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("connect failed:", reason_code)
return
client.publish(STATUS, "online", qos=1, retain=True)
print("online")
def shutdown(signum, frame):
client.publish(STATUS, "offline", qos=1, retain=True).wait_for_publish(timeout=5)
client.disconnect()
client.loop_stop()
sys.exit(0)
client.on_connect = on_connect
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
client.connect(BROKER, 1883, keepalive=15)
client.loop_start()
while True:
time.sleep(1)
Run it, watch "online" appear in the watcher terminal, then kill it with kill -9 to see the broker-published will, or press Ctrl+C to see the script's own offline message.
Design Tips for Reliable Offline Detection
Pick a sensible keepalive. The keepalive sets the worst-case detection time for silent failures. 15 to 60 seconds is common for mains-powered devices. Very short values cost battery and bandwidth; very long ones delay alerts. Our guide to the MQTT keep alive interval covers the trade-offs.
Always pair LWT with a birth message. The will only says "offline". Something has to say "online" again after reconnecting, and it must be retained so it overwrites the retained will.
Use the same topic and retain flag for both. If the will is retained but the birth is not, new subscribers see "offline" forever.
Watch out for duplicate client IDs. If two devices share a client ID, the broker disconnects one when the other connects, and they kick each other off in a loop. Status flaps between online and offline. Make every client ID unique.
Consider a will delay in MQTT 5. A short delay prevents an "offline" flash when a device reconnects quickly after a network blip. See the MQTT will delay interval for details.
Include context in JSON payloads. A payload such as {"state":"offline","reason":"lwt"} lets consumers tell a crash from a planned shutdown.
Last Will and Testament on ConnectMQ
ConnectMQ supports LWT on both MQTT 3.1.1 and 5.0 connections, including retained wills. On a production broker you connect with the credentials from the portal (YOUR_USERNAME, YOUR_PASSWORD, YOUR_CLIENT_ID) over TLS on port 8883, and the will works exactly as in the examples above. Because client IDs on ConnectMQ are bound to issued credentials, the duplicate-ID flapping problem is easier to avoid.
For broker-side visibility in addition to LWT, ConnectMQ also exposes connection state on $SYS/broker/connection/{clientId}/state, which is handy for monitoring tools that should not depend on each device configuring its will correctly. If you are deciding where to host this, compare options in our hosted MQTT broker overview, and for the protocol fundamentals see the full MQTT tutorial.
Get Started with ConnectMQ
Sign up at app.connectmq.com and create a broker on the free Serverless plan, no credit card required. Point your devices at it with a retained Last Will and a birth message, and you have reliable online/offline status from day one.
Frequently Asked Questions
Is the Last Will published when a client disconnects normally?
No. A normal DISCONNECT tells the broker to discard the will. Publish your own offline message before disconnecting if you need one for planned shutdowns.
How long does it take for the broker to publish the will?
If the socket closes, it is almost immediate. If the device silently disappears, the broker waits about 1.5 times the keepalive interval, plus any MQTT 5 will delay interval.
Should the will message be retained?
Usually yes. A retained will ensures that clients connecting after the failure still see the device as offline.
Can I change the will after connecting?
No. The will is part of the CONNECT packet. To change it, disconnect and connect again with new will settings.



