· 4 min read
How to Design an MQTT Topic Hierarchy
Heshan Fernando
Co-founder & COO
Four hundred sensors are deployed across three buildings. Six months later someone asks for every temperature reading site-wide, and the topic structure cannot express it — because temperature sits above site in the hierarchy.
Fixing that means reflashing four hundred devices. The decision that caused it took thirty seconds and looked arbitrary at the time.
Wildcards work positionally
MQTT has two wildcards, and both operate on level positions.
+ matches exactly one level. site/+/sensor-04/temperature matches that sensor’s temperature in any building.
# matches all remaining levels, and must be last. site/factory1/# matches everything under factory1.
Because they are positional, the order of levels determines which subscriptions are possible. There is no way to wildcard across a level that is not in the position you need.
site/building/device/metric lets you subscribe to one device’s everything, or one metric across everything, because metric is last and can be wildcarded at any level above it.
site/metric/building/device gives you a different set of possibilities, and neither ordering gives you both cheaply.
Design from the subscriptions backwards
The hierarchy follows from what you will need to subscribe to, not from how the organisation is structured.
Write the subscriptions first:
- All temperatures, everywhere
- Everything from one device
- All metrics from one building
- All alarms across all sites
Then check whether a proposed ordering expresses them. Anything it cannot express is either impossible or requires multiple subscriptions, which is workable and worth knowing before deployment.
| Order | All temps site-wide | One device’s data |
|---|---|---|
| site/building/device/metric | site/+/+/temperature | site/b1/dev4/# |
| site/metric/building/device | site/temperature/# | Not expressible in one |
Naming rules worth following
No leading slash. /site/building creates an empty first level, which is legal and confuses every tool that touches it.
No spaces. Legal in the specification and a persistent source of trouble in shell scripts, config files and logs.
No + or # in a published topic name. These are wildcard characters and cannot appear in a topic being published to.
Lowercase, consistently. Topics are case sensitive, so Temperature and temperature are different topics — and a mix of both is a bug that surfaces months later when one publisher is deployed with the wrong case.
ASCII only. UTF-8 is permitted and regularly regretted, particularly when a device with a limited MQTT client cannot handle it.
Depth is a cost
Each level is one more thing every publisher must construct correctly, and one more place a typo produces a topic nobody subscribes to — silently, because MQTT does not error on publishing to a topic with no subscribers.
Deep enough to express the subscriptions you need, and no deeper. A level that exists “in case we need it later” is pure overhead now and rarely turns out to be the level you actually needed.
Separate commands from telemetry
A structural decision worth making at the top of the hierarchy.
Telemetry flows from devices outward. Commands flow inward toward devices. Mixing them in the same branch means a wildcard subscription intended for readings also picks up commands, and a misconfigured client can publish to a topic a device treats as an instruction.
The common pattern is two top-level branches — one for state and telemetry, one for commands — with the same structure beneath each. A device subscribes only to its command branch and publishes only to its telemetry branch, which makes the permission model straightforward.
That separation also makes broker access control workable, since permissions can be granted per branch rather than per individual topic.
Common mistakes to avoid
- Designing the hierarchy from the org chart rather than from the subscriptions.
- Putting the attribute you most need to filter across in an early level.
- Leading slashes and mixed case.
- Adding speculative levels that are never used.
- Deploying before testing the wildcard subscriptions, since reordering afterwards means touching every device.
How to do it with MQTT Topic Structure Planner
The MQTT Topic Structure Planner tests the subscriptions against a proposed hierarchy.
- List the levels from most general to most specific.
- Write the subscriptions you will actually need.
- Check each one is expressible — reordering now is free.
- Review the naming warnings before deploying anything.
The MQTT specification defines topic and wildcard rules precisely. Other developer tools are in the tools directory.
Frequently asked questions
Should the hierarchy go general to specific?
Generally yes, because wildcards work positionally and the attribute you most need to filter across should sit where a wildcard can reach it. Design from the subscriptions rather than from convention.
What characters should I avoid?
Leading slashes, spaces, mixed case, and the wildcard characters themselves. Non-ASCII is legal and causes problems with constrained clients.
How deep should the hierarchy go?
Deep enough for the subscriptions you need. Every extra level is one more thing every publisher must construct correctly, and a typo publishes silently to a topic nobody reads.
Final thought
Write the subscriptions before the hierarchy. Reordering levels costs nothing on paper and means reflashing the fleet after deployment.