Smart city IoT projects usually start as separate pilots, one for street lights, one for waste bins, one for air quality, and then struggle to share data. This guide explains a city-scale architecture in which every domain publishes to a common MQTT messaging layer, so you can add services without building a new silo each time. You will see the layers, a practical topic hierarchy, connectivity options and the security and scaling decisions that matter.
The Problem with Siloed City Pilots
A typical city ends up with a lighting vendor portal, a waste collection dashboard and an environmental monitoring platform, each with its own data model, login and export format. Combining them, for example dimming lights when air quality is poor or routing collection trucks around road works, becomes an integration project every time.
A shared messaging layer changes the default. If every system publishes its events and states to one broker under a common namespace, any new application can subscribe to what it needs. Vendors still supply devices and specialist software; they just agree to publish to, and read from, the city's backbone. If you need a refresher on the broker's role, read what an MQTT broker does.
Smart City IoT Architecture in Five Layers
| Layer | What lives there | Typical technologies |
|---|---|---|
| Devices | Lamp controllers, bin fill sensors, air quality stations, parking pucks | Microcontrollers, NEMA/Zhaga lamp nodes, ultrasonic and optical sensors |
| Field networks | Low-power radio and cellular links | LoRaWAN, NB-IoT, LTE-M, Wi-Fi in buildings, fibre to cabinets |
| Gateways and network servers | Decode radio payloads, translate protocols | LoRaWAN network servers, edge gateways, protocol converters |
| Messaging backbone | Routes events between producers and consumers | MQTT broker with TLS, structured topics |
| Applications | Dashboards, analytics, control logic, open data | Node-RED, time-series databases, GIS maps, custom services |
The broker sits in the middle on purpose. Field networks and applications change at different speeds; decoupling them through publish/subscribe means a new air quality analytics tool does not require any change to the sensors.
Three Example Domains
Smart street lighting
Lamp controllers report power draw, burning hours and faults, and accept dimming commands. Typical topics include a retained state with the current dim level and a set command topic used by the scheduling service. Fault events, such as a failed driver, become maintenance tickets automatically when a subscriber picks them up. Our article on smart lighting with MQTT covers the command and state patterns in detail.
Waste bin fill levels
Ultrasonic or radar sensors inside bins report fill percentage a few times a day over LoRaWAN or NB-IoT. A route-planning service subscribes to all bins in a district and plans collections for bins above a threshold. The value of MQTT here is less about latency and more about decoupling: the route planner, the open-data portal and the cleanliness dashboard all read the same retained fill levels.
Air quality monitoring
Stations measure particulate matter, nitrogen dioxide, temperature and humidity. Data feeds public maps, health alerts and research. The same sensing and publishing approach used indoors in an indoor air quality monitor scales to street level, with more attention to calibration, enclosure heating and data validation.
Parking is a fourth common domain, described end to end in our smart parking IoT blueprint.
A City-Wide Topic Hierarchy
A shared namespace is the most valuable agreement in the whole project. A layout that has worked well follows the pattern domain first, then geography, then asset:
city/<domain>/<district>/<asset-type>/<asset-id>/<signal>
city/lighting/north/lamp/L-10442/state retained {"on":true,"dim":60,"watts":38}
city/lighting/north/lamp/L-10442/state/set {"dim":30}
city/lighting/north/lamp/L-10442/fault {"code":"DRIVER_FAIL","ts":1760600000}
city/waste/centre/bin/B-2210/fill retained {"pct":82,"ts":1760600000}
city/air/harbour/station/AQ-07/pm25 retained {"ugm3":14.2,"ts":1760600000}
city/parking/old-town/summary retained {"free":112,"total":420}
Why domain first? Because ownership follows domains. The lighting department is responsible for city/lighting/#, which makes credential scoping, auditing and vendor contracts easier to reason about. Applications that need geography can still subscribe across domains: city/+/harbour/# returns everything in the harbour district.
Rules worth writing down at the start:
- Every payload is JSON with a timestamp and explicit units in the key names.
- States are retained; events and commands are not.
- Asset IDs match the city's asset register, not vendor serial numbers.
- Commands always use a
/setsuffix and only designated services publish them.
Connectivity: Getting Data to the Broker
Most city sensors are battery-powered and far from Wi-Fi, so the field network is usually LoRaWAN or cellular IoT. Neither requires each sensor to speak MQTT itself:
- LoRaWAN sensors send small binary payloads to gateways on rooftops; a network server decodes them and publishes to MQTT. Running your own with ChirpStack and MQTT is a common choice for municipalities that want control over their network.
- NB-IoT and LTE-M devices can open an MQTT connection directly over TLS, which suits mains-powered assets like lamp controllers or air stations.
- Wired assets in cabinets and buildings connect through edge gateways that translate Modbus or vendor protocols.
The broker receives clean, decoded data either way. A useful side effect is that connection counts stay modest: thousands of LoRaWAN sensors may arrive through a handful of network server connections.
Consuming City Data: A Small Example
Any application is just another MQTT client. This Python subscriber, using paho-mqtt 2.x, watches every air quality station and flags readings above a threshold. Try it against the public test broker with your own prefix:
import json
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/c9d1e4" # your own random id
LIMIT = 25.0 # example threshold for PM2.5 in ug/m3
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(f"{PREFIX}/air/+/station/+/pm25", qos=1)
def on_message(client, userdata, msg):
parts = msg.topic.split("/")
district, station = parts[-4], parts[-2]
reading = json.loads(msg.payload)
if reading["ugm3"] > LIMIT:
print(f"High PM2.5 in {district} at {station}: {reading['ugm3']}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="air-watch-c9d1e4")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()
mosquitto_pub -h broker.connectmq.com -p 1883 -i aq-c9d1e4 -r -q 1 \
-t connectmq/demo/c9d1e4/air/harbour/station/AQ-07/pm25 -m '{"ugm3":31.5,"ts":1760600000}'
The demo prefix already uses three levels, so the example drops the city root to stay within the public broker's eight-level limit; on a private broker you would use the full city/air/... path. In production, alert thresholds can also live in the broker portal: ConnectMQ offers threshold alert rules and an alerts log, while richer logic belongs in Node-RED or your own services connected as clients.
Security and Governance
City infrastructure is critical infrastructure, and the messaging layer must be treated that way:
- TLS everywhere. Port 8883 for devices and gateways, 8084 secure WebSocket for browser dashboards.
- Unique credentials per gateway or service. Never share one login across vendors. Client IDs bound to credentials make impersonation harder.
- Separate command paths. Only the scheduling service should publish lighting commands; vendors and dashboards should read state.
- Isolation where needed. Sensitive domains can live on a separate broker or a dedicated cluster rather than sharing one with public data.
- Open data by publication, not by access. Publish curated datasets to the open-data portal instead of handing out broker credentials.
Our MQTT security best practices guide expands on credentials, TLS and monitoring.
Scaling and Hosting Decisions
City projects grow from a pilot district to the whole city over years, so choose a hosting model that grows with them. Running your own broker cluster is possible but makes the city responsible for patching, certificates, monitoring and capacity. A fully managed MQTT service shifts that work to the provider. ConnectMQ offers shared serverless plans for pilots, dedicated isolated clusters for production, and BYOC deployments that run in the city's own cloud account when data residency rules require it. For a wider comparison of hosting models, see the cloud MQTT broker guide.
Related use cases such as cold chain monitoring for municipal food and vaccine logistics follow the same pattern, and the IoT automation guide collects projects for buildings, farms and energy.
Smart City IoT Readiness Checklist
- A written topic namespace with domain, district, asset type and ID.
- A payload convention with timestamps and units.
- An asset register that maps IDs to locations and owners.
- Field networks chosen per domain, bridged into MQTT by gateways.
- Credentials per gateway and service, TLS on every connection.
- A clear list of which services may publish commands.
- Monitoring of device availability and battery levels.
- A hosting plan that scales from pilot to city-wide.
Build Your City Data Backbone on ConnectMQ
Start a pilot district on a free Serverless broker, prove the topic model with real sensors, then move to a dedicated or BYOC deployment for production. Sign up at app.connectmq.com, no credit card required, and watch your first city topics appear in the live topic explorer.
Frequently Asked Questions
Do smart city sensors need to support MQTT directly?
No. Most battery sensors use LoRaWAN or NB-IoT, and a network server or gateway decodes their payloads and publishes them to the MQTT broker on their behalf.
Why put the domain before the district in the topic?
Ownership and access usually follow domains, such as lighting or waste. Domain-first topics make it easier to assign responsibilities while still allowing district-wide wildcard subscriptions.
Can one broker serve all city departments?
Yes, as long as each department uses its own credentials and topic subtree. Highly sensitive systems can still be placed on a separate broker or dedicated cluster.
How should a city publish open data from its sensors?
Run a subscriber that curates, aggregates and publishes datasets to the open-data portal. Do not hand out broker credentials to the public.



