Fundamentals

MQTT Last Will and Testament (LWT) Explained

How MQTT Last Will and Testament works: when the broker sends it, will delay in MQTT 5, and the retained online/offline presence pattern with code examples.

Updated · 6 min read

Devices on unreliable networks rarely get the chance to say goodbye. Batteries die, Wi-Fi drops, processes crash. MQTT's Last Will and Testament (LWT) lets a client register a message in advance that the broker publishes on its behalf if the client disappears unexpectedly. It is the standard building block for presence detection and alerting in MQTT systems.

How Last Will works

The will is declared in the CONNECT packet. It has the same parts as a normal publish:

FieldMeaning
Will TopicWhere the broker will publish, e.g. acme/sensor-17/status
Will PayloadThe message body, e.g. offline
Will QoS0, 1 or 2 — see QoS levels
Will RetainWhether the will is stored as a retained message
Will Properties (MQTT 5)Will Delay Interval, Message Expiry, Content Type, User Properties…

The broker keeps the will alongside the session and does nothing with it while the client is connected. What happens next depends on how the connection ends.

When is the will published?

The broker publishes the will when the network connection closes without a normal disconnect, including when:

  • the broker detects an I/O error or network failure;
  • the client fails to communicate within the keep-alive period — the broker waits up to 1.5 × the keep-alive interval before treating the connection as dead;
  • the client closes the TCP/WebSocket connection without sending DISCONNECT;
  • the broker closes the connection because of a protocol error.

The will is not published when the client sends a normal DISCONNECT; a clean shutdown deletes it. MQTT 5 adds one exception: a client can disconnect with reason code 0x04 (Disconnect with Will Message) to close cleanly but still have the will sent.

Will Delay Interval (MQTT 5)

In MQTT 3.1.1 the will fires immediately. That is noisy for devices that reconnect quickly after a brief network blip: subscribers see offline followed a second later by online. MQTT 5 adds the Will Delay Interval property (in seconds):

  • The broker waits for the delay before publishing the will.
  • If the client reconnects with the same client ID within the delay, the will is not sent.
  • The will is published when the delay expires or when the session ends, whichever comes first — so a will delay longer than the Session Expiry Interval is effectively capped by it.
MQTT 5 will with a 30-second delay (MQTT.js)
const client = mqtt.connect('wss://broker.emqx.io:8084/mqtt', {
  protocolVersion: 5,
  clientId: 'sensor-17',
  properties: { sessionExpiryInterval: 300 },
  will: {
    topic: 'acme/sensor-17/status',
    payload: 'offline',
    qos: 1,
    retain: true,
    properties: { willDelayInterval: 30 },
  },
});

The presence pattern: online/offline status

The most common use of LWT combines it with retained messages so any client can see whether a device is online at any moment:

  1. Connect with a will: topic acme/sensor-17/status, payload offline, QoS 1, retain = true.
  2. Right after CONNACK, publish online to the same topic, QoS 1, retain = true. This overwrites any stale offline left from last time.
  3. On graceful shutdown, publish offline (retained) yourself, then send DISCONNECT. Because the will is discarded on a clean disconnect, this explicit publish is what keeps the status accurate.
  4. On a crash, the broker publishes the retained offline will for you.

Dashboards then subscribe to acme/+/status and immediately receive the current status of every device, thanks to retention.

presence.mjs (MQTT.js)
import mqtt from 'mqtt';

const statusTopic = 'acme/sensor-17/status';

const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt', {
  clientId: 'sensor-17',
  keepalive: 30,
  will: { topic: statusTopic, payload: 'offline', qos: 1, retain: true },
});

client.on('connect', () => {
  client.publish(statusTopic, 'online', { qos: 1, retain: true });
});

process.on('SIGINT', () => {
  client.publish(statusTopic, 'offline', { qos: 1, retain: true }, () => client.end());
});

Common pitfalls

  • Client ID collisions. If a second client connects with the same client ID, the broker disconnects the first one — and publishes its will. Two instances fighting over one ID produce a flapping online/offline status.
  • Libraries that always disconnect cleanly. Some SDKs send DISCONNECT on process exit, so the will never fires. Test by killing the process or cutting the network.
  • Forgetting retain. A non-retained will is only seen by clients that are subscribed at that moment.
  • Authorisation. The will is published with the client's permissions. If the client is not allowed to publish to the will topic, the broker may reject the connection or silently drop the will. See MQTT connection errors.

Setting a will from the command line

The Mosquitto clients accept will options directly, which makes quick experiments easy. Start a subscriber in one terminal, then connect a second client with a will and kill it with Ctrl+C or by closing the terminal:

bash
# terminal 1 — watch the status topic
mosquitto_sub -h test.mosquitto.org -t 'testmqtt/lwt-demo/#' -v

# terminal 2 — connect with a retained will and a 10 s keep-alive
mosquitto_sub -h test.mosquitto.org -t 'testmqtt/lwt-demo/cmd' -k 10 \
  --will-topic 'testmqtt/lwt-demo/status' --will-payload 'offline' \
  --will-qos 1 --will-retain

Depending on how the process ends, the client may or may not get to send DISCONNECT. The most reliable way to see the will is to cut the network (for example, disable Wi-Fi) and wait for the keep-alive to expire — exactly the failure that LWT is designed for.

How to test Last Will

  1. In one tab of the online MQTT client, subscribe to testmqtt/lwt-demo/#.
  2. In a second tab, connect with a will on testmqtt/lwt-demo/status and a short keep-alive.
  3. Close the second tab without disconnecting. Within the keep-alive window, the first tab receives the will.

Try it in the online MQTT client

Frequently asked questions

Why is my MQTT Last Will not being sent?

The broker only publishes the will when a client disconnects ungracefully. If your client library sends a DISCONNECT packet when it closes, the will is discarded. Also check the keep-alive: the broker may wait up to 1.5 times the keep-alive interval before it notices a dead connection.

Can I change the Last Will after connecting?

No. The will is part of the CONNECT packet and cannot be modified during the session. To change it, disconnect and connect again with a new will.

Should the Last Will message be retained?

For presence and status topics, yes. A retained offline will means that clients subscribing later still see the device as offline, instead of seeing nothing or an old online message.