MQTT latency is the time between a publisher sending a message and a subscriber receiving it, and for most IoT systems it is dominated by things outside the protocol itself. This guide breaks down every component of round-trip time, explains which knobs actually matter, and gives you a small Python script to measure latency from your own network against a live broker.
What "Latency" Means in MQTT
People use the word for several different measurements, so be precise before you optimize:
- One-way latency: publisher to subscriber. Hard to measure accurately because the two clocks are on different machines.
- Round-trip time (RTT): a client publishes to a topic it is also subscribed to and measures how long until the message comes back. One clock, so the number is trustworthy. This is what the script below measures.
- Connect latency: time from opening a socket to receiving CONNACK. Relevant for devices that wake, connect, publish and sleep.
- Acknowledgement latency: time from PUBLISH to PUBACK at QoS 1, which tells you when the broker has taken responsibility for the message.
For devices that keep a connection open (the normal MQTT pattern), round-trip time is the most useful single number.
The Components of MQTT Latency
A message from a sensor to a dashboard passes through several stages. Each adds delay:
| Stage | What happens | Typical influence | Main lever |
|---|---|---|---|
| Client stack | Library queues, serializes, writes to socket | Small, but large on busy microcontrollers | Non-blocking loop, avoid long delay() calls |
| Last-mile network | Wi-Fi, cellular, satellite access | Often the largest and most variable part | Better link, cellular signal, wired gateway |
| Internet path | Routing to the broker's region | Grows with physical distance | Broker region close to devices |
| TLS | Encryption per packet, handshake per connect | Handshake costs extra round trips once; per-packet cost is small | Keep connections long-lived |
| QoS handshake | PUBACK or 4-step QoS 2 flow | Does not delay delivery much, but limits throughput | Choose QoS per data type |
| Broker processing | Auth, topic matching, fan-out | Usually small on a healthy broker | Avoid huge fan-outs and hot topics |
| Subscriber side | Same path in reverse, plus app processing | Mirrors the publisher side | Lightweight callbacks |
Network distance
Light in fiber is fast, but not instantaneous, and real routes are not straight lines. A device in Istanbul talking to a broker in another continent will always see a higher baseline than one talking to a broker in its own region. No protocol setting can remove this; only broker placement can.
TLS overhead
The TLS handshake adds round trips when the connection opens. After that, symmetric encryption costs very little on modern CPUs and only a modest amount on chips like the ESP32. Because MQTT clients normally stay connected for hours or days, TLS mostly affects connect latency, not steady-state message latency. That is why "TLS is slow" is rarely a good reason to use port 1883; see MQTT ports explained for the trade-offs.
QoS level
This is the most misunderstood part. At QoS 0, the broker forwards immediately. At QoS 1, the broker also forwards immediately on receipt, and the PUBACK travels back in parallel, so delivery latency is similar to QoS 0 on a healthy link. QoS 2 adds a four-step handshake; depending on the broker implementation, forwarding can happen after the first or second step. The bigger effect of higher QoS is on throughput and on what happens during packet loss: retransmissions add latency when the network misbehaves. The MQTT QoS levels guide covers the flows in detail.
Nagle, buffering and the client loop
Some stacks delay small writes to combine them. Many MQTT libraries disable Nagle's algorithm, but embedded stacks and custom sockets may not. On microcontrollers, a main loop that calls client.loop() only once per second adds up to a second of latency to incoming messages, regardless of how fast the network is.
Keep-alive and reconnects
Keep-alive does not affect latency on a healthy connection, but it decides how fast a dead connection is detected. A silently dropped link with a 300-second keep-alive can mean minutes before the client reconnects, and every message in that window is late.
Measure MQTT Latency Yourself
The script below connects to the public MQTT test broker at broker.connectmq.com, subscribes to a unique topic, publishes timestamped messages to it, and computes round-trip statistics. It stays under the public broker's limit of 10 messages per second per client by sending two per second.
# mqtt_rtt.py - measure MQTT round-trip latency
# pip install "paho-mqtt>=2.0"
import json
import statistics
import sys
import threading
import time
import uuid
import paho.mqtt.client as mqtt
HOST = "broker.connectmq.com"
USE_TLS = "--tls" in sys.argv
PORT = 8883 if USE_TLS else 1883
QOS = 1
SAMPLES = 50
INTERVAL = 0.5 # seconds between publishes (2 msg/s)
run_id = uuid.uuid4().hex[:8]
topic = f"connectmq/demo/{run_id}/rtt"
rtts = []
subscribed = threading.Event()
done = threading.Event()
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected to {HOST}:{PORT} -> {reason_code}")
client.subscribe(topic, qos=QOS)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
subscribed.set()
def on_message(client, userdata, msg):
received = time.perf_counter()
data = json.loads(msg.payload)
rtts.append((received - data["t"]) * 1000.0)
if len(rtts) >= SAMPLES:
done.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"rtt-{run_id}")
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
if USE_TLS:
client.tls_set() # uses the system CA store
t0 = time.perf_counter()
client.connect(HOST, PORT, keepalive=60)
client.loop_start()
if not subscribed.wait(10):
sys.exit("Subscribe timed out")
print(f"Connect + subscribe took {(time.perf_counter() - t0) * 1000:.1f} ms")
for seq in range(SAMPLES):
payload = json.dumps({"seq": seq, "t": time.perf_counter()})
client.publish(topic, payload, qos=QOS)
time.sleep(INTERVAL)
done.wait(10)
client.loop_stop()
client.disconnect()
if not rtts:
sys.exit("No messages received")
rtts.sort()
p95 = rtts[int(len(rtts) * 0.95) - 1]
print(f"samples={len(rtts)} lost={SAMPLES - len(rtts)}")
print(f"min={rtts[0]:.1f} ms median={statistics.median(rtts):.1f} ms "
f"p95={p95:.1f} ms max={rtts[-1]:.1f} ms")
Run it twice and compare:
python mqtt_rtt.py # plain TCP on 1883
python mqtt_rtt.py --tls # TLS on 8883
Reading the results
- Minimum approximates the best-case path: network plus broker with nothing in the way. It is your floor.
- Median is what users typically experience.
- p95 and max reveal jitter from Wi-Fi retries, cellular scheduling or congested links. For control loops and alarms, these matter more than the median.
- Connect + subscribe shows the TLS handshake cost. Expect it to be noticeably higher with
--tls, while the per-message median stays close to the plain TCP run. - Lost samples at QoS 1 usually mean the script ended before late messages arrived, or the connection dropped. Check the console for disconnects.
Run the test from where your devices actually live (the plant network, a cellular modem, a home router), not only from your office. The difference is often the most valuable finding.
Practical Ways to Reduce MQTT Latency
- Keep connections open. Connect once, publish many times. Reconnecting per message pays the TCP and TLS handshake every time.
- Choose a broker region near your devices. Distance sets the floor.
- Pick QoS per data type. High-rate telemetry where the next sample replaces the last can use QoS 0; commands and alarms deserve QoS 1.
- Keep payloads compact. Large JSON documents take longer to send on slow links. Consider binary encodings for high-rate data and MQTT 5 topic aliases to shorten repeated topic names.
- Service the client loop often. On Arduino or ESP32, call
loop()every iteration and avoid blocking delays. - Tune keep-alive to detect dead links quickly (30 to 60 seconds is common) without wasting bandwidth.
- Avoid unnecessary hops. Every bridge, gateway or Node-RED relay in the path adds its own processing and queueing.
- Make subscriber callbacks fast. Hand heavy work to a worker thread or queue so the network loop is not blocked.
Troubleshooting High or Spiky Latency
| Symptom | Likely cause | What to check |
|---|---|---|
| High minimum RTT | Distance to broker or slow access network | Ping and traceroute to the broker host |
| Good median, terrible p95 | Wi-Fi retries, cellular scheduling | Test on a wired link to compare |
| Latency grows over time | Subscriber cannot keep up, messages queue | Callback duration and inflight counts |
| Periodic multi-second gaps | Blocking code in the client loop | Long sleep or synchronous I/O in callbacks |
| Spikes only on reconnect | TLS handshake plus session resume | Connection stability and keep-alive |
| Throttling on the public broker | Rate limit of 10 msg/s per client | Lower publish rate or use a private broker |
From Measurement to Production
The public broker is shared, so treat its numbers as indicative of your network path rather than as a benchmark. For production traffic, a managed MQTT broker such as ConnectMQ gives you a private, authenticated endpoint on 8883 and 8084 without the public rate limits; plan details are in the cloud MQTT broker guide. To repeat the test against your own ConnectMQ broker, add client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD"), set the client ID to YOUR_CLIENT_ID, and run with --tls. For broader protocol background, see the MQTT tutorial and how MQTT works.
Get Started with ConnectMQ
Create a free Serverless broker at app.connectmq.com, no credit card needed, and point the latency script at it in two lines. ConnectMQ's in-browser client and live topic explorer help you see exactly when each message arrives.
Frequently Asked Questions
What is a good MQTT latency?
It depends on distance and access network. Compare your minimum RTT with a plain network ping to the broker host; if MQTT adds only a little on top, the broker is not your bottleneck.
Does QoS 1 make MQTT slower than QoS 0?
On a healthy link, delivery latency is similar because the broker forwards before the PUBACK completes. QoS 1 mainly adds overhead when packets are lost and must be resent.
Does TLS increase MQTT latency?
TLS adds handshake time when connecting, but on a long-lived connection the per-message overhead is small. Keeping connections open is the best way to minimize its impact.
Why measure round-trip instead of one-way latency?
Round-trip uses a single clock, so it avoids errors from unsynchronized clocks on publisher and subscriber machines.



