Fundamentals

MQTT Keep Alive Explained: PINGREQ, Timeouts and Tuning

How MQTT keep alive works: the 1.5x timeout rule, PINGREQ and PINGRESP, MQTT 5 Server Keep Alive, NAT and mobile pitfalls, and how to choose a good interval.

Updated · 7 min read

TCP connections can die silently. A router reboots, a mobile phone switches cells, a NAT table entry expires — and neither side receives a clean close. MQTT's keep alive mechanism exists so that both the client and the broker notice a dead connection within a predictable time, and so that the broker can publish the client's Last Will message when it happens. This article explains exactly how keep alive works in MQTT 3.1.1 and 5, and how to pick a value that suits your network.

What the keep alive interval is

The keep alive is a 16-bit value, in seconds, that the client sends in its CONNECT packet. It is a promise: "I will never be silent for longer than this." The maximum value is 65535 seconds (a little over 18 hours), and 0 disables the mechanism entirely.

Crucially, the timer is about any control packet, not just pings. A client that publishes a sensor reading every 10 seconds with a 60-second keep alive never needs to send a ping, because the PUBLISH packets already prove it is alive. Pings are only sent when the client has nothing else to say.

PINGREQ and PINGRESP

When the keep alive period is about to elapse without the client sending anything, it sends a PINGREQ. The broker must answer with a PINGRESP. Both packets are tiny: just a 2-byte fixed header with no variable header and no payload.

PacketDirectionBytes on the wirePurpose
PINGREQClient → broker0xC0 0x00Proves the client is alive; asks if the broker is alive
PINGRESPBroker → client0xD0 0x00Proves the broker and the path back to the client work

Two bytes of MQTT is not the whole story — TCP/IP, TLS records and, for browsers, WebSocket framing all add overhead to every ping. If you are sizing a cellular data plan, the MQTT packet size calculator helps you estimate the MQTT part of each packet so you can add your transport overhead on top.

The 1.5× rule: when a connection is declared dead

The two sides use the keep alive differently:

  • Broker side — if the broker does not receive any control packet from the client within one and a half times the keep alive period, it must close the network connection as if the network had failed. With a 60-second keep alive, that is 90 seconds of silence.
  • Client side — if the client sends a PINGREQ and does not receive a PINGRESP within a reasonable time, it should close the connection itself. Libraries usually treat a missing ping response by the next keep alive tick as a failure and start reconnecting.

Because the broker treats a keep alive timeout as an abnormal disconnect, the client's Last Will is published. This is what makes the classic "device offline" pattern work: set a retained will of offline on devices/42/status, and the broker announces the failure on your behalf at most 1.5 keep alive periods after the device disappears.

Server Keep Alive in MQTT 5

In MQTT 3.1.1 the client decides the keep alive and the broker has to accept it (or refuse the connection). MQTT 5 adds a Server Keep Alive property to CONNACK. If the broker includes it, the client must use the broker's value instead of the one it asked for. Brokers use this to stop clients from requesting a keep alive of 0 or many hours, which would let dead connections pile up.

MQTT 5 also makes the failure explicit: before closing, a broker may send a DISCONNECT with reason code 0x8D (Keep Alive timeout). You can look that and every other code up in the MQTT reason codes reference. See MQTT 5 vs 3.1.1 for the other protocol differences.

Brokers often enforce limits on the server side as well. Mosquitto, for example, has a max_keepalive option: MQTT 5 clients get the capped value through Server Keep Alive, while MQTT 3.1.1 clients that ask for more may be rejected.

Setting keep alive in common clients

Most libraries default to 60 seconds. Here is how to change it:

mosquitto_sub
# -k sets the keep alive in seconds
mosquitto_sub -h broker.hivemq.com -t 'testmqtt/ka-demo/#' -k 30 -v
MQTT.js
import mqtt from 'mqtt';

const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt', {
  keepalive: 30,        // seconds; 0 disables
  reconnectPeriod: 2000 // ms between reconnect attempts
});
Paho MQTT (Python)
import paho.mqtt.client as mqtt

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.connect("broker.hivemq.com", 1883, keepalive=30)
client.loop_forever()

Want the full snippet for your language? The MQTT code generator produces connection code with the keep alive already set. To watch the behaviour live, connect the online MQTT client with a short keep alive and leave it idle.

Try it in the online MQTT client

NAT, firewalls and mobile networks

Keep alive has a second job that the spec does not mention: keeping middleboxes happy. NAT gateways, carrier-grade NAT on mobile networks, load balancers and stateful firewalls all drop idle TCP flows after a timeout. Once the mapping is gone, the client still believes it is connected, but nothing it sends reaches the broker and nothing the broker sends reaches the client.

  • Stay under the shortest idle timeout on the path. Cloud load balancers and mobile carriers often use idle timeouts of a few minutes, sometimes less. A keep alive comfortably below that keeps the mapping alive.
  • Do not rely on TCP keepalive. Operating-system TCP keepalive defaults are usually measured in hours, far too slow to protect an MQTT session.
  • Balance against battery. Every ping wakes the radio. On battery-powered cellular devices, longer intervals combined with a persistent session (so missed messages are queued) are often the better trade-off.
  • WebSockets are no exception. Browser clients behind proxies face the same idle timeouts; MQTT keep alive works the same way over MQTT over WebSockets.

How to choose a keep alive value

ScenarioSuggested keep aliveWhy
Dashboards and backend services on stable networks30–60 sLibrary default, low overhead, detection in about a minute
Devices where "offline" must show quickly10–20 sWill message fires within roughly 15–30 s
Cellular / NAT-heavy networksBelow the shortest idle timeout on the pathPrevents silent half-open connections
Battery-powered, infrequent reportingSeveral minutes, or connect–publish–disconnectFewer radio wake-ups; rely on persistent sessions

Troubleshooting keep alive disconnects

If clients disconnect every 1.5× keep alive, the usual cause is a client loop that is not running — for example, Paho's network loop blocked by long-running work in a callback, so no PINGREQ is sent. Frequent reconnects at a fixed interval of a few minutes instead point to an idle timeout on a proxy or NAT. For other causes, see the MQTT connection errors guide.

Frequently asked questions

What is a good MQTT keep alive interval?

For most devices on stable networks, 30 to 60 seconds is a sensible default. Use shorter values (15 to 30 seconds) when you need to detect dead clients quickly or must stay below an aggressive NAT timeout, and longer values on battery-powered devices where every radio wake-up costs energy.

What happens when the MQTT keep alive times out?

If the broker receives no control packet from the client within one and a half times the keep alive interval, it closes the network connection and treats it as an abnormal disconnect. That means the Last Will message, if any, is published. In MQTT 5 the broker may send DISCONNECT with reason code 0x8D (Keep Alive timeout) first.

Does setting keep alive to 0 disable it?

Yes. A keep alive of 0 turns the mechanism off, so the broker never disconnects the client for being idle. This is rarely a good idea, because half-open TCP connections can then stay around for a very long time without anyone noticing.