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:
- Drag an
mqtt innode onto the canvas and open it. - Select or create a broker configuration node. For testing, use server
broker.connectmq.com, port1883, and a unique non-empty client ID such asnr-dyn-91c4. - Change Action from "Subscribe to single topic" to Dynamic subscription.
- Set Output to "auto-detect" or "a parsed JSON object" as usual.
- 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 normalmqtt innode reads it and feeds the function above, so changing the retained message reconfigures every Node-RED instance that listens. - HTTP endpoint. An
http innode 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
- No input wire, no subscriptions. A dynamic node subscribes to nothing until it receives a message.
- 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.
- Overlapping filters. Subscribing to both
line1/#andline1/cnc1/telemetrycan deliver the same message twice. Keep filters disjoint. - Limits. Respect the broker's subscription and topic-depth limits; the public broker caps topics at 8 levels.
- Lost state. Store the desired list and reapply it on startup, as shown above.
- Wrong output for the wrong topic. All dynamic topics share one output, so route with a Switch node on
msg.topicif 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.



