Node-RED

Node-RED Dynamic MQTT Subscribe: Change Topics at Runtime

Learn how the mqtt in node's dynamic subscription mode works, how to subscribe and unsubscribe with msg.action, how to restore subscriptions after a restart and how to drive it from a dashboard or a device list.

Node-RED flow sending subscribe and unsubscribe actions to an mqtt in node for a Node-RED dynamic MQTT subscribe setup
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

Node-RED dynamic MQTT subscribe mode lets a flow decide which topics to listen to while it is running, instead of hard-coding one topic in the mqtt in node. In this tutorial you will configure a dynamic subscriber, add and remove topics with msg.action, list active subscriptions, restore them after a restart and drive the whole thing from a simple device list.

When You Need Dynamic Subscriptions

A normal mqtt in node subscribes to one fixed topic filter at deploy time. That covers most flows, especially when a wildcard like site/+/telemetry already matches every device. Dynamic subscriptions are worth the extra wiring when:

  • An operator picks which machine to watch from a dashboard, and you only want that machine's data flowing.
  • The list of devices lives in a database or configuration topic and changes without a redeploy.
  • A wildcard would pull in far more traffic than you need, for example # on a busy site.
  • You want to temporarily follow a debug topic during troubleshooting and then drop it.
  • You are close to a broker's subscription limit and must be selective.

The last point matters on shared infrastructure. The free public MQTT broker at broker.connectmq.com, for example, allows 10 subscriptions per client, so subscribing on demand is a practical way to stay within budget.

Configure the mqtt in Node for Dynamic Mode

Dynamic subscriptions arrived in Node-RED 2.2 and are part of every current release. To enable them:

  1. Drag an mqtt in node onto the canvas and open it.
  2. Select or create a broker configuration node. For testing, use server broker.connectmq.com, port 1883, and a unique non-empty client ID such as nr-dyn-91c4.
  3. Change Action from "Subscribe to single topic" to Dynamic subscription.
  4. Set Output to "auto-detect" or "a parsed JSON object" as usual.
  5. Deploy.

The node now has an input port, and its status shows "connected" but it is not subscribed to anything yet. Everything from here is controlled by messages you send into that input.

You will also notice a Connect automatically option on the broker configuration node. Leave it ticked unless you also want to control the connection itself with connect and disconnect actions, covered later.

Node-RED Dynamic MQTT Subscribe Actions

The node reacts to msg.action and msg.topic:

msg.action msg.topic Result
subscribe "a/b/c" Subscribe to one topic at the node's default QoS
subscribe ["a/+", "b/#"] Subscribe to several topic filters
subscribe { "topic": "a/b", "qos": 1 } Subscribe with explicit options
unsubscribe "a/b/c" or an array Remove those subscriptions
unsubscribe true Remove all subscriptions on this node
getSubscriptions not needed Output the current list in msg.payload
connect / disconnect not needed Open or close the broker connection

The object form accepts MQTT 5 subscription options as well, such as nl (no local), rap (retain as published) and rh (retain handling), when the broker node uses protocol version 5.

Messages received on dynamically added topics come out of the same output as a normal subscription, with msg.topic set to the actual topic, so the rest of your flow does not change.

A Working Example on the Public Broker

Start a publisher that simulates three machines. Replace 91c4 with your own random string so your topics are unique:

for i in $(seq 1 300); do
  for m in cnc1 cnc2 press1; do
    mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-dyn-pub-91c4 \
      -t "connectmq/demo/91c4/line1/$m/telemetry" \
      -m "{\"machine\":\"$m\",\"load\":$((RANDOM % 100))}"
  done
  sleep 3
done

Now import the flow below. It has inject nodes that subscribe to cnc1, add press1, list subscriptions and unsubscribe everything.

[
  {"id":"tabd","type":"tab","label":"Dynamic subscribe"},
  {"id":"brkd","type":"mqtt-broker","name":"ConnectMQ public","broker":"broker.connectmq.com","port":"1883","clientid":"nr-dyn-91c4","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true},
  {"id":"i1","type":"inject","z":"tabd","name":"watch cnc1","props":[{"p":"action","v":"subscribe","vt":"str"},{"p":"topic","v":"connectmq/demo/91c4/line1/cnc1/telemetry","vt":"str"}],"repeat":"","once":false,"x":130,"y":60,"wires":[["dyn"]]},
  {"id":"i2","type":"inject","z":"tabd","name":"add press1 (QoS 1)","props":[{"p":"action","v":"subscribe","vt":"str"},{"p":"topic","v":"{\"topic\":\"connectmq/demo/91c4/line1/press1/telemetry\",\"qos\":1}","vt":"json"}],"repeat":"","once":false,"x":150,"y":110,"wires":[["dyn"]]},
  {"id":"i3","type":"inject","z":"tabd","name":"list","props":[{"p":"action","v":"getSubscriptions","vt":"str"}],"repeat":"","once":false,"x":110,"y":160,"wires":[["dyn"]]},
  {"id":"i4","type":"inject","z":"tabd","name":"unsubscribe all","props":[{"p":"action","v":"unsubscribe","vt":"str"},{"p":"topic","v":"true","vt":"bool"}],"repeat":"","once":false,"x":140,"y":210,"wires":[["dyn"]]},
  {"id":"dyn","type":"mqtt in","z":"tabd","name":"dynamic","topic":"","qos":"0","datatype":"auto-detect","broker":"brkd","inputs":1,"x":380,"y":130,"wires":[["dbgd"]]},
  {"id":"dbgd","type":"debug","z":"tabd","name":"received","active":true,"complete":"true","x":560,"y":130,"wires":[]}
]

Click "watch cnc1" and only cnc1 data appears in the debug sidebar. Click "add press1" and both machines show up. "list" outputs the active subscriptions, and "unsubscribe all" stops the flow of data without a redeploy.

Restore Subscriptions After a Restart

This is the part most people miss: dynamic subscriptions live in memory. When you redeploy the node, restart Node-RED or the broker connection drops and reconnects with a clean session, the node may come back with nothing subscribed. Plan for it from the start.

A reliable pattern is to keep the desired list in persistent context and re-apply it on startup:

// Function node "apply subscriptions"
// Input: msg.payload = array of machine IDs, e.g. ["cnc1", "press1"]
const prefix = "connectmq/demo/91c4/line1/";
const wanted = Array.isArray(msg.payload) ? msg.payload : [];

global.set("watchedMachines", wanted, "file");   // needs a file-backed context store

// Two messages, in order, from a single output
return [[
    { action: "unsubscribe", topic: true },
    { action: "subscribe", topic: wanted.map(m => ({ topic: prefix + m + "/telemetry", qos: 1 })) }
]];

The double brackets send both messages from the single output in sequence, so the unsubscribe always lands before the new subscribe. Then add an inject node set to fire once after a few seconds, with its payload set to the global property #:(file)::watchedMachines (the syntax Node-RED uses to read from a named context store), and feed it into the same function. Setting up file-backed storage is explained in our guide to Node-RED context.

Clearing everything before resubscribing keeps the node's state predictable: what you stored is exactly what you get.

Driving Subscriptions from a Dashboard or Config Topic

Because the input is just a message, anything can control it:

  • Dashboard dropdown. A Dashboard 2.0 dropdown outputs the selected machine; a Change node builds msg.action = "subscribe" and the topic. Send an unsubscribe for the previous selection first.
  • Configuration topic. Publish a retained JSON list of machines to a topic such as connectmq/demo/91c4/config/watchlist. A normal mqtt in node reads it and feeds the function above, so changing the retained message reconfigures every Node-RED instance that listens.
  • HTTP endpoint. An http in node can accept a list from another system and apply it the same way.

The retained configuration topic is especially neat: the broker hands the latest list to Node-RED as soon as it subscribes, which also solves the restart problem without file storage. If you need to clear that list later, see our note on deleting retained messages.

Dynamic Broker Connections

The same mechanism controls connections. Sending msg.action = "disconnect" closes the broker connection and connect reopens it. With connect you can also pass msg.broker containing properties such as broker, port, clientid, username and password to override the configuration node at runtime. This is useful for switching between a test broker and production, but keep credentials out of flows: read them from environment variables in a function node rather than typing them into inject nodes.

Pitfalls Checklist

  1. No input wire, no subscriptions. A dynamic node subscribes to nothing until it receives a message.
  2. Duplicate client IDs. Two Node-RED instances with the same client ID will knock each other off the broker; each instance needs its own ID.
  3. Overlapping filters. Subscribing to both line1/# and line1/cnc1/telemetry can deliver the same message twice. Keep filters disjoint.
  4. Limits. Respect the broker's subscription and topic-depth limits; the public broker caps topics at 8 levels.
  5. Lost state. Store the desired list and reapply it on startup, as shown above.
  6. Wrong output for the wrong topic. All dynamic topics share one output, so route with a Switch node on msg.topic if they need different handling.

If something refuses to connect at all, work through Node-RED MQTT not connecting before debugging subscription logic. Full details of the node's input properties are in the official Node-RED MQTT node documentation and the node's built-in help panel.

Use Dynamic Subscriptions with ConnectMQ

On a private broker you get room to grow: ConnectMQ is a hosted MQTT broker supporting MQTT 3.1.1 and 5.0, so MQTT 5 subscription options in the object form work as expected. Its portal includes a live topic explorer, which makes it easy to confirm which topics devices are actually publishing before you build a watchlist. Create a free Serverless broker at app.connectmq.com, no credit card required, paste the credentials into your broker node with TLS on port 8883, and your dynamic flow runs unchanged. For background on hosting choices, read about running an MQTT broker in the cloud, or start from the Node-RED MQTT tutorial if you are new to these nodes.

Frequently Asked Questions

Which Node-RED version supports dynamic MQTT subscriptions?

Node-RED 2.2 and later. If your mqtt in node has no "Dynamic subscription" option, upgrade Node-RED.

Do dynamic subscriptions survive a redeploy?

No. They are held in memory, so store the list you want in persistent context or a retained configuration topic and reapply it when the flow starts.

Can I set QoS per topic with dynamic subscriptions?

Yes. Send an object such as { "topic": "a/b", "qos": 1 }, or an array of such objects, as msg.topic with msg.action set to subscribe.

How do I see what the node is subscribed to?

Send a message with msg.action set to getSubscriptions. The node outputs the current subscription list in msg.payload.

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.