MQTT load testing isn’t just about seeing how many connections your broker can handle; it’s about understanding how your application behaves when those millions of devices start sending data.

Let’s watch a small, simulated MQTT load test come to life. We’ll use mosquitto_bench from the Mosquitto project, a common choice for this kind of work.

First, we need a broker. If you don’t have one running, a quick local setup:

docker run -d -p 1883:1883 eclipse-mosquitto

Now, let’s simulate 100 clients publishing messages every second. We’ll target the test/topic and publish 1000 messages per client.

mosquitto_bench -h localhost -p 1883 -t test/topic -c 100 -n 1000 -i 1000

This command breaks down as:

  • -h localhost: Connect to the broker on localhost.
  • -p 1883: Use the standard MQTT port.
  • -t test/topic: Publish to this topic.
  • -c 100: Start 100 concurrent clients.
  • -n 1000: Each client will publish 1000 messages.
  • -i 1000: Each client will have a 1000ms (1 second) interval between messages.

When you run this, you’ll see output like this:

Client 0 started
Client 1 started
...
Client 99 started
Publishing 1000 messages from 100 clients at 1000ms intervals.
[2023-10-27 10:00:00] 100 clients connected.
[2023-10-27 10:00:01] 1000 messages published (1000/sec). 0 failed.
[2023-10-27 10:00:02] 2000 messages published (1000/sec). 0 failed.
...
[2023-10-27 10:01:40] 100000 messages published (1000/sec). 0 failed.
100 clients disconnected.

This simple test shows you the throughput your broker can handle for publishing. You can then increase -c to see connection limits and -n to see how sustained load affects things.

But what if your devices also subscribe? Let’s add subscribers. We’ll have 50 clients subscribing to test/topic and 50 publishing.

# In one terminal:
mosquitto_bench -h localhost -p 1883 -t test/topic -c 50 -n 1000 -i 1000 -V 5 -u testuser -P testpass

# In another terminal:
mosquitto_bench -h localhost -p 1883 -t test/topic -c 50 -n 1000 -i 1000 -V 5 -u testuser -P testpass --subscribe

Here, --subscribe tells the clients to subscribe to the topic instead of publishing. The -V 5 uses MQTT v5, and -u testuser -P testpass are for authentication, which you’d typically configure on your broker.

The real power comes from understanding what each parameter does to the system. -c stresses the connection management and authentication mechanisms. -n tests the message persistence and queueing. -i (the interval) directly impacts the rate of incoming messages. When you ramp up to millions of devices, you’re not just hitting network bandwidth; you’re hitting CPU for packet processing, memory for connection state, and disk I/O if persistence is enabled.

The most surprising thing about MQTT load testing is how often the bottleneck isn’t the broker’s raw message throughput, but the overhead of managing millions of individual connections. Each connection requires kernel resources for sockets, memory for state, and CPU for TLS handshakes and packet parsing. A broker might advertise millions of connections per second, but that often assumes lightweight clients and minimal per-connection processing. When you add authentication, authorization, topic filtering, and message persistence, that number can drop dramatically.

Consider a scenario where you have 1 million clients, and each client publishes one message every 10 seconds. That’s 100,000 messages per second hitting your broker. If each message is small (e.g., 50 bytes), and your broker is configured for minimal persistence, you might be fine. But if those messages are larger, or if your clients also subscribe and your broker has to fan out messages to many subscribers, the load shifts from pure ingress to internal processing and egress.

The critical lever you control is the per-client load. If you have 1 million devices, and each device sends a 1KB message every minute, that’s (1,000,000 devices * 1024 bytes/message * 1 message/60 seconds) = ~17MB/s ingress. Manageable. But if each device sends a 1KB message every second, that’s ~1GB/s ingress, which is a completely different beast. Your load testing must reflect these per-client behaviors.

When you’re testing with a large number of clients, especially with TLS enabled, the CPU cost of the TLS handshake for each connection can become a significant factor. If your load test tool isn’t also performing TLS handshakes, or if it’s using a highly optimized TLS library, your real-world performance with actual devices will likely be lower than your test results suggest. This is why simulating the exact client behavior, including TLS, is crucial for accurate scaling predictions.

The next challenge is understanding how authorization rules and complex topic hierarchies impact performance.

Want structured learning?

Take the full Mqtt course →