Azure IoT Hub at Scale: Message Routing Without the Spaghetti
At 10,000 devices you can read every message. At 10 million you can't — you route. Here's the routing pattern that stays sane past a billion messages a day.
Azure IoT Hub starts feeling small around the time your fleet crosses ten thousand active devices. Not because the hub itself can't handle it — it scales to millions — but because the consumer side becomes a mess. One consumer trying to handle telemetry, twin-change events, lifecycle events, and command acknowledgements turns into a stateful monster.
The pattern: route by message type, fan out by purpose
Use IoT Hub message routing to split traffic at the source.
Each route has a query (e.g. $body.temperature > 70,
$twin.tags.tier = 'gold', or messageType = 'lifecycleEvents')
and a destination endpoint:
- Hot path — anomalies, alerts. Route to Event Hubs → Stream Analytics or Functions.
- Warm path — dashboards. Route to Event Hubs → ASA → Cosmos DB / ADX.
- Cold path — archival, ML training. Route to Blob Storage with auto-partitioning by date.
- Twin events — desired-state sync. Route to Service Bus queues for ordered handling.
- Lifecycle events — device created/deleted. Route to Event Grid → fan out to provisioning workflows.
Why this beats "one consumer reads everything"
- Scale independently. Hot path can have 32 partitions; cold path needs none.
- Different SLAs per path. Anomaly detection at ms latency; archival can be batch.
- Failure isolation. A broken alerting Function doesn't back-pressure your archival pipe.
- Auditing is free. Every route logs its own metrics in Azure Monitor.
The capacity math
IoT Hub Standard S1 = 400k messages/day per unit. Sounds like a lot until you realize a 10k-device fleet sending every 5 seconds is 172.8 million messages/day. You'd need 432 S1 units — absurd.
Switch to S2 (6M msgs/day/unit) or S3 (300M msgs/day/unit) before the bill becomes a meeting.
One thing the docs don't shout
Routing is exclusive by default — a message goes to the first
matching route. To send the same message to multiple destinations, set
fallback route behavior or duplicate-route the message and let downstream
dedupe. Most teams discover this only when their analytics dashboard goes
blank because they added an alerting route above it.