Broker Testing

MQTT Broker Load Testing: Benchmark Throughput and Latency

How to load test an MQTT broker: measure messages per second, connection limits and p99 latency with emqtt-bench, mqtt-benchmark and a Python script.

Updated · 9 min read

MQTT Broker Load Testing: Benchmark Throughput and Latency

A broker that works with one test client can still fall over with a thousand devices. Load testing an MQTT broker answers three questions before production does: how many concurrent connections it accepts, how many messages per second it can route, and what latency your subscribers see while it is busy.

This guide shows how to plan a benchmark, run it with two open-source tools and a Python script, and read the numbers without fooling yourself.

What to measure

MetricWhat it tells youTypical target
Connection rateHow fast the broker accepts new CONNECTs (reconnect storms!)Hundreds per second
Concurrent connectionsMemory and file-descriptor limitsYour fleet size × 1.5
Publish throughputMessages per second accepted from publishersPeak traffic × 2
Fan-out throughputMessages per second delivered to subscribersDepends on topic design
End-to-end latency (p50 / p99)Time from publish to deliveryp99 under 50 ms in-region
Error rateDisconnects, dropped QoS 0 messages, timeoutsClose to zero

Always benchmark the same QoS, payload size and topic structure that your devices use. A test that publishes 10-byte QoS 0 messages to one topic says nothing about 2 KB QoS 1 telemetry spread over 50,000 topics. See MQTT QoS levels for how each level changes the packet count.

Pick the right target

Never run a heavy benchmark against a shared public sandbox — the numbers are meaningless (you are competing with every other user) and the operators will throttle or block you. Smoke-test there, then point the load at an instance you control.

For the examples below we use broker.connectmq.com; replace the host, port and credentials with your own instance.

1. emqtt-bench

emqtt-bench is an Erlang tool that can open tens of thousands of connections from one machine. The easiest way to run it is Docker:

Connection test — 2,000 clients at 200/s
docker run --rm -it emqx/emqtt-bench conn \
  -h broker.connectmq.com -p 1883 \
  -c 2000 -R 200 \
  -u "$MQTT_USER" -P "$MQTT_PASS"

Then measure throughput with a subscriber group and a publisher group running at the same time:

Terminal 1 — 100 subscribers
docker run --rm -it emqx/emqtt-bench sub \
  -h broker.connectmq.com -p 1883 \
  -c 100 -t 'bench/%i/#' -q 1
Terminal 2 — 100 publishers, 1 msg every 10 ms each
docker run --rm -it emqx/emqtt-bench pub \
  -h broker.connectmq.com -p 1883 \
  -c 100 -I 10 -t 'bench/%i/data' -s 256 -q 1

%i expands to the client index, so every publisher has its own topic and its own subscriber — a realistic one-to-one telemetry pattern. 100 publishers × 100 msg/s gives a target of 10,000 messages per second. emqtt-bench prints sent and received rates every second; if the received rate falls behind the sent rate, the broker (or your network) is the bottleneck.

2. mqtt-benchmark (Go)

If you prefer a single binary, mqtt-benchmark publishes a fixed number of messages and reports throughput and latency statistics:

Install and run
go install github.com/krylovsk/mqtt-benchmark@latest

mqtt-benchmark \
  --broker tcp://broker.connectmq.com:1883 \
  --clients 50 --count 1000 --size 512 --qos 1 \
  --topic bench/go --format text

The summary at the end includes messages per second per client, the aggregate rate and mean / standard deviation of publish times — a quick way to compare two brokers or two configurations.

3. A latency script you can trust

Throughput tools measure how fast messages go in. Users feel how fast they come out. This Python script (paho-mqtt 2.x) embeds the send time in every payload and computes end-to-end percentiles on the subscriber side:

latency.py
import json, statistics, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"bench/{uuid.uuid4().hex[:8]}/latency"
N, RATE = 2000, 200          # messages, messages per second
samples = []

def on_message(client, userdata, msg):
    sent = json.loads(msg.payload)["t"]
    samples.append((time.perf_counter() - sent) * 1000)

sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
sub.on_message = on_message
sub.connect(HOST, PORT)
sub.subscribe(TOPIC, qos=1)
sub.loop_start()

pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)                # let the subscription settle

for i in range(N):
    pub.publish(TOPIC, json.dumps({"i": i, "t": time.perf_counter()}), qos=1)
    time.sleep(1 / RATE)

time.sleep(3)
samples.sort()
p = lambda q: samples[int(len(samples) * q) - 1]
print(f"received {len(samples)}/{N}")
print(f"p50 {p(0.50):.1f} ms  p95 {p(0.95):.1f} ms  p99 {p(0.99):.1f} ms  max {samples[-1]:.1f} ms")
print(f"mean {statistics.mean(samples):.1f} ms")

Because publisher and subscriber run in the same process, they share one clock — no NTP drift in the result. Run it once idle and once while emqtt-bench is hammering the broker: the difference is the latency your devices will see at peak.

Reading the results

  • Received < sent at QoS 0 means the broker or the client dropped messages under pressure. Retry at QoS 1 to see whether it is a delivery or a capacity problem.
  • p99 far above p50 points to garbage-collection pauses, disk-backed persistence or a saturated network link. Check broker CPU and the load generator's CPU — a maxed-out benchmark machine is the most common false result.
  • Connection test stalls at a round number (1,024, 4,096, 65,535) — you hit a file-descriptor or ephemeral-port limit on the load generator, not the broker. Raise ulimit -n or spread the load across several machines or source IPs.
  • Disconnects during the run — look for keep-alive timeouts or the broker's per-client inflight / queue limits. See MQTT keep alive and connection errors.

A repeatable load-test checklist

  1. Smoke-test connectivity first with the online MQTT client or mosquitto_pub / mosquitto_sub.
  2. Fix the scenario: number of clients, QoS, payload size, topic layout, retained or not.
  3. Ramp up gradually (10 %, 50 %, 100 %, 150 % of expected peak) and hold each step for a few minutes.
  4. Record broker CPU, memory and connection count next to the client-side numbers.
  5. Repeat the run twice; only trust results that reproduce.
  6. Re-run after every broker upgrade or configuration change.

Smoke-test the broker in your browser first

Load testing turns "it works on my laptop" into a number you can plan capacity around. Start small, measure latency where your users are, and keep the scripts in your repository so the next benchmark takes minutes instead of days.

Frequently asked questions

Can I load test a public MQTT broker?

Only lightly. Public sandboxes are shared and rate-limited, and heavy traffic degrades the service for everyone else. Run real load tests against a broker you own or a private instance provided for testing.

What is a good MQTT latency?

On the same region, a healthy broker delivers QoS 0 messages end-to-end in a few milliseconds and QoS 1 in under 20 ms at moderate load. Track p99, not the average — spikes are what break devices.

Which tool is best for MQTT benchmarks?

emqtt-bench is the most complete open-source option for connections, publish and subscribe load. mqtt-benchmark (Go) is simpler to install. A small custom script is best when you need your exact payloads and topic layout.