Most MQTT guides start with the protocol. Let's start with the problem instead: you have devices in the field, a backend in the cloud, and a network between them that drops out far more often than anyone likes to admit.
MQTT — short for Message Queuing Telemetry Transport — is a lightweight publish–subscribe messaging protocol built for exactly that situation. Instead of devices calling your backend directly, a broker sits in the middle: devices publish their readings to named topics, your backend subscribes to the topics it cares about, and the two never talk to each other directly. That one change absorbs most of the hard problems of scale and flaky connectivity.
The whole integration comes down to four decisions: which broker to run, how to name your topics, which delivery guarantees to pay for, and how to lock it all down. This guide walks through each one with the defaults I use in production — where an MQTT backbone moves live telemetry from 1,000+ tracked assets into a Spring Boot backend in under a second.
What is MQTT, actually?
Think of the broker as a notice board rather than a phone line. A sensor doesn't call your server and wait for an answer — it pins a small message to a topic like sensors/site-a/temperature and moves on. Anything that cares about that topic gets the message the moment it appears.
fleet/…
That decoupling — not the famously small packets — is the real reason MQTT owns IoT messaging. The protocol adds a few bytes of overhead per message and was designed from day one for connections that drop: keep-alives, session resumption, and queued delivery for subscribers that were offline when the message arrived.
MQTT vs HTTP: when does a broker earn its place?
Honest answer first: plenty of projects don't need MQTT. One device, decent connectivity, a reading every few minutes? A plain POST to your API with retry logic is simpler to build, simpler to debug, and one less thing to run. Reach for a broker when you recognize your project in this list:
- Device count is growing — Per-device HTTP retry logic multiplies with every unit you ship; broker fan-in stays flat no matter how many devices connect.
- Connectivity is unreliable — MQTT's keep-alive and QoS machinery handles dropouts gracefully. Hand-rolled HTTP retries handle them badly, and usually at 3 a.m.
- More than one thing consumes the data — Dashboard, alerting, analytics — pub-sub fan-out is free. With HTTP, something has to duplicate and forward every reading.
- Commands flow back to devices — A subscribed device hears its command instantly. An HTTP device has to poll and ask "anything for me?" over and over.
One of these alone is survivable with HTTP and stubbornness. Two or more, and the broker stops being extra infrastructure and starts being the cheapest component you run.
Which broker should you choose?
For most deployments: Mosquitto, the small, stable, packaged-everywhere reference broker. It comfortably handles tens of thousands of connected clients on modest hardware, and its limits are operational rather than performance-related. Here's how the realistic options compare:
| Broker | Best for | The catch |
|---|---|---|
| Mosquitto | Almost everyone. Small, stable, handles tens of thousands of clients on modest hardware. | Single node — no built-in clustering — and config-file management. |
| EMQX / HiveMQ | Outgrowing Mosquitto: clustering, built-in dashboards, MQTT-over-WebSocket at scale. | More moving parts to operate; the useful features pull toward paid tiers. |
| AWS IoT Core (managed) | Fleets where device identity — certificates, provisioning, rotation — is the harder half of the problem. | You trade money for operations, and pricing scales with message volume. |
And when you do install it, don't ship the defaults. This is the configuration shape I'd actually deploy:
# /etc/mosquitto/conf.d/production.conf
# Never ship the default open listener.
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/broker.crt
keyfile /etc/mosquitto/certs/broker.key
# Every client authenticates; no anonymous devices.
allow_anonymous false
password_file /etc/mosquitto/passwd
# Per-client topic permissions.
acl_file /etc/mosquitto/acl
# Persist queued QoS 1/2 messages across broker restarts.
persistence true
persistence_location /var/lib/mosquitto/How should you design the topic hierarchy?
Topics are your data model on the wire — design them like one, because renaming them after a fleet ships is miserable. The pattern that has held up across every one of my deployments is three segments:
What subscribers can ask for
sensors/+/environment — this reading, from every device
sensors/pi-greenhouse-01/# — everything from this one device
- 1.Describe the source, never the consumer — No dashboard/... topics. Consumers come and go; the sensor that produced the reading doesn't.
- 2.Keep it boring — Three or four segments, no cleverness. Every deep, elaborate hierarchy I've inherited was a liability by year two.
- 3.Give commands their own root — commands/{device-id}/..., with ACLs so each device can only read its own. A flat namespace where any device can publish anywhere is the security hole you find last.
What QoS level should you use?
Quality of service is the delivery contract between a client and the broker, set per message. The short answer: QoS 1 for anything you'd miss if it vanished, QoS 0 for chatty readings, and QoS 2 almost never.
At most once
Fire and forget — fastest, but a dropped connection drops the message.
Chatty readings where the next sample replaces this one anyway.
At least once
Delivery guaranteed — but duplicates happen, so the backend must dedupe.
Telemetry you can't afford to lose. The default for most data.
Exactly once
No loss, no duplicates — paid for with a 4-packet handshake per message.
Almost never. Deduping on the backend gets you there cheaper.
The one caveat that matters: QoS 1 means duplicates will happen, so your backend must deduplicate — ideally on the device's own timestamp and identity, never on arrival order. Get that right and QoS 2's expensive four-packet handshake buys you nothing you don't already have.
How do you secure a broker for production?
The config earlier in this guide already encodes the non-negotiables — TLS on port 8883 (plaintext 1883 belongs in the lab, nowhere else), no anonymous connections, and an ACL file. Three practices turn that checklist into actual security.
Give every device its own identity
One shared password across a fleet means one compromised device forces you to re-provision everything. Per-device credentials mean revocation is a one-line change.
Write ACLs from the topic design
A sensor publishes to sensors/{its-id}/# and subscribes to commands/{its-id}/# — nothing else. A hijacked device can then lie about itself, but it can't impersonate the fleet or eavesdrop on it.
Watch the broker itself
This is the step most guides skip. Mosquitto publishes its own health under $SYS/# — connected clients, message rates, dropped messages. Pointing your monitoring stack at those topics is a ten-minute job that turns "the devices went quiet and nobody noticed" into an alert.
How does the backend plug in?
From the broker's point of view, your backend is just another client: it authenticates, subscribes to the telemetry tree, and consumes messages as they arrive. The full Spring Boot wiring — validation, timestamp normalization, deduplication, persistence, live fan-out — is its own article, and I've written it: the Raspberry Pi + Spring Boot guide linked below covers that half of the pipeline end to end. Between the two, you have the whole path from sensor to screen.
When to bring in help
Broker setup is a solved problem — you can do it from this guide. Where deployments actually go wrong is the surrounding design: topic schemas that don't scale, QoS chosen globally, security bolted on after provisioning, ingestion that trusts arrival time. If your MQTT layer is becoming business-critical, that design review is exactly what I do as a consulting engagement.