MQTT brokers are chatty, and tracing a message through them can feel like trying to follow a single raindrop in a storm.

Let’s see it in action. Imagine a simple scenario: a sensor publishes its temperature to /devices/sensor1/temperature, and a subscriber wants to read it.

# On Broker Host (assuming mosquitto)
# Start a listener for all messages on all topics
mosquitto_sub -v -t "#"

# On Sensor Host
# Publish a temperature reading
mosquitto_pub -t "/devices/sensor1/temperature" -m "22.5"

# On Subscriber Host
# (This would be running mosquitto_sub as above)
# You'd see the output:
# /devices/sensor1/temperature 22.5

This basic mosquitto_sub is your first tool. It hooks into the broker and shows you everything arriving. But what if you need to know why a message didn’t arrive, or who sent it?

The core problem MQTT solves is decoupling publishers from subscribers. A publisher doesn’t know or care who’s listening, and subscribers don’t know who’s publishing. They just agree on topics and the broker. When things go wrong, this decoupling becomes a challenge for debugging.

Common Tools and Techniques

  1. Broker Logs: Most MQTT brokers, like Mosquitto, HiveMQ, or EMQX, have detailed logging capabilities.

    • Diagnosis: Check the broker’s log file (e.g., /var/log/mosquitto/mosquitto.log for Mosquitto on Linux). Look for connection attempts, authentication failures, or messages about topic subscriptions/unsubscriptions.
    • Fix: If you see Client <client_id> disconnected due to ... errors, it often points to network issues, incorrect credentials, or the client exceeding connection limits. For authentication issues, verify password_file or acl_file configurations in mosquitto.conf.
    • Why it works: Broker logs provide the broker’s perspective on client activity, revealing handshake failures or protocol violations.
  2. mosquitto_sub with -v and -t "#": As shown above, subscribing to # (the wildcard for all topics) on the broker or a debugging client gives you a real-time view of all messages passing through.

    • Diagnosis: Run mosquitto_sub -v -t "#" -h <broker_host> -p <broker_port>. If your message doesn’t appear here, it never reached the broker, or it was filtered out by a broker-level ACL.
    • Fix: If messages aren’t appearing, check the publisher’s connectivity to the broker (ping <broker_host>) and ensure it’s using the correct host, port, and client ID. Verify the topic string exactly.
    • Why it works: This directly taps into the broker’s message queue, showing what the broker is actively routing.
  3. mosquitto_pub with -d: The -d (debug) flag on mosquitto_pub is invaluable for understanding what the publisher is doing.

    • Diagnosis: Run mosquitto_pub -d -t "/my/topic" -m "hello" -h <broker_host>. Look for output indicating successful connection, subscription confirmation (if applicable), and message publication. Errors here mean the publisher can’t even talk to the broker.
    • Fix: If you see Client <client_id> sending CONNECT and then nothing, or Connection refused errors, re-check broker accessibility, port, and potentially allow_anonymous false in mosquitto.conf if not using anonymous access.
    • Why it works: This shows the client’s outgoing traffic and the broker’s immediate responses, highlighting issues at the client-to-broker communication layer.
  4. Broker ACLs (Access Control Lists): If messages are published but not received by specific subscribers, ACLs are a prime suspect.

    • Diagnosis: Examine the broker’s configuration file (e.g., mosquitto.conf) for acl_file directives. Inspect the ACL file for rules that might deny subscriptions or publications for certain clients or topics. For Mosquitto, a typical rule looks like: deny <clientid> <topic> or allow <clientid> <topic>.
    • Fix: If a subscriber cannot receive messages on /data/sensor1, and the ACL file has a line like deny sensor1_subscriber /data/sensor1, remove or modify that line. Ensure the client ID or username used by the subscriber matches the ACL rule.
    • Why it works: ACLs are the gatekeepers. If a client’s identity doesn’t permit access to a topic, the broker will silently drop the message for that client.
  5. Client-Side Subscriptions: Ensure subscribers are actually subscribed to the correct topic, including wildcards.

    • Diagnosis: On the subscriber client, use mosquitto_sub -v -t "some/topic". If you expect messages on /data/+/temperature, but only subscribe to /data/sensor1/temperature, you’ll miss others.
    • Fix: Subscribe using wildcards if necessary. For example, mosquitto_sub -v -t "/data/#" will receive messages on /data/sensor1/temperature, /data/sensor2/temperature, etc.
    • Why it works: MQTT’s topic matching is hierarchical. A subscriber must express interest in the exact topic path or use wildcards (+ for a single level, # for multiple levels) that encompass the published topic.
  6. Message Retain Flag: A message published with the retain flag set (mosquitto_pub -r) is stored by the broker and sent to new subscribers when they connect. If a subscriber doesn’t get a message immediately, it might be because it wasn’t retained.

    • Diagnosis: When publishing, check if the -r flag was used. When subscribing, check if the subscriber is missing an initial "last known good" value. If the subscriber connects after a message was published and the retain flag was not set, it will never receive that historical message.
    • Fix: If a persistent last value is needed, ensure the publisher uses mosquitto_pub -r -t "/status/device1" -m "online".
    • Why it works: The retain flag is a specific broker feature; without it, messages are ephemeral and only delivered to currently connected subscribers.
  7. Quality of Service (QoS) Levels: Different QoS levels have different guarantees and behaviors. A QoS 0 message is fire-and-forget. A QoS 1 message is guaranteed at least once. A QoS 2 message is guaranteed exactly once. Misunderstanding these can lead to lost messages or duplicates.

    • Diagnosis: Check the QoS level used by both publisher and subscriber. If a publisher uses QoS 0 and the subscriber is temporarily offline, the message is lost. If a publisher uses QoS 1 and the network is flaky, the subscriber might receive duplicates.
    • Fix: For critical data, use QoS 1 or 2. For high-volume telemetry where occasional loss is acceptable, QoS 0 is fine. Ensure the subscriber is prepared to handle potential duplicates if using QoS 1.
    • Why it works: QoS levels dictate the acknowledgment handshake between client and broker, and broker and client, affecting reliability and message delivery guarantees.

After fixing all these, the next thing you’ll likely encounter is a client failing to connect due to TLS certificate issues.

Want structured learning?

Take the full Mqtt course →