Taking an MQTT prototype to production is mostly about closing the gaps a demo can ignore: who can connect, who can read the data, how topics are organized and how you find out when something breaks. This guide walks a typical hobby project from the public test broker to a private, TLS-protected broker with a clean namespace and basic monitoring, in the order that causes the least rework.
The Starting Point: A Working Prototype
Most MQTT projects begin the same way. A sensor or an ESP32 publishes JSON to a public MQTT test broker, a laptop runs mosquitto_sub or a Node-RED flow, and the data shows up. A typical prototype looks like this:
mosquitto_pub -h broker.connectmq.com -p 1883 -i proto-dev-x9f3 \
-t "connectmq/demo/x9f3/temp" -m '{"c":22.1}'
That is a perfectly good way to start. It is also not production, for five reasons:
- No authentication. Anyone can connect; the public area of broker.connectmq.com is readable by everyone.
- No encryption. Port 1883 sends credentials and payloads in clear text.
- Ad hoc topics. The topic tree grew organically and will not survive more than a handful of devices.
- No presence. You cannot tell an offline device from a quiet one.
- No visibility. When data stops, you find out because a chart looks flat.
The steps below close each gap in turn.
Step 1: Move to Private Credentials
Create an account at app.connectmq.com, pick a plan, create a broker and copy the generated username, password and client ID. The free Serverless plan is enough for a pilot of up to 25 connections; the paid Serverless plan raises that to 1,000 and adds QoS 2, 1 MB messages and shared subscriptions.
Two rules make the rest of the migration painless:
- One credential set per device or service. Client IDs are bound to issued credentials, so sharing one set between two devices makes them disconnect each other.
- Keep credentials out of source control. Load them from environment variables, a secrets file excluded from git, or a provisioning step at the factory.
# .env (never commit this file)
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
Step 2: Turn On TLS Everywhere
Production traffic should use TLS on port 8883 for devices and Secure WebSocket on port 8084 with path /mqtt for browsers. ConnectMQ uses a Let's Encrypt certificate, so most operating systems and language runtimes already trust it. On microcontrollers you typically embed the ISRG Root X1 certificate; our ESP32 MQTT SSL guide shows the details.
Here is the prototype publisher rewritten for production in Python with paho-mqtt 2.x. It reads credentials from the environment, connects over TLS with MQTT 5, sets a Last Will so the broker can announce unexpected disconnects, and publishes a retained online status.
import json
import os
import time
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
HOST = os.environ["MQTT_HOST"]
PORT = int(os.environ.get("MQTT_PORT", "8883"))
CLIENT_ID = os.environ["MQTT_CLIENT_ID"]
BASE = "acme/site1/greenhouse/zone2/sensor-01" # production namespace
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("connect failed:", reason_code)
return
print("connected")
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
def on_disconnect(client, userdata, flags, reason_code, properties):
print("disconnected:", reason_code)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=CLIENT_ID, protocol=mqtt.MQTTv5)
client.username_pw_set(os.environ["MQTT_USERNAME"], os.environ["MQTT_PASSWORD"])
client.tls_set()
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
connect_props = Properties(PacketTypes.CONNECT)
connect_props.SessionExpiryInterval = 3600 # keep the session for an hour offline
client.connect(HOST, PORT, keepalive=60, properties=connect_props)
client.loop_start()
try:
while True:
reading = {"ts": int(time.time()), "temp_c": 22.1, "rh": 61}
client.publish(f"{BASE}/telemetry", json.dumps(reading), qos=1)
time.sleep(30)
except KeyboardInterrupt:
client.publish(f"{BASE}/status", "offline", qos=1, retain=True).wait_for_publish()
client.disconnect()
client.loop_stop()
Notice how little changed compared with the prototype: hostname, port, credentials, TLS and a few properties. That is the point of using standard MQTT end to end.
Step 3: Design a Production Topic Namespace
A prototype topic like connectmq/demo/x9f3/temp works for one sensor. A production namespace has to survive new sites, new device types and new consumers without renaming anything. A good structure goes from general to specific and separates data from commands and status.
| Level | Example | Purpose |
|---|---|---|
| Organization or tenant | acme |
Isolates customers or business units |
| Site | site1 |
Physical location |
| Area | greenhouse |
Building, line or room |
| Unit | zone2 |
Logical group of devices |
| Device | sensor-01 |
Stable device identifier |
| Message class | telemetry, status, cmd, cmd/ack |
What the message is |
Practical rules that save pain later:
- Keep levels lowercase, no spaces, no leading slash.
- Put the device ID in the topic, not only in the payload, so subscribers can filter with wildcards.
- Use a separate
cmdbranch for commands so devices subscribe narrowly. - Make status topics retained so late subscribers see the current state immediately.
- Keep depth reasonable; the public broker allows 8 levels, which is a sensible budget anywhere.
If your project is industrial, consider aligning with ISA-95 levels from the start. ConnectMQ's devices view groups clients by ISA-95 levels when your topics follow that pattern. Our smart home MQTT topic structure guide covers residential projects.
Step 4: Track Device Presence
Retained online and offline status messages with a Last Will, as in the code above, give every subscriber a reliable view of which devices are connected. You can also watch broker-side state through $SYS/broker/connection/{clientId}/state, which is useful for a backend service that needs to know when a specific client drops.
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID -V mqttv5 \
-t 'acme/site1/+/+/+/status' -v
Step 5: Add Monitoring and Alerts
Production means someone finds out about problems before users do. Start simple:
- Use the portal dashboard and live topic explorer to confirm data is flowing after each deployment.
- Create threshold alert rules for values that matter, such as a temperature out of range, and review the alerts log.
- Log MQTT 5 reason codes on every client so quota and authorization problems are diagnosable.
- If you need long-term storage or custom notifications, run your own subscriber, for example a Python service, Telegraf or Node-RED, that connects to the broker as a normal MQTT client and writes to your database or messaging tool.
For a deeper list, read our MQTT broker monitoring guide.
The Production Readiness Checklist
Before you ship, confirm each item:
- Every device and service has its own credentials and client ID.
- All connections use TLS (8883) or Secure WebSocket (8084).
- No credentials live in firmware source control.
- The topic namespace is documented and starts with an organization or tenant level.
- Devices publish retained status with a Last Will.
- Reconnect logic uses exponential backoff with jitter.
- Payloads stay well under the plan's message size limit.
- Commands use at least QoS 1 and are safe to receive twice.
- You have a plan for firmware updates that rotate credentials.
- Alerts reach a human when key values or device presence change.
When the list is done, the MQTT production checklist and MQTT security best practices go further on hardening.
Choosing the Right Plan for Production
Start with the smallest plan that covers your connection count with headroom. Move to paid Serverless when you need more than 25 connections, QoS 2, larger messages or shared subscriptions for backend consumers. Choose Dedicated when you want an isolated cluster, BYOC when the broker must run in your own cloud account, and Self Managed Enterprise when you need to run it yourself. The cloud MQTT broker guide compares these hosting models in more depth.
Take Your Prototype to Production with ConnectMQ
ConnectMQ gives you private credentials, TLS, MQTT 5 and a portal with a live topic explorer and alert rules, on the same standard MQTT your prototype already speaks. Sign up at app.connectmq.com, create a broker on the free Serverless plan with no credit card, and point your prototype at it in minutes. If you would rather not run any infrastructure yourself, a managed MQTT broker is the shortest path.
Frequently Asked Questions
Can I keep using the public broker in production?
No. The public area of broker.connectmq.com is readable by everyone and has tight per-client limits, so it is meant for testing only. Production data belongs on a private broker with credentials and TLS.
Do I need to change my device code to move to production?
Usually only the connection settings change: hostname, port 8883, credentials and TLS. Topic names change if you adopt a new namespace, which is worth doing before you scale.
How should I handle credentials on many devices?
Issue one credential set per device, load it during provisioning rather than compiling it into shared firmware, and keep a record so you can revoke a single device without touching the others.
Which plan should a small pilot use?
A pilot with up to 25 concurrent connections fits the free Serverless plan. Move to paid Serverless when you need more connections, QoS 2 or larger messages.



