MQTT is the common language of local smart home devices. Zigbee2MQTT, Tasmota, Shelly, ESP boards and many DIY sensors can all publish to a broker on your own network. Node-RED is a visual tool that listens to those messages and reacts to them. Together they make a small, vendor-neutral automation setup that runs on a Raspberry Pi or any Debian or Ubuntu machine, with no cloud involved. This guide installs both, locks them down properly, and builds one useful flow: a motion sensor that turns on a light and switches it off two minutes after the last movement.

How the pieces fit

  • Mosquitto is the broker. Devices publish messages to topics (for example zigbee2mqtt/hall_motion) and subscribe to topics they care about. The broker passes messages between them and does nothing else.
  • Node-RED is a client of the broker. A flow subscribes to a topic, applies logic and publishes a command to another topic.
  • Devices never talk to each other directly. That is the point: you can replace a sensor or a light without touching anything except the topic name.

Step 1: install Mosquitto

On Raspberry Pi OS, Debian or Ubuntu:

sudo apt update
sudo apt install mosquitto mosquitto-clients

If you start it now and try to connect from another computer, it will fail. Since version 2.0, Mosquitto with no listener configured binds only to the loopback interface, so only programs on the same machine can connect. That is a sensible default, not a bug. You have to choose explicitly how clients authenticate before opening it to the network.

Step 2: add users and open a listener

Create a password file with one user per device or service. The -c flag creates the file and overwrites an existing one, so use it only for the first user:

sudo mosquitto_passwd -c /etc/mosquitto/passwd nodered
sudo mosquitto_passwd /etc/mosquitto/passwd zigbee2mqtt
sudo mosquitto_passwd /etc/mosquitto/passwd tasmota
sudo chown mosquitto: /etc/mosquitto/passwd
sudo chmod 600 /etc/mosquitto/passwd

Then create /etc/mosquitto/conf.d/local.conf:

listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd

Restart and test from the same machine:

sudo systemctl restart mosquitto
mosquitto_sub -h localhost -u nodered -P 'your-password' -t 'test/#' -v
# in a second terminal:
mosquitto_pub -h localhost -u nodered -P 'your-password' -t test/hello -m 'it works'

A few habits pay off later:

  • One account per client. If a cheap plug is ever compromised, you can lock out that one account without reconfiguring everything else.
  • Restrict topics if you can. An ACL file (acl_file) defines which user may read or write which topics, for example topic readwrite tasmota/# for the Tasmota account only. Recent Mosquitto documentation recommends the ACL file plugin over the old acl_file option, so check the manual for your version.
  • Keep the broker inside your network. Port 1883 is unencrypted. Do not forward it from your router. If you need access from outside, use a VPN rather than exposing the broker.
  • After editing the password file, reload the broker (sudo systemctl reload mosquitto or send it a SIGHUP) so it reads the changes.

Step 3: install Node-RED

The Node-RED project maintains an install script for Debian-based systems, including Raspberry Pi OS:

bash <(curl -sL https://github.com/node-red/linux-installers/releases/latest/download/install-update-nodered-deb)

It installs Node.js 22 LTS if no Node.js is present (or checks that an existing one is at least v20), installs Node-RED with npm and sets it up as a service. You can read the script on GitHub before running it. Two notes from the official documentation: you may need sudo apt install build-essential git curl first, and as of Node.js 24 there are no 32-bit builds, so armv6 boards such as the original Raspberry Pi Zero are no longer supported.

Enable it at boot and start it:

sudo systemctl enable nodered.service
node-red-start

The editor is at http://<pi-address>:1880.

Step 4: secure the editor

By default the Node-RED editor has no password. Anyone on your network who finds port 1880 can change your flows, and a flow can run commands on the machine. Fix this before building anything:

  1. Generate a password hash: node-red admin hash-pw.
  2. In ~/.node-red/settings.js, uncomment the adminAuth section and paste the hash:
adminAuth: {
  type: "credentials",
  users: [{
    username: "admin",
    password: "$2b$08$...your-hash...",
    permissions: "*"
  }]
},
  1. Restart with node-red-restart.

The same settings file has a commented-out https section if you want to serve the editor over HTTPS with your own certificate.

Step 5: connect Node-RED to the broker

Drag an mqtt in node onto the canvas and open it. Add a new broker: server localhost, port 1883, and on the Security tab the nodered user and its password. Set the topic to zigbee2mqtt/hall_motion (use your own sensor's topic) and the output to a parsed JSON object. Connect a debug node, click Deploy and wave at the sensor. You should see something like {"occupancy": true, "battery": 97, ...} in the debug sidebar.

Step 6: the motion light flow

Four nodes do the job:

  1. mqtt in: topic zigbee2mqtt/hall_motion, output parsed JSON.
  2. switch: property msg.payload.occupancy, rule "is true". Messages with occupancy: false stop here, which matters because the off timer should depend on the last movement, not on when the sensor decides to report "no motion".
  3. trigger: send {"state":"ON"} as JSON, then wait 2 minutes, then send {"state":"OFF"}. Tick the option to extend the delay if a new message arrives. Each new movement restarts the two minutes.
  4. mqtt out: topic zigbee2mqtt/hall_light/set. Zigbee2MQTT listens on <device>/set for commands.

Deploy, walk past the sensor, and the light should come on and switch off two minutes after you leave. To add a night mode, put a second switch node before the trigger that checks the time, or use a time range node from the palette.

Why use Node-RED alongside a platform?

If you already run Home Assistant or openHAB, Node-RED is optional. Its strengths are logic that is easier to see than to write in YAML, glue between systems that have no shared integration, and transformations of MQTT payloads from one format to another. Many people keep simple automations in their main platform and put the awkward ones in Node-RED. Whichever you use, keep the broker as the single meeting point so devices do not depend on one piece of software.

Common mistakes

  • Leaving the editor open. An unsecured Node-RED on port 1880 is a remote shell for anyone on the network.
  • Using -c twice with mosquitto_passwd. It overwrites the file and removes every earlier user.
  • One shared MQTT account for everything. It works until you need to revoke one device.
  • Retained command messages. If you publish a set command with the retain flag, the broker replays it every time the device reconnects, and the light turns on after a power cut. Retain state topics, not commands.
  • No backups. Flows live in ~/.node-red/flows.json (older installs may use a file name with the host name in it), and the broker setup in /etc/mosquitto. Copy both somewhere else.