The MQTT keep alive interval is the number of seconds a client promises never to stay silent for, and it is the main tool a broker has for spotting dead connections. In this guide you will learn exactly how PINGREQ, PINGRESP and the 1.5x timeout rule work, how keep alive interacts with NAT routers, cellular links and battery life, and which values to start with for your devices.
What the MQTT Keep Alive Interval Actually Does
When a client sends CONNECT, it includes a two-byte Keep Alive field measured in seconds. The value is a contract: "I will send some control packet to you at least once in this period." Any packet counts, whether it is a PUBLISH, a SUBSCRIBE or a PUBACK. Only when the client has nothing else to send does it fall back to a tiny PINGREQ packet, and the broker answers with PINGRESP.
Keep alive exists because TCP itself is bad at noticing dead peers. If a device loses power or its cellular modem drops off the network, no FIN or RST packet is ever sent. The broker's socket stays "open" forever from its point of view, and any subscriber waiting for the device's Last Will and Testament waits forever too. Keep alive gives both sides a deadline.
The 1.5x Timeout Rule
The MQTT specification says that if the broker receives nothing from a client within one and a half times the keep alive interval, it must close the network connection as if it had failed. That means:
| Keep alive | Broker gives up after | Typical use |
|---|---|---|
| 15 s | 22.5 s | Interactive dashboards, fast failover |
| 30 s | 45 s | Mains-powered devices on unreliable Wi-Fi |
| 60 s | 90 s | Sensible default for most devices |
| 120 s | 180 s | Cellular devices watching data usage |
| 300 s | 450 s | Rarely chatty gateways on stable links |
| 0 | Never | Keep alive disabled (avoid in production) |
The client has its own side of the deal. If it sends PINGREQ and does not get PINGRESP within a "reasonable amount of time" (most libraries use the keep alive value or a fixed timeout), it should treat the connection as broken and reconnect. That is how a client discovers a half-open socket on its end.
What Happens When the Timeout Fires
When the broker closes a connection because of keep alive, it treats it as an abnormal disconnect. Practical consequences:
- The client's will message is published (after the will delay, if you set one in MQTT 5; see MQTT will delay interval).
- If the client had a persistent session, the broker keeps its subscriptions and starts queuing QoS 1 and QoS 2 messages for it.
- Online status topics and dashboards flip to "offline".
Keep alive therefore directly controls how quickly your system learns that something went wrong.
Choosing an MQTT Keep Alive Value
There is no universally correct number. You are trading detection speed against traffic and power.
Detection Speed
A shorter interval means the broker and subscribers learn about failures faster. If an operator dashboard needs to show a machine as offline within half a minute, a 60 second keep alive (90 second detection) will not meet that requirement, while 15 or 20 seconds will.
NAT and Firewall Idle Timeouts
This is the pitfall that catches most teams. Home routers, corporate firewalls, cloud load balancers and especially mobile carrier NAT gateways drop TCP mappings that have been idle for a while. Once the mapping is gone, the client's next packet goes nowhere, and the broker only notices at the 1.5x deadline. If your keep alive is longer than the shortest NAT idle timeout on the path, you get mysterious disconnects every few minutes. Carrier NAT timeouts vary widely and are not always documented, so on cellular links it is safer to stay well under a few minutes.
Battery and Data Usage
Each PINGREQ/PINGRESP round trip is only 2 bytes of MQTT payload each way, plus TCP/IP (and TLS record) overhead. On Wi-Fi that is trivial. On a battery-powered cellular device the cost is mostly radio wake-ups, not bytes: every ping can keep the modem in a high-power state for seconds. For such devices, consider whether you need a permanent connection at all, or whether waking up, publishing and disconnecting with a persistent session is better.
A Practical Starting Point
- Mains-powered devices on Wi-Fi or Ethernet: 60 seconds.
- Dashboards and browser clients over WebSocket: 30 seconds.
- Cellular gateways: 60 to 120 seconds, then shorten if you see unexplained drops.
- Backend services in the same cloud region: 30 to 60 seconds.
- Never use 0 in production; it removes dead-connection detection entirely.
Setting the Keep Alive Interval in Common Clients
You can try all of these against the public MQTT test broker at broker.connectmq.com. Use a unique client ID and a unique topic prefix, and remember the public area is readable by everyone.
mosquitto_sub and mosquitto_pub
# Subscribe with a 30 second keep alive and debug output so you can see PINGREQ/PINGRESP
mosquitto_sub -h broker.connectmq.com -p 1883 -i ka-demo-7f3a \
-k 30 -d -t 'connectmq/demo/7f3a/#'
With -d you will see lines such as Client ka-demo-7f3a sending PINGREQ and received PINGRESP every 30 seconds while the topic is quiet.
Python (paho-mqtt 2.x)
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
client.subscribe("connectmq/demo/7f3a/#", qos=1)
def on_disconnect(client, userdata, flags, reason_code, properties):
print("Disconnected:", reason_code)
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id="ka-python-7f3a",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
# keepalive is the third argument, in seconds
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()
paho sends PINGREQ automatically from its network loop. If you block the loop (for example with a long time.sleep inside a callback), pings stop and the broker will drop you after 1.5x the interval.
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "ka-node-7f3a",
protocolVersion: 5,
keepalive: 45, // seconds
reconnectPeriod: 5000, // ms between reconnect attempts
});
client.on("connect", () => console.log("connected"));
client.on("close", () => console.log("connection closed"));
client.on("error", (err) => console.error(err.message));
ESP32 (PubSubClient)
PubSubClient defaults to 15 seconds. Change it before calling connect() with client.setKeepAlive(60);, and make sure client.loop() runs frequently in your sketch, because pings are only sent from inside loop(). Long delay() calls are the most common reason ESP32 boards drop off at regular intervals. A well-structured ESP32 sketch uses a non-blocking loop with millis() timers to avoid this.
Server Keep Alive in MQTT 5
MQTT 5 lets the broker override the client's request. If the CONNACK contains a Server Keep Alive property, the client must use that value instead of its own. Brokers do this to enforce a minimum or maximum interval across a shared platform. Most libraries apply it silently, so if you notice pings at a different rate than you configured, log the CONNACK properties. In paho you can print properties in on_connect; MQTT.js exposes them on the connack packet passed to the connect event.
Troubleshooting Keep Alive Problems
Use this checklist when clients disconnect at suspiciously regular intervals:
- Measure the gap. If disconnects happen about 1.5x your keep alive after the last activity, the client is not sending pings. Look for a blocked network loop.
- Check for a fixed idle drop. If drops happen after a constant number of minutes regardless of keep alive, a NAT or firewall is timing out. Lower the interval below that value.
- Look for duplicate client IDs. Two clients with the same ID kick each other off, which looks like a keep alive problem but is not. See MQTT client ID best practices.
- Watch for single-threaded callbacks. Heavy work inside
on_messagedelays PINGREQ. Move processing to a worker thread or queue. - Confirm TLS handshakes complete. On constrained devices, a slow TLS reconnect can push the next ping past the deadline.
- Enable debug logging in your library and compare client timestamps with broker connection events.
On a ConnectMQ broker you can also watch $SYS/broker/connection/{clientId}/state to see exactly when the broker considers a client connected or disconnected, which makes it easy to correlate with your device logs. The portal's in-browser MQTT client is a quick way to subscribe to that topic without installing anything.
Keep Alive, Sessions and Last Will Working Together
Keep alive is one piece of a bigger reliability design. A sensible production pattern looks like this:
- A keep alive short enough to beat NAT timeouts and meet your detection target.
- A retained "online" message published on connect, plus a retained "offline" will message.
- A persistent session so QoS 1 messages published while the device reconnects are not lost.
- Exponential backoff on reconnect, so a broker restart does not turn into a thundering herd.
The MQTT tutorial covers these building blocks from the beginning if you want the wider context.
Get Started with ConnectMQ
ConnectMQ is a fully managed MQTT service that supports MQTT 3.1.1 and 5.0 over TCP, TLS, WebSocket and secure WebSocket, so you can test any keep alive strategy on the same endpoints you will use in production. When you are ready to move beyond the public test broker, create a free Serverless broker with no credit card, copy the generated credentials, and watch connection state live in the portal. If you are still comparing options, our guide to choosing a cloud MQTT broker walks through the trade-offs.
Frequently Asked Questions
What is a good default MQTT keep alive interval?
60 seconds is a good default for most mains-powered devices. Shorten it to 15 to 30 seconds if you need faster offline detection, or if a NAT device on the path drops idle connections sooner.
Does any MQTT packet reset the keep alive timer?
Yes. Any control packet the client sends, including PUBLISH and PUBACK, counts as activity. PINGREQ is only sent when the client has been otherwise idle for the full interval.
What happens if I set keep alive to 0?
Keep alive is disabled, so the broker never closes a silent connection on its own. Dead devices may appear connected indefinitely and their will messages may never be sent, so avoid 0 in production.
Why does my client disconnect exactly every few minutes?
A constant interval regardless of your keep alive setting usually points to a NAT or firewall idle timeout. Set the keep alive below that timeout so pings keep the mapping alive.
Can the broker change my keep alive value?
In MQTT 5, yes. The broker can return a Server Keep Alive property in CONNACK, and the client must use that value instead of the one it requested.



