MQTT Fundamentals

MQTT Tutorial: The Complete Beginner's Guide to MQTT

A hands-on MQTT tutorial for beginners: learn brokers, topics, QoS, retained messages and Last Will, then publish and subscribe for real on the free broker.connectmq.com with the CLI, Python and JavaScript.

Illustration for an MQTT tutorial showing a laptop, a sensor and a phone exchanging messages through a central broker
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

This MQTT tutorial takes you from zero to a working publish/subscribe setup in one sitting. You will learn the handful of concepts that make MQTT tick, then run every step for real against the free public broker broker.connectmq.com using the command line, Python and a browser. By the end you will know how to design topics, pick a QoS level, use retained messages and Last Will, and when to move to a private broker.

What Is MQTT and Why Learn It?

MQTT is a lightweight publish/subscribe messaging protocol that runs on top of TCP. It was designed for unreliable networks and small devices, which is why it is now the default language of IoT: sensors, gateways, PLC adapters, mobile apps and backend services all speak it. The current versions are MQTT 3.1.1 and MQTT 5.0, both standardized by OASIS (the full text is published on mqtt.org).

Three properties explain its popularity:

  • Tiny overhead. The fixed header of a packet can be as small as two bytes, so MQTT works over cellular links, satellite and congested Wi-Fi.
  • Decoupling. Publishers never talk to subscribers directly. They do not need to know each other's IP addresses, and they do not even need to be online at the same time.
  • Push, not poll. Once a client subscribes, data arrives the moment it is published. There is no request loop burning battery and bandwidth.

If you want a deeper look at the packet flow before getting your hands dirty, read how MQTT works under the hood. Otherwise, keep going: the concepts below are enough to build real systems.

The Core Concepts in Five Minutes

Broker

The broker is the server in the middle. Every client opens one long-lived TCP connection to it, and the broker routes each published message to every client whose subscription matches. It also tracks sessions, keeps retained messages and fires Last Will messages. For a full explanation of what an MQTT broker does, including how it differs from a message queue, see our dedicated guide.

Clients

Anything that connects to the broker is a client: an ESP32, a Python script, a Node-RED flow, a dashboard in a browser. A client can publish, subscribe or both. Each connection carries a client ID, which the broker uses to identify the session. Two clients with the same ID will kick each other off, so always make it unique.

Topics

A topic is a UTF-8 string with levels separated by slashes, for example factory1/line2/press3/temperature. Topics are not created in advance; they exist the moment someone publishes to them. Good topic design is the single most important decision in an MQTT project, so we cover it in detail in MQTT topic naming conventions.

Publish and Subscribe

A publisher sends a message to a topic. A subscriber registers interest in a topic filter. The broker matches the two. This is the publish/subscribe model, and it is what lets you add a new dashboard or a new database writer without touching a single device.

Wildcards

Subscribers can use two wildcards:

  • + matches exactly one level: factory1/+/press3/temperature
  • # matches all remaining levels and must be last: factory1/#

Publishers can never use wildcards. The full matching rules, including edge cases around $SYS topics, are in our guide to MQTT wildcards.

Step 1: Pick a Broker for This MQTT Tutorial

You need a broker to talk to. Installing one locally works, but it adds firewall, port and service management to your first hour. The quickest route is a public test broker. In this tutorial we use broker.connectmq.com, the free public MQTT broker run by ConnectMQ. It accepts anonymous connections on all standard ports:

Transport Port URL Use it for
MQTT over TCP 1883 mqtt://broker.connectmq.com:1883 CLI tools, quick tests
MQTT over TLS 8883 mqtts://broker.connectmq.com:8883 Encrypted device and server connections
WebSocket 8083 ws://broker.connectmq.com:8083/mqtt Local web experiments
Secure WebSocket 8084 wss://broker.connectmq.com:8084/mqtt Browsers and HTTPS pages

Rules worth knowing before you start:

  1. Use a non-empty, unique client ID on every connection.
  2. Prefix all topics with something nobody else will use, such as connectmq/demo/<random-id>/. Pick your random ID now, for example a7k2q9.
  3. Everything in the public area is readable by everyone. Never publish passwords, personal data or anything sensitive.
  4. The public broker has limits: 64 KB per message, 10 messages per second per client, 10 subscriptions per client, up to 8 topic levels and sessions that expire after at most 30 minutes. They are generous for learning and intentionally too small for production.

If you want to understand why those four ports exist and which one to choose behind a corporate firewall, read MQTT ports explained.

Step 2: Your First Publish and Subscribe From the Command Line

The Mosquitto command line clients are the simplest way to see MQTT working. Install them with your package manager:

# Debian / Ubuntu / Raspberry Pi OS
sudo apt install mosquitto-clients

# macOS (Homebrew)
brew install mosquitto

On Windows, the installer from the Mosquitto project includes mosquitto_pub.exe and mosquitto_sub.exe.

Open a terminal and subscribe to everything under your prefix. The -v flag prints the topic next to each payload:

mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-a7k2q9 \
  -t "connectmq/demo/a7k2q9/#" -v

Open a second terminal and publish:

mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-a7k2q9 \
  -t "connectmq/demo/a7k2q9/room1/temperature" -m "21.5"

The first terminal prints connectmq/demo/a7k2q9/room1/temperature 21.5 immediately. That is the entire MQTT loop: connect, subscribe, publish, receive.

Now try the encrypted port. Because broker.connectmq.com uses a Let's Encrypt certificate, your system CA bundle can verify it:

mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -i pub-tls-a7k2q9 -t "connectmq/demo/a7k2q9/room1/humidity" -m "48"

On macOS or Windows, point --cafile at a CA bundle file instead of using --capath.

Step 3: QoS, Retained Messages and Last Will

These three features are what separate MQTT from a plain socket. Try each one now.

Quality of Service

Every publish and subscription carries a QoS level:

QoS Guarantee Packet flow Typical use
0 At most once PUBLISH High-frequency telemetry where a missed sample does not matter
1 At least once PUBLISH, PUBACK Most IoT data and commands; duplicates are possible
2 Exactly once PUBLISH, PUBREC, PUBREL, PUBCOMP Billing events, counters, anything that must not duplicate

The effective QoS for a subscriber is the lower of the publish QoS and the subscription QoS. Publish with -q 1 and subscribe with -q 1 to see acknowledged delivery. Our guide on choosing between QoS 0, 1 and 2 walks through the trade-offs with real failure scenarios.

Retained Messages

A retained message is stored by the broker as the "last known value" of a topic and delivered instantly to any new subscriber. Publish one:

mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-a7k2q9 -r \
  -t "connectmq/demo/a7k2q9/room1/setpoint" -m "22"

Now start a fresh subscriber. It receives 22 immediately, even though nothing was published after it connected. To clear a retained message, publish an empty payload with the retain flag: -r -n. More patterns, including when retained messages cause confusion, are in MQTT retained messages.

Last Will and Testament

When a client connects, it can register a "will" message. If the client disappears without a clean disconnect (power loss, network drop, crash), the broker publishes the will on its behalf. Combine it with a retained "online" message and you get reliable device presence:

mosquitto_sub -h broker.connectmq.com -p 1883 -i dev-a7k2q9 \
  -t "connectmq/demo/a7k2q9/room1/cmd" \
  --will-topic "connectmq/demo/a7k2q9/room1/status" \
  --will-payload "offline" --will-retain --will-qos 1 -k 10

Kill that process with your terminal's close button (not Ctrl+C, which may still send a clean disconnect on some builds) and watch offline arrive on your wildcard subscriber after roughly one and a half keep-alive intervals.

Step 4: The Same MQTT Tutorial in Python

The command line is great for exploring; real projects use a library. Install paho-mqtt 2.x:

pip install "paho-mqtt>=2.0"

This script connects with MQTT 5, subscribes to its own command topic, publishes a reading every five seconds and announces its presence with a retained status plus a Last Will:

import json
import random
import time
import uuid

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/a7k2q9"
CLIENT_ID = f"tutorial-py-{uuid.uuid4().hex[:8]}"


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connect failed: {reason_code}")
        return
    print("Connected as", CLIENT_ID)
    client.publish(f"{PREFIX}/room1/status", "online", qos=1, retain=True)
    client.subscribe(f"{PREFIX}/room1/cmd", qos=1)


def on_message(client, userdata, msg):
    print(f"Command on {msg.topic}: {msg.payload.decode()}")


def on_disconnect(client, userdata, flags, reason_code, properties):
    print(f"Disconnected: {reason_code}")


client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id=CLIENT_ID,
    protocol=mqtt.MQTTv5,
)
client.will_set(f"{PREFIX}/room1/status", "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect

client.tls_set()  # verify the Let's Encrypt certificate with system CAs
client.connect(BROKER, 8883, keepalive=30)
client.loop_start()

try:
    while True:
        reading = {"temperature": round(random.uniform(19, 24), 2), "ts": int(time.time())}
        client.publish(f"{PREFIX}/room1/telemetry", json.dumps(reading), qos=1)
        time.sleep(5)
except KeyboardInterrupt:
    client.publish(f"{PREFIX}/room1/status", "offline", qos=1, retain=True).wait_for_publish()
    client.disconnect()
    client.loop_stop()

Leave your mosquitto_sub terminal running and you will see telemetry flowing. Send a command back with mosquitto_pub -t "connectmq/demo/a7k2q9/room1/cmd" -m "reboot" and the script prints it. Notice the paho 2.x details: CallbackAPIVersion.VERSION2 is required, and the callbacks receive reason_code and properties arguments. Mixing in old 1.x examples is the most common source of errors here.

Step 5: MQTT in the Browser With WebSocket

Browsers cannot open raw TCP sockets, so they speak MQTT over WebSocket. Save this as index.html and open it in any modern browser:

// Load MQTT.js from a CDN in your HTML first:
// <script src="https://unpkg.com/mqtt@5.10.1/dist/mqtt.min.js"></script>

const clientId = "tutorial-web-" + Math.random().toString(16).slice(2, 10);
const prefix = "connectmq/demo/a7k2q9";

const client = mqtt.connect("wss://broker.connectmq.com:8084/mqtt", {
  clientId,
  protocolVersion: 5,
  clean: true,
  reconnectPeriod: 2000,
});

client.on("connect", () => {
  console.log("Connected", clientId);
  client.subscribe(`${prefix}/#`, { qos: 1 });
  client.publish(`${prefix}/room1/cmd`, "hello from the browser", { qos: 1 });
});

client.on("message", (topic, payload) => {
  console.log(topic, payload.toString());
});

client.on("error", (err) => console.error("MQTT error", err.message));

Open the developer console and you will see the Python telemetry, the retained setpoint and status, and your own command echoed back. Port 8084 with the /mqtt path is the one to use from HTTPS pages, because browsers block insecure WebSocket connections from secure origins. Our MQTT over WebSocket guide covers proxies, frameworks and authentication in the browser.

Designing Topics and Payloads That Scale

Once messages flow, design decides whether the system stays maintainable. A few rules that hold up in practice:

  1. Go from general to specific. site/area/line/device/measurement lets you subscribe to a whole line with one wildcard.
  2. Do not start with a slash. /factory/line1 creates an empty first level that complicates every filter.
  3. Keep identifiers stable. Use device IDs, not human names that will change.
  4. Separate data from commands. .../telemetry, .../status and .../cmd should be distinct topics so permissions and QoS can differ.
  5. Pick one payload format. JSON is readable and easy to debug; binary formats such as Protobuf save bandwidth on constrained links. Whatever you choose, include a timestamp and units.
  6. Avoid one topic per reading. Publishing ten values to ten topics every second multiplies packet overhead; a small JSON object is usually better.

For industrial systems, a hierarchy aligned with ISA-95 (enterprise, site, area, line, cell) has become a common convention, and it is the basis of the Unified Namespace pattern explained in Sparkplug B and the Unified Namespace.

Common Beginner Mistakes and How to Fix Them

Symptom Likely cause Fix
Two clients keep disconnecting each other Same client ID used twice Generate a unique ID per process; see MQTT client ID rules
Subscriber receives nothing Topic typo or case mismatch Topics are case-sensitive; subscribe to prefix/# to see what actually arrives
Messages arrive twice QoS 1 redelivery after reconnect Make consumers idempotent or use QoS 2 where duplicates matter
Connection refused on port 8883 No TLS configured in the client Enable TLS (tls_set(), mqtts://) or use port 1883 for plain tests
Browser cannot connect Using ws:// from an HTTPS page, or missing /mqtt path Use wss://broker.connectmq.com:8084/mqtt
Old retained value keeps appearing Retained message still stored Publish an empty retained payload to clear it
Python callback signature errors 1.x example code with paho 2.x Pass CallbackAPIVersion.VERSION2 and use the v2 signatures

From Tutorial to Production

A public broker is ideal for learning and nothing more. Before real devices go live, you need private credentials, TLS everywhere, isolation from other users and limits sized for your fleet. You can run your own broker, but you then own patching, certificates, monitoring and capacity planning. A managed MQTT broker moves that work to the provider.

With ConnectMQ the move is mostly a configuration change. Sign up, choose a plan, create a broker and the portal gives you a generated username, password and client ID. The same code from this tutorial works once you add credentials:

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id="YOUR_CLIENT_ID",
    protocol=mqtt.MQTTv5,
)
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()
client.connect("broker.connectmq.com", 8883, keepalive=30)

The portal also includes an in-browser MQTT client and a live topic explorer, so you can repeat every step of this tutorial on your private broker without installing anything. To compare deployment options, from a shared serverless tier to a dedicated cluster or your own cloud account, read our guide to choosing a cloud MQTT broker. Then work through MQTT security best practices before connecting the first device in the field.

Your Learning Path After This Tutorial

Next topic Why it matters
MQTT 5 features Message expiry, shared subscriptions, reason codes and user properties
ESP32 MQTT tutorial Put MQTT on real hardware
MQTT persistent sessions Receive messages that arrive while a client is offline
MQTT keep alive interval Tune how fast the broker notices a dead connection
MQTT vs HTTP Know when MQTT is the right tool and when it is not

Get Started with ConnectMQ

You have just used every core MQTT feature on a public broker. When you are ready for private topics and real devices, create a free broker at app.connectmq.com. The Serverless free plan needs no credit card, supports MQTT 3.1.1 and 5.0 on TCP, TLS and WebSocket, and gives you up to 25 connections to build your first project. Your ConnectMQ credentials drop straight into the code above.

Frequently Asked Questions

Is MQTT hard to learn?

No. The core model is just clients, a broker, topics and publish/subscribe, and you can send your first message in under five minutes with the CLI. The depth comes later with sessions, QoS trade-offs and topic design.

Do I need to install a broker to follow this MQTT tutorial?

No. Every step runs against broker.connectmq.com, which accepts anonymous connections. You only need the Mosquitto client tools, Python or a browser.

Should I learn MQTT 3.1.1 or MQTT 5?

Learn the concepts once; they are the same in both. Use MQTT 5 for new projects because it adds reason codes, message expiry, shared subscriptions and user properties, while staying compatible with brokers that support both versions.

Is the public broker safe for real data?

No. Anything in the public area can be read by anyone, so use it only for tests and demos. Real data belongs on a private broker with credentials and TLS.

What should I build after this tutorial?

Connect a real sensor with an ESP32 or Raspberry Pi, store readings in a database with a small subscriber script, and build a dashboard over WebSocket. Each of those steps reuses exactly what you learned here.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.