MQTT v5 isn’t just a minor update; it’s a fundamental re-imagining of the protocol that makes it far more robust and developer-friendly than its predecessor.

Let’s see it in action. Imagine a simple scenario: a sensor publishing temperature data to a broker, and a client subscribing to receive it.

# Publisher (client sending data)
import paho.mqtt.client as mqtt
import time

def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("Connected successfully with result code " + str(rc))
    else:
        print("Connection failed with result code " + str(rc))

client = mqtt.Client("publisher_client_v5", protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.connect("localhost", 1883, keepalive=60)

# Publish a message with a Reason Code and User Property
topic = "sensor/temperature"
payload = "25.5"
properties = mqtt.Properties(mqtt.PacketType.PUBLISH)
properties.UserProperty("location", "living_room")
properties.ReasonString("Temperature reading")

for i in range(5):
    message_info = client.publish(topic, payload, qos=1, properties=properties)
    print(f"Published message {i+1} with mid {message_info.mid} to topic {topic}")
    time.sleep(2)

client.loop_stop()
client.disconnect()
# Subscriber (client receiving data)
import paho.mqtt.client as mqtt
import time

def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("Connected successfully with result code " + str(rc))
    else:
        print("Connection failed with result code " + str(rc))

def on_message(client, userdata, msg, properties=None):
    print(f"Received message on topic {msg.topic}: {msg.payload.decode()}")
    if properties:
        print("  Properties:")
        for key, value in properties.UserProperties():
            print(f"    {key}: {value}")
        if properties.ReasonString():
            print(f"    Reason String: {properties.ReasonString()}")


client = mqtt.Client("subscriber_client_v5", protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, keepalive=60)

topic = "sensor/temperature"
client.subscribe(topic, qos=1)

client.loop_forever()

The core problem MQTT v5 tackles is the ambiguity and lack of extensibility in MQTT 3.1.1, especially around error handling and feature additions. In 3.1.1, a connection failure might result in a generic rc=5 (Not Authorized), leaving you guessing if it’s a password issue, a certificate problem, or something else entirely. Similarly, adding new features required breaking changes or reserved fields that were never utilized.

MQTT v5 introduces several key features that address this:

  • Reason Codes and Reason Strings: Instead of generic return codes like rc=5, v5 uses specific reason codes (e.g., 0x05 for Not Authorized, 0x82 for Topic Name Invalid, 0x97 for Quota Exceeded). Crucially, it also adds optional ReasonString properties, allowing the broker to provide human-readable explanations for failures. This dramatically improves debugging and client-side error handling. When a client receives a CONNACK packet with rc=0x05, it knows precisely why it wasn’t authorized.

  • Properties: This is perhaps the most significant change. MQTT v5 replaces the fixed header fields and reserved bytes of 3.1.1 with a flexible Properties system. These properties can be attached to almost every MQTT packet type (CONNECT, PUBLISH, SUBSCRIBE, etc.) and can carry arbitrary key-value pairs. This allows for extensibility without breaking compatibility. Examples include SessionExpiryInterval, AuthenticationMethod, UserProperty, and TopicAlias. The UserProperty in the example above is a perfect illustration: you can attach arbitrary metadata to messages without the broker needing to understand its meaning, simply passing it along.

  • Enhanced SUBSCRIBE/UNSUBSCRIBE: The SUBSCRIBE and UNSUBSCRIBE packets now return an SUBACK or UNSUBACK packet that includes a list of reason codes, one for each topic filter requested. This means you can subscribe to multiple topics and know individually which ones succeeded and which failed (e.g., due to authorization or invalid topic names), rather than a single success/failure for the entire operation.

  • Session Expiry: Clients can now specify a SessionExpiryInterval in the CONNECT packet. If this is set to a non-zero value, the broker will retain the session state (subscriptions, QoS 1/2 message acknowledgements) for that client for the specified duration, even if the client disconnects. This is invaluable for mobile clients or intermittent network connections, as they can reconnect and resume their state seamlessly.

  • Shared Subscriptions: While not strictly a v5 protocol feature, the concept of shared subscriptions is often implemented alongside v5. This allows multiple clients to subscribe to the same topic filter with a shared group ID. The broker then distributes messages published to that topic among the clients in the group, enabling load balancing. For example, topic/data#group1 would mean all clients subscribing to this with group1 share the load.

  • Flow Control and Congestion Control: MQTT v5 introduces TopicAlias and MaximumPacketSize properties. TopicAlias allows a client to send a short integer instead of the full topic name after the first publish, saving bandwidth. MaximumPacketSize allows the client to advertise the largest packet it’s willing to receive, helping prevent denial-of-service attacks and managing resource usage.

The most surprising true thing about MQTT v5 is how effectively it retrofits extensibility into a protocol that was originally designed with a fixed feature set. The Properties mechanism isn’t just a way to add new features; it’s a fundamental shift that allows the protocol to evolve organically, with brokers and clients negotiating capabilities and exchanging contextual information that was previously impossible. This makes it far more adaptable to future use cases and integrations without requiring a full protocol version bump.

The next evolution you’ll likely encounter is exploring how these v5 features integrate with specific broker implementations, such as the advanced filtering capabilities or custom authentication mechanisms that leverage the new properties.

Want structured learning?

Take the full Mqtt course →