Skip to content

MQTT

!! abstract "Summary"

- **MQTT (Message Queuing Telemetry Transport)** is a lightweight **publish/subscribe** messaging protocol built for constrained devices and unreliable networks — exactly the conditions IoT devices usually live in.
- Unlike HTTP's request/response model, MQTT clients never talk to each other directly — every message passes through a central **broker**, which routes messages based on **topics** rather than destination addresses.
- It's an OASIS standard, currently at version 5.0 (with 3.1.1 still widely deployed), and runs over TCP — typically port `1883` unencrypted, or `8883` over TLS.

!!! tip "Think of MQTT as:"

    "A radio station, not a phone call — publishers broadcast onto a named channel (topic), and anyone tuned in (subscribed) hears it, with no idea who else is publishing or listening."
If you are confused...

MQTT is an application-layer protocol that can run over any IP-capable network, cellular included. If you've read the 5G pages, one way to place MQTT: it's the layer that would sit above everything covered there, riding inside a PDU session's user-plane traffic once a UE actually has connectivity.

Overall Ideas

A Bit of History

MQTT wasn't designed for the modern IoT boom — it was invented in 1999 by Andy Stanford-Clark (IBM) and Arlen Nipper (Arcom, now Cirrus Link) to solve a much narrower problem: monitoring oil pipeline sensors over satellite links that were slow, expensive per byte, and frequently dropped.1 Those constraints shaped five design goals that still define MQTT today — simple to implement, support a range of QoS levels, lightweight and bandwidth-efficient, agnostic to the data it carries, and able to maintain continuous session awareness across an unreliable link.1

Year Milestone
1999 Invented at IBM, for oil pipeline SCADA monitoring over satellite
2010 Released royalty-free (v3.1)
2011 Client implementations contributed to the Eclipse Paho project
2014 v3.1.1 becomes an OASIS Standard
2016 Also published as ISO/IEC 20922:2016
2019 v5.0 becomes an OASIS Standard

Table 1. MQTT Milestones

Why It Caught On for IoT

  • Minimal overhead — the fixed header can be as small as 2 bytes, so framing cost stays trivial even for a tiny payload.
  • Bandwidth efficient — pub/sub means the network carries a message once per interested party (via the broker), not once per potential recipient.
  • Flexible reliability — QoS 0–2 lets each individual message pick its own tradeoff, instead of forcing an entire application into one reliability model.
  • Bidirectional & connection-aware — the broker can push data to a client at any time, and Keep Alive plus LWT mean a dropped connection gets detected and reported quickly, not silently.
  • Scales to many-to-many — thousands of devices can publish to, or subscribe from, the same broker without any of them knowing about each other.

Built on TCP/IP

MQTT doesn't reinvent reliable delivery — it's an application-layer protocol that assumes an already-established TCP connection underneath, and leans on TCP for what TCP already does well: guaranteed delivery, in-order bytes, and retransmission of lost packets at the transport layer.2 That's a deliberate choice, not an oversight — it's exactly what lets MQTT's own framing stay so minimal. The tradeoff is that MQTT needs a working, connection-oriented network underneath it, which is part of why MQTT-SN exists — for links too constrained to justify a full TCP stack.

Publish/Subscribe Architecture

MQTT Publish/Subscribe Architecture

Figure 1. MQTT Publish/Subscribe Architecture3

Every MQTT connection is between one client and the broker — clients never connect directly to each other.1 This decoupling is the whole point:

  • A publisher doesn't need to know who (or how many) subscribers exist.
  • A subscriber doesn't need to know who's publishing.
  • Publishers and subscribers don't even need to be connected at the same time — that's what retained messages and sessions exist to handle.

Client is a loose term

A "client" can be a publisher, a subscriber, or both at once — the same device very commonly does both (e.g., a smart thermostat publishing temperature readings while subscribing to a topic for setpoint commands).

Topics & Wildcards

Topics are hierarchical, UTF-8 strings with levels separated by / — for example, home/livingroom/temperature. There's no need to "create" a topic in advance; publishing or subscribing to one simply makes it exist.

Subscribers can use two wildcard characters when subscribing (never when publishing):2

Wildcard Matches Example
+ Exactly one topic level home/+/temperature matches home/livingroom/temperature and home/kitchen/temperature, but not home/livingroom/sensor1/temperature
# That level and all levels below it — must be the last character in the filter home/# matches everything under home/, at any depth

Table 2. MQTT Wildcards

Topic Name vs. Topic Filter

A Topic Name (used when publishing) must be fully specified and can't contain wildcards.2 A Topic Filter (used when subscribing) is the only place wildcards are allowed. Mixing these up is one of the most common beginner MQTT mistakes.

Quality of Service (QoS)

MQTT defines three delivery guarantees, chosen per-message by the publisher and capped per-subscription by the subscriber:2

QoS Guarantee Handshake
0 At most once — "fire and forget" PUBLISH only, no acknowledgment
1 At least once — may be delivered more than once PUBLISH → PUBACK
2 Exactly once PUBLISH → PUBREC → PUBREL → PUBCOMP (a four-packet handshake)

Table 3. MQTT QoS

Why does QoS 2 need four packets instead of two?

QoS 1's simple ACK can still produce duplicates — if the PUBACK itself gets lost, the sender retransmits a message the receiver already got. QoS 2's extra PUBREC/PUBREL round trip exists specifically to let both sides confirm the exact message was received and processed exactly once, at the cost of more overhead — which is exactly why QoS 2 is recommended sparingly, and QoS 0 or 1 covers most IoT telemetry just fine.

Retained Messages & Last Will and Testament

Two features specifically address MQTT's async nature — that publishers and subscribers aren't guaranteed to be online at the same time:

  • Retained messages — when a publisher sets the retain flag, the broker stores that message (only the single most recent one per topic) and immediately delivers it to any client that subscribes afterward, rather than making them wait for the next publish.4
  • Last Will and Testament (LWT) — a client can register a message, at connect time, that the broker will publish on its behalf if that client disconnects ungracefully (network drop, timeout) rather than cleanly (a proper DISCONNECT packet).5
Combining both

A common pattern: a device publishes a retained "online" message to devices/sensor001/status on connect, and registers "offline" as its LWT on the same topic. Any client subscribing to that topic — at any time — immediately learns the device's current status, whether it's currently connected or not.

Sessions & Keep Alive

  • Clean Session (MQTT 3.1.1) / Session Expiry Interval (MQTT 5.0) — controls whether the broker discards a client's subscriptions and undelivered QoS ½ messages when it disconnects, or preserves them for when it reconnects.6
  • Keep Alive — the client tells the broker, at connect time, the longest gap it will allow between packets. If nothing else is sent, the client pings the broker with PINGREQ (answered with PINGRESP) to prove the connection is still alive. If the broker hears nothing for 1.5× the Keep Alive interval, it closes the connection and fires the client's LWT if one was set.6

The 18-hour ceiling

Keep Alive is encoded as a 16-bit number of seconds, which caps the maximum interval at exactly 18 hours, 12 minutes, 15 seconds — a nice example of a protocol limit falling straight out of its wire format.6

MQTT Control Packets

MQTT Packet Size

Figure 2. MQTT Packet Size7

Every MQTT interaction is built from one of 15 Control Packet types, each identified by a 4-bit type value in the packet's fixed header:2

Type Packet Purpose
1 CONNECT Client → broker, opens a session
2 CONNACK Broker → client, acknowledges CONNECT
3 PUBLISH Carries an application message
4 PUBACK QoS 1 acknowledgment
5 PUBREC QoS 2 handshake, step 2
6 PUBREL QoS 2 handshake, step 3
7 PUBCOMP QoS 2 handshake, step 4
8 SUBSCRIBE Client → broker, requests one or more topic filters
9 SUBACK Broker → client, acknowledges SUBSCRIBE
10 UNSUBSCRIBE Client → broker, removes a subscription
11 UNSUBACK Broker → client, acknowledges UNSUBSCRIBE
12 PINGREQ Client → broker, Keep Alive ping
13 PINGRESP Broker → client, Keep Alive response
14 DISCONNECT Graceful connection close (suppresses the LWT)
15 AUTH Extended authentication exchange — MQTT 5.0 only

Table 4. MQTT Control Packet Types and their type values

Type 15 wasn't always AUTH

In MQTT 3.1.1, value 15 was simply reserved and unused. MQTT 5.0 is what gave it a job — AUTH, for extended authentication exchanges (e.g., challenge/response mechanisms like SCRAM) that don't fit into a plain username/password CONNECT.

Overall Flow

MQTT Protocol Flow Overview

Figure 3. MQTT Protocol Flow Overview8

Putting everything above together, a typical MQTT session looks like this:8

  1. Connect — client sends CONNECT, broker replies CONNACK.
  2. Subscribe — client sends SUBSCRIBE with one or more topic filters, broker replies SUBACK confirming the granted QoS for each.
  3. Exchange messages — as matching messages are published (by this client or any other), the broker forwards them as PUBLISH packets, running whatever QoS handshake that message's QoS level requires.
  4. Stay alive — during any quiet period, PINGREQ/PINGRESP keep the connection alive.
  5. Disconnect — client sends DISCONNECT to close cleanly, suppressing its LWT.

This is why nothing on this page needed a separate diagram for "how you connect" versus "how you publish" — it's all one continuous session, built entirely from the packets and concepts already covered above.

Example

The fastest way to actually feel how MQTT works is two terminals and a local broker — no cloud account, no IoT device required.

Install Mosquitto — an open-source MQTT broker that also ships the mosquitto_pub/mosquitto_sub CLI clients:

sudo apt install -y mosquitto mosquitto-clients
sudo systemctl status mosquitto

Mosquitto starts automatically on install, listening on localhost:1883.

Terminal 1 — subscribe and leave it running:

mosquitto_sub -h localhost -t "kuoquo/test" -v

Terminal 2 — publish a message:

mosquitto_pub -h localhost -t "kuoquo/test" -m "Hello from Terminal 2"

You should see the message appear in Terminal 1 immediately — that round trip is the entire flow described above: CONNECT/CONNACK for both clients, SUBSCRIBE/SUBACK for Terminal 1, and a PUBLISH the broker forwarded the moment it arrived.

Try building on this
  • Retained messages: mosquitto_pub -h localhost -t "kuoquo/test" -m "I persist" -r, then open a third terminal and subscribe fresh — the message arrives immediately, without Terminal 2 publishing again.
  • Wildcards: subscribe with mosquitto_sub -h localhost -t "kuoquo/#" -v, then publish to kuoquo/test/sub — it still arrives, even though nothing subscribed to that exact topic.
  • QoS: add -q 2 to the publish command to force the four-packet QoS 2 handshake — delivery looks identical from the terminal, but a packet capture (e.g., Wireshark on lo, filtered on mqtt) would show PUBREC/PUBREL/PUBCOMP happening underneath.

MQTT 5.0: What's New

MQTT 5.0 (2019) added several features on top of 3.1.1,9 without changing the core pub/sub model:2

  • Reason Codes — nearly every acknowledgment packet can now carry a specific reason for success or failure, instead of MQTT 3.1.1's mostly binary outcomes.
  • User Properties — arbitrary key/value metadata attachable to most packets.
  • Session Expiry Interval & Message Expiry Interval — finer-grained, explicit control (in seconds) over how long a session or an individual message survives, including how long a retained message sticks around.10
  • Topic Alias — lets a client substitute a short integer for a long topic name after the first use, shrinking repeated PUBLISH overhead.
  • Shared Subscriptions — multiple clients can subscribe to the same topic filter as a group, with the broker load-balancing messages across them rather than delivering to every member.
  • Request/Response pattern support — standardized Response Topic and Correlation Data properties, making RPC-style interactions over MQTT's pub/sub model much cleaner to implement.

MQTT-SN: For Constrained Devices

MQTT-SN (MQTT for Sensor Networks) is a related but separate specification — not simply "MQTT over UDP" — designed for devices too constrained even for MQTT's already-light TCP footprint, such as battery-powered sensors on low-power radio links. It replaces long text-based topic names with short pre-registered topic IDs, and typically talks to the rest of an MQTT network through a gateway that translates between MQTT-SN and standard MQTT.

Best Practices

Guidance drawn from how the concepts above actually get used in production:

  • Design topics deliberately — use a consistent hierarchy (e.g., <location>/<device-type>/<device-id>/<measurement>), and treat topic names as a stable API, not an afterthought. Changing them later breaks every subscriber depending on the old names.
  • Default to QoS 0, not QoS 2 — QoS 2's four-packet handshake has real overhead. Reach for QoS 1 or 2 only when a specific message actually needs the stronger guarantee — a command is a better candidate than a repeating sensor reading, where the next update arriving soon anyway makes an occasional drop harmless.
  • Set an LWT on every long-lived client — it's the only reliable way subscribers learn a device went offline unexpectedly, rather than assuming silence just means "nothing to report."
  • Use retained messages for state, not one-off events — a device's current status is a good fit; a discrete event like a button press generally isn't, since a new subscriber would receive that "stale" retained event as if it just happened.
  • Use TLS (8883) outside a trusted local network — plain MQTT (1883) sends everything, including usernames and passwords, in cleartext.
  • Keep payloads small and structured — JSON is common and interoperable, but for genuinely constrained devices, a compact binary format (CBOR, Protobuf) can meaningfully cut bandwidth and parsing cost.
  • Set a sensible Keep Alive, not the maximum — too long delays detecting a dead connection (and firing the LWT); too short wastes bandwidth on pings. Tens of seconds to a few minutes covers most IoT use cases.

Useful Resources


  1. HiveMQ. (n.d.). Introducing the MQTT protocol — MQTT Essentials: Part 1. https://www.hivemq.com/blog/mqtt-essentials-part-1-introducing-mqtt/ ↩↩↩

  2. OASIS. (2019). MQTT Version 5.0. OASIS Standard. https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html ↩↩↩↩↩↩

  3. MQTT.org. (n.d.). MQTT: The standard for IoT messaging. https://mqtt.org/ ↩

  4. HiveMQ. (n.d.). What are retained messages in MQTT? — MQTT Essentials: Part 8. https://www.hivemq.com/blog/mqtt-essentials-part-8-retained-messages/ ↩

  5. HiveMQ. (n.d.). What is MQTT Last Will and Testament (LWT)? — MQTT Essentials: Part 9. https://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament/ ↩

  6. HiveMQ. (n.d.). What is MQTT Keep Alive and client take-over? — MQTT Essentials: Part 10. https://www.hivemq.com/blog/mqtt-essentials-part-10-alive-client-take-over/ ↩↩↩

  7. HiveMQ. (n.d.). MQTT packets: A comprehensive guide. https://www.hivemq.com/blog/mqtt-packets-comprehensive-guide/ ↩

  8. Ryu, J. (n.d.). IoT – MQTT. ShareTechnote. https://www.sharetechnote.com/html/IoT/App_Protocol_MQTT.html#Protocol_Sequence ↩↩

  9. OASIS. (2014). MQTT Version 3.1.1. OASIS Standard. https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html ↩

  10. HiveMQ. (n.d.). MQTT session expiry and message expiry intervals — MQTT 5 Essentials: Part 4. https://www.hivemq.com/blog/mqtt5-essentials-part4-session-and-message-expiry/ ↩