MQTT Topic Design Patterns

IoTMQTTArchitecture
Share on LinkedIn

MQTT topics are your API. Bad names become ACL spaghetti and subscription storms. Good names make brokers, bridges, and humans predictable. I've inherited fleets where every firmware engineer invented their own top-level (data, Data, telemetry, tel) — the broker survived; the ops team did not.

Treat topic design as a public contract: version it, document it, and review changes like API breaking changes.

A durable pattern

{org}/{env}/{domain}/{deviceId}/{channel}/{name}

Example for EV chargers:

acme/prod/evse/sn-1004/telemetry/power
acme/prod/evse/sn-1004/command/set_limit
acme/prod/evse/sn-1004/state/online
acme/prod/evse/sn-1004/event/fault

Why this shape works:

Keep channel closed-world. If someone needs a fifth channel, debate it — don't invent misc.

Wildcards and blast radius

Wildcard Meaning Typical use
+ Single level acme/prod/evse/+/state/online
# Multi-level (trailing only) acme/prod/evse/sn-1004/#

Rules of thumb:

Shared subscriptions (MQTT 5) help competing cloud consumers; they don't fix a topic layout that dumps the whole fleet into one queue without keys.

ACL alignment

Design topics so policies are prefix-based:

# Device sn-1004 identity
publish:   acme/prod/evse/sn-1004/telemetry/#
publish:   acme/prod/evse/sn-1004/state/#
publish:   acme/prod/evse/sn-1004/event/#
subscribe: acme/prod/evse/sn-1004/command/#
subscribe: acme/prod/evse/sn-1004/config/#

If your ACL language needs regex hell or per-metric exceptions, the hierarchy is wrong. The topic tree should make the least-privilege story obvious to a human reading the policy file.

Bridge rules (site ↔ cloud) should rewrite or strip prefixes deliberately — don't bridge +/+/+/+/command/# from the internet into the plant network.

Payloads vs topics

Put identity and routing in the topic; put values and units in the payload (JSON, CBOR, Protobuf). Anti-pattern:

acme/prod/evse/sn-1004/telemetry/power/watts/1234   # value in path — explosion

Better:

topic:   acme/prod/evse/sn-1004/telemetry/power
payload: {"ts":1710000000,"w":1234,"phase":"l1"}

Version breaking payload changes with a field (schema: 2) or a rare topic segment (…/telemetry/v2/power). Prefer payload versioning so ACLs stay stable.

Retained messages and LWT

Align retained usage with state channels — state/online as retained + Last Will is a classic pattern. Don't retain high-frequency telemetry (broker memory and stale-read bugs). Document retained/LWT behavior alongside the topic list; see retained messages and last will.

Anti-patterns

Migration without a flag day

When renaming:

  1. Publish dual-write to old and new topics for a release
  2. Move subscribers
  3. Deprecate old paths with metrics on residual traffic
  4. Drop old ACLs last

Topic design is security design. Sketch ACLs and fan-in before you ship a million publishers — renaming later is a fleet firmware problem.

Shared subscription groups

MQTT brokers deliver each message once per subscription group — use for load-balanced consumers:

# Three workers share load for same topic pattern
Worker 1: SUBSCRIBE $share/processors/telemetry/+/metrics
Worker 2: SUBSCRIBE $share/processors/telemetry/+/metrics
Worker 3: SUBSCRIBE $share/processors/telemetry/+/metrics

Without shared subscriptions, every worker receives every message — triple processing, triple cost. Shared groups require broker support (EMQX, HiveMQ, Mosquitto 2.x with config).

QoS selection guide

QoS Delivery Use case
0 At most once Telemetry, metrics (loss OK)
1 At least once Commands, state updates (dedupe required)
2 Exactly once Billing events, critical config (expensive)

QoS 2 on high-frequency telemetry will melt broker CPU. Use QoS 0 for 1 Hz sensor data, QoS 1 for commands with idempotent handlers.

Topic length and broker limits

Most brokers cap topic length at 65535 bytes but practical limit is lower. Keep segments short:

Monitor broker topic count — unbounded unique topics (one per session ID) exhaust memory indexes.

Pair with IoT OTA updates rollback when command topics trigger firmware deployments.

Common production mistakes

Teams get mqtt topic design patterns wrong in predictable ways:

Production implementations of mqtt topic design patterns fail when staging mirrors production topology poorly, rollback is untested, and on-call runbooks describe the happy path only.

Resources


Frequently asked questions

How deep should an MQTT topic hierarchy be?

Deep enough to encode identity and purpose for ACLs and subscriptions — typically 4–7 levels — but not so deep that every attribute becomes a segment. Put high-cardinality IDs in a stable position; don't invent a new top-level for every feature.

Should device IDs be near the root or the leaf?

Common pattern: `tenant/region/device/{deviceId}/telemetry/{metric}`. Putting `deviceId` before wildcards lets you ACL a device to its own subtree. Avoid `telemetry/#` globally for clients that shouldn't see everything.

What's wrong with a new topic per message type randomly named?

Subscribers and ACLs can't evolve. Standardize verbs (`telemetry`, `command`, `state`, `event`) and payload schemas. Version payloads in the message or a version segment when breaking.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →