Node-RED

Node-RED MQTT Tutorial: Flows, Dashboards and Automation

Learn MQTT in Node-RED end to end: broker configuration, mqtt in and mqtt out nodes, JSON handling, a Dashboard 2.0 view, threshold alerts and a secure TLS connection to a private broker.

Node-RED editor with MQTT in and MQTT out nodes wired to a dashboard in a Node-RED MQTT tutorial
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

This Node-RED MQTT tutorial shows you how to connect Node-RED to a broker, receive and publish messages, turn raw JSON into a live dashboard and fire alerts when values cross a threshold. You will start on the free public broker at broker.connectmq.com with an importable flow, then switch the same flow to a private, TLS-encrypted broker by changing one configuration node. Everything here works on a laptop, a Raspberry Pi or a Docker container.

Why Node-RED and MQTT Fit Together

Node-RED is a flow-based programming tool: you wire nodes together in a browser editor, and each message travels along the wires as a JavaScript object called msg. MQTT is a publish/subscribe protocol where clients exchange messages through a broker using topics. The two share the same mental model. An mqtt in node is a subscriber that injects messages into a flow, and an mqtt out node is a publisher that sends whatever reaches it.

That makes Node-RED a natural glue layer around an MQTT system. Typical jobs include:

  • Translating between devices that speak different payload formats.
  • Building quick operator dashboards without a front-end project.
  • Watching thresholds and sending notifications by email or chat.
  • Writing telemetry into a time-series database.
  • Bridging protocols such as Modbus, OPC UA or HTTP into MQTT topics.

If you need a refresher on brokers, topics and wildcards first, read what an MQTT broker does. The rest of this guide assumes you know that + matches one topic level and # matches everything below a level.

Install Node-RED

Pick whichever installation suits your machine. Any current Node-RED 4.x release includes the MQTT nodes; you do not need extra packages for this tutorial until the dashboard step.

# Option 1: npm (requires Node.js 18 or newer)
sudo npm install -g --unsafe-perm node-red
node-red

# Option 2: Docker
docker run -d --name nodered -p 1880:1880 -v node_red_data:/data nodered/node-red:4.0

# Option 3: Raspberry Pi OS / Debian, official install script
bash <(curl -sL https://github.com/node-red/linux-installers/releases/latest/download/update-nodejs-and-node-red)

Open http://localhost:1880 (or the Pi's address) in a browser. For service setup, upgrades and Windows specifics, see our step-by-step guide to installing Node-RED, and if you prefer containers, running Node-RED in Docker covers volumes and restart policies. The official Node-RED documentation is the reference for every core node.

Step 1: Configure the MQTT Broker Node

Every MQTT node in Node-RED points to a shared configuration node of type mqtt-broker. Configure it once and reuse it across all flows; Node-RED then keeps a single connection to the broker, which matters on brokers that limit connections per account.

Drag an mqtt in node onto the canvas, double-click it, and click the pencil next to Server to add a new broker:

Field Value for the public broker Notes
Server broker.connectmq.com Hostname only, no mqtt:// prefix
Port 1883 Use 8883 with TLS later
Protocol MQTT V5 V3.1.1 also works
Client ID nodered-7f3a (choose your own) Must be non-empty and unique
Keep Alive 60 Seconds
Use clean start Checked Fine for most dashboards

On the Messages tab, set a birth message (connectmq/demo/nr-7f3a/status, payload online, retained, QoS 1) and a will message on the same topic with offline. Other clients can now see whether your Node-RED instance is alive.

Why fill in the client ID yourself? If you leave it empty, Node-RED generates one, which is fine on a test broker. On a production broker that binds client IDs to credentials, you must enter the exact ID you were issued. Two Node-RED instances (say, your laptop and your Pi) with the same client ID will repeatedly disconnect each other.

Step 2: Import a Working Flow

The fastest way to learn is to import a complete flow and take it apart. In the editor, open the menu, choose Import, paste the JSON below and click Import. Change every nr-7f3a to your own random suffix first so you are not sharing topics with other readers of this Node-RED MQTT tutorial.

[
  { "id": "tab_mqtt", "type": "tab", "label": "MQTT tutorial" },
  {
    "id": "cmq_broker", "type": "mqtt-broker", "name": "ConnectMQ public",
    "broker": "broker.connectmq.com", "port": "1883", "clientid": "nodered-7f3a",
    "autoConnect": true, "usetls": false, "protocolVersion": "5", "keepalive": "60",
    "cleansession": true, "autoUnsubscribe": true,
    "birthTopic": "connectmq/demo/nr-7f3a/status", "birthQos": "1", "birthRetain": "true", "birthPayload": "online", "birthMsg": {},
    "closeTopic": "connectmq/demo/nr-7f3a/status", "closeQos": "1", "closeRetain": "true", "closePayload": "offline", "closeMsg": {},
    "willTopic": "connectmq/demo/nr-7f3a/status", "willQos": "1", "willRetain": "true", "willPayload": "offline", "willMsg": {},
    "userProps": "", "sessionExpiry": ""
  },
  {
    "id": "inj_tick", "type": "inject", "z": "tab_mqtt", "name": "every 5 s",
    "props": [{ "p": "payload" }], "repeat": "5", "crontab": "", "once": true, "onceDelay": "1",
    "topic": "", "payload": "", "payloadType": "date", "x": 120, "y": 80, "wires": [["fn_sim"]]
  },
  {
    "id": "fn_sim", "type": "function", "z": "tab_mqtt", "name": "simulate sensor",
    "func": "const base = 25 + 6 * Math.sin(Date.now() / 60000);\nmsg.payload = {\n  temperature: Math.round((base + Math.random()) * 10) / 10,\n  humidity: Math.round(40 + Math.random() * 10),\n  ts: Date.now()\n};\nreturn msg;",
    "outputs": 1, "x": 310, "y": 80, "wires": [["out_tel"]]
  },
  {
    "id": "out_tel", "type": "mqtt out", "z": "tab_mqtt", "name": "publish telemetry",
    "topic": "connectmq/demo/nr-7f3a/line1/sensor1/telemetry", "qos": "0", "retain": "false",
    "broker": "cmq_broker", "x": 530, "y": 80, "wires": []
  },
  {
    "id": "in_tel", "type": "mqtt in", "z": "tab_mqtt", "name": "all sensors",
    "topic": "connectmq/demo/nr-7f3a/+/+/telemetry", "qos": "1", "datatype": "auto-detect",
    "broker": "cmq_broker", "nl": false, "rap": true, "rh": 0, "inputs": 0,
    "x": 130, "y": 180, "wires": [["fn_alert", "dbg_tel"]]
  },
  {
    "id": "fn_alert", "type": "function", "z": "tab_mqtt", "name": "alert on crossing 30 C",
    "func": "const t = Number(msg.payload.temperature);\nif (Number.isNaN(t)) return null;\nconst key = msg.topic;\nconst wasHigh = context.get(key) || false;\nconst isHigh = t > 30;\ncontext.set(key, isHigh);\nif (isHigh === wasHigh) return null;\nmsg.topic = msg.topic.replace(/telemetry$/, 'alert');\nmsg.payload = { level: isHigh ? 'high' : 'normal', temperature: t, ts: Date.now() };\nreturn msg;",
    "outputs": 1, "x": 360, "y": 180, "wires": [["out_alert", "dbg_alert"]]
  },
  {
    "id": "out_alert", "type": "mqtt out", "z": "tab_mqtt", "name": "publish alert",
    "topic": "", "qos": "1", "retain": "true", "broker": "cmq_broker", "x": 590, "y": 160, "wires": []
  },
  {
    "id": "dbg_tel", "type": "debug", "z": "tab_mqtt", "name": "telemetry", "active": true,
    "tosidebar": true, "console": false, "complete": "payload", "x": 340, "y": 240, "wires": []
  },
  {
    "id": "dbg_alert", "type": "debug", "z": "tab_mqtt", "name": "alerts", "active": true,
    "tosidebar": true, "console": false, "complete": "true", "x": 580, "y": 220, "wires": []
  }
]

Click Deploy. Within a few seconds, the small status indicator under each MQTT node turns green with the word "connected", and the debug sidebar starts showing telemetry objects. Verify from a terminal that the messages really go through the broker:

mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-cli-7f3a -v -t "connectmq/demo/nr-7f3a/#"

You should see the retained status message first, then a telemetry message every five seconds and, whenever the simulated temperature crosses 30, an alert.

Step 3: Understand the mqtt in and mqtt out Nodes

The imported flow uses each MQTT node in a specific way, and these settings are the ones you will change most often.

The mqtt in node

  • Topic accepts wildcards. connectmq/demo/nr-7f3a/+/+/telemetry receives telemetry from every sensor on every line under your prefix, and msg.topic tells you which one sent it.
  • Output set to auto-detect parses JSON payloads into objects, leaves plain text as a string and passes binary data as a Buffer. If a device sometimes sends malformed JSON, choose a String instead and parse it yourself with a JSON node so you can catch errors.
  • QoS is the maximum QoS you want to receive. The broker delivers at the lower of the publisher's and subscriber's QoS.
  • Dynamic subscription: set the node to accept input and send it msg.action = "subscribe" with msg.topic. This lets one flow follow topics that are only known at runtime; the dynamic MQTT subscriptions in Node-RED guide has a full example.

The mqtt out node

  • If the node's Topic field is empty, it publishes to msg.topic. The alert branch relies on this: the function node rewrites the topic from .../telemetry to .../alert.
  • If the Topic field is filled, it overrides msg.topic. A frequent beginner bug is setting both and wondering why messages land on the wrong topic.
  • Objects in msg.payload are serialised to JSON automatically. Numbers and booleans become strings.
  • Retain on alert and state topics means any client that subscribes later sees the latest value immediately. Leave it off for high-frequency telemetry.

For deeper JSON handling, including converting strings to objects and back, see the Node-RED JSON node. For writing logic like the alert function, read the Node-RED function node, which covers context storage, multiple outputs and error handling.

Step 4: Build a Node-RED MQTT Dashboard

Raw debug output is not something you can hand to an operator. Dashboard 2.0 is the current, actively maintained dashboard for Node-RED. Install it from Manage palette, search for @flowfuse/node-red-dashboard, or from the command line:

cd ~/.node-red
npm install @flowfuse/node-red-dashboard
# restart Node-RED afterwards

Then extend your flow:

  1. Add a function node after the all sensors mqtt in node with the code below. It splits each telemetry object into chart-friendly messages.
  2. Wire its first output to a ui-gauge (range 0 to 50, label "Temperature") and also to a ui-chart (type line, X axis time).
  3. Wire the second output to a second ui-gauge for humidity.
  4. When you add the first widget, Node-RED asks you to create a page and a group; accept the defaults.
  5. Deploy and open http://localhost:1880/dashboard.
// Split telemetry into one message per metric for gauges and charts
const sensor = msg.topic.split("/").slice(-2, -1)[0]; // e.g. "sensor1"
const p = msg.payload;
if (typeof p !== "object" || p === null) return null;

return [
  { topic: `${sensor} temperature`, payload: Number(p.temperature) },
  { topic: `${sensor} humidity`, payload: Number(p.humidity) }
];

Set this function node to two outputs on its Setup tab. Using the sensor name in msg.topic means a single chart automatically draws one line per sensor when you add more devices.

To control a device from the dashboard, add a ui-switch whose on and off payloads are ON and OFF, and wire it to an mqtt out node with a topic such as connectmq/demo/nr-7f3a/line1/fan1/set, QoS 1. Best practice is to drive the switch's displayed state from the device's own state topic rather than from the click, so the dashboard never shows a state the hardware did not reach. Layouts, themes and multi-page dashboards are covered in our Node-RED Dashboard 2.0 guide, and keeping long history is the topic of Node-RED with InfluxDB.

Step 5: Alerts and Automation

The alert on crossing 30 C function in the imported flow is deliberately written as an edge detector. It stores the previous state per topic in node context and only emits a message when the state changes. Without that, a sensor sitting at 31 degrees would trigger an alert every five seconds and flood your inbox.

From the alert topic you can fan out to any notification channel:

Because the alert itself is published back to MQTT as a retained message, Node-RED is not the only consumer. Any dashboard, mobile app or script subscribed to +/alert sees the same event, which keeps your automation logic in one place and your notification channels decoupled.

A useful pattern for larger setups is a hierarchical topic structure such as site/area/line/cell/device/metric, inspired by ISA-95. It lets a single wildcard subscription in Node-RED cover a whole line or the whole site, and it is the basis of the Unified Namespace approach described in MQTT, Sparkplug B and the Unified Namespace.

Step 6: Move to a Private Broker with TLS

The public broker is shared and readable by anyone, so it is only for testing. For real flows, use your own broker with encryption and credentials. You can run one yourself, but a fully managed MQTT service removes patching, certificates and uptime from your to-do list. With ConnectMQ, you sign up, choose a plan, create a broker and copy the generated username, password and client ID from the portal.

Open the ConnectMQ public configuration node and change it:

Setting New value
Server broker.connectmq.com
Port 8883
Use TLS Checked; add a TLS configuration with Verify server certificate enabled and no certificate files
Client ID YOUR_CLIENT_ID from the portal
Security tab: Username / Password YOUR_USERNAME / YOUR_PASSWORD
Topics Replace connectmq/demo/nr-7f3a with your own namespace, for example acme/plant1

The broker uses a publicly trusted Let's Encrypt certificate, so Node-RED validates it with its built-in CA store and you do not upload any CA file. Credentials are stored in Node-RED's encrypted credentials file, not in the flow JSON, which is why exported flows never contain passwords. Set a credentialSecret in settings.js so that file stays readable after you move Node-RED to another machine.

Details such as client certificates, custom CAs and common handshake errors are in Node-RED MQTT over TLS. Also lock down the editor itself: an exposed Node-RED editor without a login is effectively remote code execution on your server. Securing Node-RED explains adminAuth, HTTPS and network exposure.

Node-RED MQTT Tutorial Troubleshooting

When a node shows "disconnected" or "connecting" forever, work through this checklist:

  1. Hostname typo or protocol prefix. The Server field takes broker.connectmq.com, not mqtt://broker.connectmq.com.
  2. Wrong port for TLS. Port 1883 is plain TCP, 8883 requires the Use TLS checkbox. Mixing them causes silent timeouts.
  3. Duplicate client ID. Another Node-RED instance, a test script or MQTT Explorer using the same ID kicks the connection off every few seconds.
  4. Bad credentials or client ID mismatch. The node log shows "Connection refused: Not authorized". Re-copy all three values from the portal.
  5. Outbound ports blocked. Run nc -vz broker.connectmq.com 8883 from the Node-RED host. Docker containers may also need correct DNS settings.
  6. Messages arrive but look wrong. Check the mqtt in Output setting; a Buffer in the debug sidebar means the node is set to binary.
  7. Rate or size limits. On the public broker, keep each client under 10 messages per second and 64 KB per message; inject nodes set to very short intervals hit this quickly.

For log-level diagnosis of each of these, our dedicated Node-RED MQTT not connecting guide walks through the exact error messages.

Where to Go Next with Node-RED

Once the basics feel familiar, these guides extend the same broker connection into real projects:

Run Your Node-RED Flows on ConnectMQ

Node-RED handles the logic; the broker carries every message between your flows, devices and dashboards. ConnectMQ's free Serverless plan needs no credit card and gives you a private broker with TLS on port 8883, MQTT 5, up to 25 connections and an in-browser MQTT client to inspect topics while you build. When your flows grow, paid Serverless adds shared subscriptions for load-balancing several Node-RED instances, and Dedicated plans give you an isolated cluster. Create your free broker at app.connectmq.com, paste the credentials into your broker node, and deploy. To weigh hosting options in more depth, read our guide to running an MQTT broker in the cloud, or start testing right away on the public MQTT test broker.

Frequently Asked Questions

Does Node-RED include an MQTT broker?

No. Node-RED ships MQTT client nodes only. You connect them to an external broker, such as a hosted broker or a self-run Mosquitto instance; community broker nodes exist but are not recommended for production.

How many connections does Node-RED use for MQTT?

One per mqtt-broker configuration node, no matter how many mqtt in and mqtt out nodes share it. Reuse a single configuration node to stay within connection limits.

Why does my mqtt in node output a string instead of an object?

The Output setting is probably set to a String, or the payload is not valid JSON. Switch to auto-detect or a parsed JSON object, or add a JSON node to parse it explicitly.

Can Node-RED use MQTT 5 features?

Yes. Set the protocol to MQTT V5 in the broker node to use features such as user properties, message expiry, response topics and session expiry from the mqtt in and mqtt out nodes.

Is it safe to use the public broker with Node-RED?

It is fine for learning and demos, but anyone can read public topics. Use unique topic prefixes, send no sensitive data and move to a private broker with TLS before connecting real equipment.

Related articles

Node-RED OPC UA Client to MQTT with ISA-95 Topics

Use node-red-contrib-opcua to subscribe to OPC UA variables on a PLC or simulation server, map node IDs to ISA-95 style MQTT topics, and publish values with quality and timestamps.

9 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.