MQTT

MQTT is a publish/subscribe messaging protocol designed for devices with very little of everything: memory, bandwidth, and reliable connectivity. Its fixed header is two bytes. A minimal publish costs a handful of bytes over TCP. It was built in the 1990s for satellite-linked oil pipeline telemetry, and those constraints — expensive links, unreliable connections, tiny devices — turn out to describe most of IoT.

The protocol's shape follows from three decisions. Clients never talk to each other; a broker sits in the middle, so devices need exactly one outbound connection and no inbound reachability. Messages are addressed to hierarchical topics rather than recipients, so producers and consumers are fully decoupled. And because devices drop off constantly, the protocol has built-in machinery for it: last will messages announce ungraceful disconnects, retained messages give new subscribers immediate state, and persistent sessions queue messages for absent clients.

TL;DR

Quick Example

A device that publishes telemetry and accepts commands — the canonical IoT shape:

Four production essentials appear there and are routinely omitted in tutorials: TLS, a last will, subscribing inside on_connect (so reconnects restore subscriptions), and idempotent command handling.

Core Concepts

The broker model

The broker is also the operational single point of failure and the natural place for authentication, authorization, and observability. Production deployments cluster it (EMQX, HiveMQ, VerneMQ) or use a managed service (AWS IoT Core, Azure IoT Hub, HiveMQ Cloud).

Topics

Topics are created implicitly by publishing — there's no registration step. That flexibility means topic design is entirely your discipline, and getting it wrong is expensive to change once thousands of devices are deployed.

Good structure: {domain}/{device-id}/{direction}/{detail}, most general to most specific, with the identity high in the tree so authorization rules can be written as prefixes. Avoid leading slashes, spaces, and $ (reserved for broker system topics like $SYS/).

Quality of Service

QoS is negotiated per hop, not end to end: publishing at QoS 2 to a broker doesn't mean a QoS 0 subscriber gets exactly-once delivery. The effective QoS is the minimum of the publish QoS and the subscription QoS.

The practical recommendation is QoS 1 with an idempotency key in the payload. QoS 2 costs four round trips and per-message broker state, and duplicate handling is something a robust consumer needs regardless. See Idempotency.

Retained messages

The broker stores the last retained message per topic and delivers it immediately to any new subscriber. This is how a dashboard shows current state on load instead of waiting for the next 30-second telemetry cycle.

Retain is for state, not events. Retaining "temperature is 22°C" is right; retaining "button pressed" means every future subscriber receives a stale button press. Clear a retained message by publishing an empty payload with retain=True.

Last Will and Testament

The client registers this at connect time. If the connection drops without a clean DISCONNECT — power loss, network failure, crash — the broker publishes it on the client's behalf. Combined with a retained "online" message on connect, this gives you accurate presence tracking that costs nothing while the device is healthy.

Detection latency is governed by the keep-alive: the broker declares a client dead after 1.5 × keep-alive with no traffic. A 60-second keep-alive means up to 90 seconds before the will fires.

Sessions and keep-alive

A persistent session (clean_session=False, or MQTT 5's sessionExpiryInterval) makes the broker remember the client's subscriptions and queue QoS 1 and 2 messages while it's offline. Essential for devices that sleep or have intermittent connectivity — and a memory cost on the broker that you should bound with an expiry.

Keep-alive is a tradeoff: shorter means faster failure detection and more radio wake-ups, which matters enormously on a battery device. Cellular and satellite links often push it to several minutes.

MQTT 5 additions

Shared subscriptions and message expiry alone justify MQTT 5 for most new server-side work; broker and client support is now widespread.

Security

⚠️ Warning: MQTT has no security of its own. Plain MQTT on port 1883 sends credentials and payloads in cleartext. Public broker scans routinely find thousands of unauthenticated brokers exposing industrial and home systems.

The baseline:

Topic ACLs are the control that matters most and is most often skipped. Without them, any authenticated device can subscribe to # and read the entire fleet's traffic, or publish commands to any other device. Scope every device to its own subtree.

For constrained devices, TLS costs flash (a stack is tens of KB), RAM (handshake buffers), and handshake time. Session resumption and pre-shared keys reduce it, and hardware crypto acceleration on parts like the ESP32 makes it entirely practical. Cost is not a reason to ship without it. See IoT Security.

Best Practices

Design the topic hierarchy before deploying

Topics are effectively an API, and they're the hardest thing to change once devices are in the field. Put identity high in the tree so ACLs are prefix rules, keep levels meaningful, and separate telemetry from commands from status so subscribers can filter precisely.

Subscribe inside the connect callback

Subscribing once at startup means a device that reconnects silently stops receiving commands — a failure that's invisible until someone notices the device ignoring them.

Use QoS 1 and make consumers idempotent

QoS 1 covers nearly every case at a fraction of QoS 2's cost. Include a message ID in the payload, track recently seen IDs, and skip duplicates. This is the same discipline any at-least-once system needs.

Retain state, not events

Retained messages are a "current value" cache. A retained event message is delivered to every future subscriber as though it just happened.

Keep payloads small and versioned

JSON is convenient for development and verbose on the wire. For high-frequency or bandwidth-constrained links, use CBOR, MessagePack, or Protobuf. Whatever the format, include a schema version field from the first release — a fleet will run several firmware versions simultaneously, and the server must handle all of them.

Tune keep-alive to the connectivity and power budget

Short keep-alives detect failures quickly and wake the radio often. On a battery device, each wake-up is measurable. Match the interval to how quickly you actually need to detect a dead device.

Implement reconnection with exponential backoff and jitter

Devices reconnect in herds after a broker restart or a network blip. Backoff with jitter prevents a thundering herd that keeps the broker from recovering. Most client libraries have this built in — verify it's enabled rather than assuming.

Bound persistent session growth

An offline device with a persistent session accumulates queued messages. Set message expiry (MQTT 5) and a session expiry interval so a device that's been off for a month doesn't reconnect to ten thousand stale messages.

Common Mistakes

Publishing without TLS

Subscribing only once at startup

Using a wildcard when publishing

Sharing one credential across the fleet

Retaining events

Reconnecting without backoff

FAQ

MQTT or HTTP for IoT?

MQTT when devices publish frequently, when the server must push to devices, when bandwidth or power is constrained, or when connectivity is intermittent — a persistent connection avoids per-request handshakes and lets the server reach devices behind NAT. HTTP when interactions are infrequent, request/response-shaped, or need to traverse restrictive corporate proxies that only allow port 443. Many fleets use both: MQTT for telemetry and commands, HTTPS for firmware downloads and provisioning.

MQTT or CoAP?

CoAP is UDP-based, REST-shaped, and even lighter, which suits very constrained devices and multicast discovery. MQTT has far broader tooling, cloud support, and library maturity, and its persistent connection handles NAT better. Choose CoAP for extremely constrained or mesh networks; MQTT for essentially everything else in practice.

Which broker should I use?

Mosquitto for development, small deployments, and edge gateways — tiny, simple, ubiquitous. EMQX, HiveMQ, or VerneMQ for clustered production deployments with tens of thousands of devices. AWS IoT Core or Azure IoT Hub when you want device identity, provisioning, and cloud integration managed for you. NanoMQ for resource-constrained edge devices.

How many devices can one broker handle?

Mosquitto on modest hardware handles tens of thousands of connections; clustered brokers handle millions. The binding constraint is usually message rate and fan-out rather than connection count — one message to a topic with 10,000 subscribers is 10,000 deliveries. Model your actual fan-out before sizing.

Can MQTT work over WebSockets?

Yes, and it's the standard way to reach browsers. Brokers expose MQTT over WebSocket (commonly port 8083, or 8084 with TLS), and libraries like MQTT.js connect directly from a web page. This is how live dashboards get real-time updates without polling. See WebSockets.

How do I handle firmware updates over MQTT?

Publish availability and metadata over MQTT — version, size, hash, URL — and download the image over HTTPS. MQTT is designed for small messages, and pushing a megabyte through it works poorly and hurts every other client on the broker. The command channel is MQTT; the bulk transfer is not. See OTA Firmware Updates.

Related Topics

References