MQTT Fundamentals

MQTT $SYS Topics: What Broker System Topics Tell You

A practical guide to the $SYS topic tree: where it comes from, why wildcards skip it, what Mosquitto-style brokers publish there and how to watch client connection state on ConnectMQ.

Illustration of mqtt sys topics branching from a broker into connection, client and message statistics
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

MQTT SYS topics are the $SYS/... branch of the topic tree where a broker publishes information about itself instead of application data. In this article you will learn where the convention comes from, why a plain # subscription never shows them, what a typical broker exposes there, and how to use the one system topic ConnectMQ publishes to track client connection state.

What Are MQTT SYS Topics?

The MQTT specification reserves topics that begin with $ for broker-specific use. Clients are not supposed to publish application data under them, and the specification leaves their contents entirely to the implementation. Over the years $SYS/ became the de facto prefix for broker telemetry: uptime, version, connected client counts, message and byte counters, and similar housekeeping data.

The important word is "de facto". There is no standard list of $SYS topics, no standard payload format and no guarantee that a given broker publishes any of them. Mosquitto popularised a large tree, other brokers publish a subset or a different layout, and many managed services replace most of it with dashboards and APIs. Treat every $SYS topic as an implementation detail of the specific broker you are connected to.

Why a # Subscription Does Not Show Them

This surprises nearly everyone the first time. If you subscribe to # you receive every application message on the broker, but nothing from $SYS. That is by design. The MQTT specification on mqtt.org states that a topic filter starting with a wildcard (# or +) must not match topic names that start with $.

The rule protects ordinary "catch-all" subscribers from being flooded with broker statistics, and it keeps tools that mirror # from accidentally republishing system data. To read system topics you have to name the $SYS prefix explicitly:

#                  -> application topics only, never $SYS
$SYS/#             -> everything under $SYS (if the broker allows it)
$SYS/broker/+/+    -> a specific depth under $SYS/broker
+/broker/uptime    -> does NOT match $SYS/broker/uptime

The same reasoning applies to the wildcard rules for MQTT topics in general: wildcards work level by level, and the leading $ level is simply excluded from wildcard matching at the first position.

A Typical $SYS Tree (Mosquitto Example)

Mosquitto is the most common reference, so it is a good way to see what system topics can contain. With the default configuration it publishes a snapshot of its counters every few seconds. The interval is controlled in mosquitto.conf:

# mosquitto.conf
# Publish $SYS updates every 10 seconds (0 disables periodic updates)
sys_interval 10

A selection of the topics you will see on a local Mosquitto instance:

Topic Meaning
$SYS/broker/version Broker version string
$SYS/broker/uptime Seconds since the broker started
$SYS/broker/clients/connected Currently connected clients
$SYS/broker/clients/disconnected Persistent sessions whose client is offline
$SYS/broker/messages/received Total messages received since start
$SYS/broker/messages/sent Total messages sent since start
$SYS/broker/subscriptions/count Active subscriptions
$SYS/broker/retained messages/count Retained messages held
$SYS/broker/load/messages/received/1min Moving average of received messages
$SYS/broker/bytes/received Total bytes received

To watch them on your own Mosquitto broker, quote the topic so your shell does not expand $SYS:

mosquitto_sub -h localhost -p 1883 -i sys-watcher-01 -t '$SYS/#' -v

Most values are published as retained messages, so a new subscriber gets the latest snapshot immediately instead of waiting for the next interval. Counters are reset when the broker restarts, which matters if you graph them: a drop to zero means a restart, not a quiet hour.

Common Pitfalls With $SYS Data

  • Assuming portability. A dashboard built on Mosquitto $SYS topics breaks the moment you move to another broker. Keep the parsing logic in one place.
  • Forgetting the shell. In bash, "$SYS/#" with double quotes expands $SYS to an empty variable and you subscribe to /#. Use single quotes.
  • Exposing it publicly. On a self-hosted broker, system topics reveal client counts and version numbers. Restrict them with your broker's authorization rules if the broker is reachable from the internet.
  • Polling too fast. A very short sys_interval adds traffic for little value. Ten to sixty seconds is plenty for operational graphs.

MQTT SYS Topics on a Managed Broker

On a managed MQTT broker you usually do not need a large $SYS tree, because the operator already collects broker metrics and shows them to you. That is the approach ConnectMQ takes. Broker-level information such as connections and traffic is presented in the portal dashboard, and the live topic explorer lets you see which topics are active, so there is no need to parse counters from system topics.

It publishes exactly one system topic:

$SYS/broker/connection/{clientId}/state

It reports the connection state of a specific client ID. This is the piece of $SYS data that application code actually needs: "is device X online right now?" Everything else, such as client counts and message rates, belongs in monitoring rather than in your MQTT data flow.

Watching a Client's Connection State

The example below uses Python paho-mqtt 2.x to subscribe to the state topic of one device on your private broker. Replace the placeholders with the credentials generated in the portal, and set DEVICE_ID to the client ID of the device you want to watch.

import ssl
import paho.mqtt.client as mqtt

BROKER = "YOUR_BROKER_HOST"      # shown in the portal
PORT = 8883
DEVICE_ID = "YOUR_DEVICE_CLIENT_ID"
STATE_TOPIC = f"$SYS/broker/connection/{DEVICE_ID}/state"

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connect failed: {reason_code}")
        return
    print("Connected, subscribing to", STATE_TOPIC)
    client.subscribe(STATE_TOPIC, qos=1)

def on_subscribe(client, userdata, mid, reason_code_list, properties):
    for rc in reason_code_list:
        print("SUBACK:", rc)

def on_message(client, userdata, msg):
    print(f"{msg.topic} -> {msg.payload.decode(errors='replace')}")

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(cert_reqs=ssl.CERT_REQUIRED)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message

client.connect(BROKER, PORT, keepalive=60)
client.loop_forever()

Run it, then power-cycle the device or stop its client. You will see the state topic change as the device disconnects and reconnects. Before you write parsing logic, look at the actual payload in the portal's in-browser MQTT client so your code matches what the broker sends rather than what you assume.

$SYS State Versus Last Will

You may already be tracking presence with a Last Will and Testament message. The two approaches complement each other:

Approach Who publishes Strength Limitation
Last Will plus retained "online" message The device and the broker on its behalf Works on any broker; topic and payload are yours Device must configure it correctly; a misconfigured client never reports
$SYS/broker/connection/{clientId}/state The broker No device code required; reflects what the broker actually sees Broker-specific topic layout

A sensible pattern is to keep the Last Will for application consumers that already understand your topic tree, and use the broker's state topic for operations tooling and for verifying that devices without a Will are behaving. The guide to MQTT device online status walks through the Will-based pattern in detail.

Monitoring Checklist Without a Big $SYS Tree

If you are coming from a self-hosted broker where Grafana graphs were fed from $SYS, here is how to cover the same needs on a managed service:

  1. Connection counts and traffic. Use the broker dashboard in the portal instead of $SYS/broker/clients/connected.
  2. Which topics are alive. Use the live topic explorer instead of subscribing to # from a laptop.
  3. Per-device presence. Subscribe to $SYS/broker/connection/{clientId}/state for the client IDs you care about, or use a Last Will.
  4. Threshold conditions on data. Configure alert rules in the portal for values such as temperature or pressure, and review the alerts log.
  5. Long-term metrics history. Run your own subscriber (Telegraf, a small Python service or Node-RED) that writes the data you need into your time-series database.
  6. Quota awareness. Watch for MQTT 5 reason code 0x97 (quota exceeded) in client logs; it tells you a plan limit was reached.

Testing on the Public Broker

If you only want to experiment with topic structure, the public MQTT test broker at broker.connectmq.com accepts anonymous connections with any non-empty unique client ID. Keep in mind that the public area is readable by everyone, so use a unique prefix and never send sensitive data:

# Terminal 1: subscribe to your own demo prefix
mosquitto_sub -h broker.connectmq.com -p 1883 -i sys-demo-sub-7f3a \
  -t 'connectmq/demo/7f3a/#' -v

# Terminal 2: publish a test message
mosquitto_pub -h broker.connectmq.com -p 1883 -i sys-demo-pub-7f3a \
  -t 'connectmq/demo/7f3a/hello' -m 'hi'

Notice that the # subscription in terminal 1 sees your message but nothing under $SYS, exactly as the specification requires. For a broader overview of how topics, subscriptions and the broker fit together, the MQTT tutorial is a good next read, and MQTT topic naming conventions covers how to keep your own tree as tidy as a broker's system tree.

Get Started with ConnectMQ

Want broker metrics without parsing $SYS counters? Create a free Serverless broker at app.connectmq.com, no credit card required. You get generated credentials, a dashboard, a live topic explorer and the $SYS/broker/connection/{clientId}/state topic for per-device presence in a few minutes.

Frequently Asked Questions

Why does subscribing to # not show $SYS messages?

The MQTT specification says topic filters that begin with a wildcard must not match topics that begin with $. Subscribe to $SYS/# explicitly if your broker publishes system topics and allows you to read them.

Are MQTT SYS topics standardized?

No. Only the reservation of $-prefixed topics is standardized. The topic names, payloads and update intervals differ between brokers, so code that reads them is broker-specific.

Which $SYS topic does ConnectMQ publish?

ConnectMQ publishes $SYS/broker/connection/{clientId}/state, which reports a client's connection state. Broker metrics such as connections and traffic are shown in the portal dashboard instead.

Can I publish my own messages to $SYS topics?

You should not. The $ prefix is reserved for the broker, and most brokers reject or ignore client publishes there. Put your own status data under your application topic tree.

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.