Node-RED is a visual, flow-based tool for wiring IoT logic together: when this happens, do that. Installed on a VPS, it gives every connected device a shared, always-on place to run automation, separate from any single vendor’s app. By default it runs on port 1880 with no authentication at all, which makes the security steps in this guide not optional, but the first thing to do.
The problem this solves
Vendor automation apps work fine until you want a rule that spans two vendors: “if the doorbell detects a person AND nobody’s home, turn on the hallway light and send a notification” rarely works cleanly across two separate apps that weren’t built to talk to each other. Node-RED solves this by being deliberately vendor-agnostic: anything that can publish MQTT, hit an HTTP endpoint, or has a Node-RED-compatible integration becomes a draggable block you can wire into logic, regardless of who made it.
Installing Node-RED
The official installer script handles Node.js and Node-RED together on Ubuntu:
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
Once installed, run it as a service so it survives reboots and crashes:
sudo systemctl enable nodered.service
sudo systemctl start nodered.service
At this point, Node-RED’s editor is reachable at http://your-server-ip:1880, with no login required by anyone who can reach that port. This is the exact moment to stop and secure it, before connecting anything real to it.
Step 1: Enable authentication on the editor
Generate a password hash:
node-red admin hash-pw
Enter your desired password when prompted, then copy the resulting hash into ~/.node-red/settings.js:
adminAuth: {
type: "credentials",
users: [{
username: "admin",
password: "$2b$08$...", // the hash you just generated
permissions: "*"
}]
}
Restart Node-RED for the change to take effect. One important detail worth knowing: this secures the editor and admin API specifically. It doesn’t automatically protect any HTTP endpoints your own flows expose (an http in node, for example), those need their own protection if they’re handling anything sensitive.
Step 2: Put it behind a reverse proxy with HTTPS
Running the editor over plain HTTP means your login credentials travel unencrypted. An Nginx reverse proxy with a Let’s Encrypt certificate fixes this cleanly:
server {
listen 443 ssl;
server_name nodered.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/nodered.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nodered.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:1880;
proxy_http_version 1.1;
proxy_set_header Upgrade ;
proxy_set_header Connection "upgrade";
proxy_set_header Host ;
}
}
The Upgrade/Connection headers matter specifically here: Node-RED’s editor uses WebSockets for live updates, and without those headers passed through correctly, the editor loads but shows constant “connection lost” errors.
Step 3: Firewall, the simple version
Once Nginx is handling HTTPS on port 443, there’s no reason for port 1880 to be reachable from outside the server at all:
sudo ufw allow 443/tcp
sudo ufw deny 1880/tcp
Access goes through Nginx on 443 only; 1880 stays internal to the server itself.
Optional but worth it: fail2ban for the login page
The built-in adminAuth doesn’t rate-limit failed login attempts on its own. Pairing it with fail2ban watching Nginx’s access logs for repeated 401 responses adds a layer against brute-force attempts against the login form itself, on top of what’s already covered in the VPS Security Hardening Checklist.
What to actually build first
Node-RED’s value isn’t really visible until you connect it to something. The natural first flows on a new install:
- MQTT in/out nodes connecting to your Mosquitto broker, so device data starts flowing into Node-RED’s logic.
- A simple conditional flow: read a sensor topic, check a threshold, publish to a notification topic. This is the smallest useful pattern and the basis for almost everything more complex.
- A dashboard node (the node-red-dashboard package) for a quick visual readout without needing a separate tool yet.
Troubleshooting common problems
“Connection lost” repeatedly in the browser, even though Node-RED is running. Almost always a WebSocket problem through the reverse proxy. Double-check the Upgrade and Connection headers shown in the Nginx config above are present exactly as written; a surprisingly common cause is a generic reverse-proxy config copied from a non-WebSocket-aware example that drops these headers silently.
Editor loads but login never succeeds. Usually a hash mismatch, either the password hash was copied incompletely (truncated when pasted into settings.js) or the file wasn’t saved before restarting the service. Regenerate the hash with node-red admin hash-pw and copy it carefully, including the full string, into the password field.
Flows stop running after a server reboot. Confirm the systemd service is actually enabled, not just started manually: sudo systemctl is-enabled nodered.service should return “enabled”. If it returns “disabled”, flows were only ever running because of a manual start that doesn’t survive a reboot.
MQTT nodes show a grey “disconnected” status. Almost always a broker connection issue rather than a Node-RED problem itself: check the broker’s address, port and credentials in the MQTT node’s configuration match exactly what’s set up on the Mosquitto side, including whether TLS is expected (port 8883) versus plaintext (port 1883).
Organising flows as a project grows
A common pattern for new users is building everything in one large, increasingly tangled flow tab. Node-RED supports multiple tabs (visible along the top of the editor) specifically to avoid this: a reasonable structure is one tab per logical area (doorbell logic, climate monitoring, alerting) rather than one enormous flow. Subflows, a more advanced feature, let you package a repeated pattern (the same threshold-check-and-alert logic used across several different sensors, for example) into a single reusable block, edited once and used everywhere it’s needed.
Backing up flows
Node-RED’s flows live in a single JSON file inside the data directory (~/.node-red/flows.json for a direct install). It’s worth including this in whatever backup or snapshot routine covers the rest of the server, since losing it means rebuilding every flow from scratch. Node-RED’s own editor also supports exporting individual flows or the whole workspace as JSON via the menu, useful for keeping a version history outside the server itself, particularly before making a large change.
A note on the Node-RED Library
Beyond the core editor, Node-RED has a large community library of pre-built nodes for specific integrations (weather APIs, specific hardware platforms, cloud services) installable directly through the palette manager. Worth a quick look before building integration logic from scratch for anything reasonably common, since a maintained community node often already handles edge cases (API rate limits, specific authentication quirks) that would otherwise need discovering the hard way.
Frequently asked questions
Can Node-RED run alongside Home Assistant on the same VPS?
Yes, and it’s a common pairing. Home Assistant handles device integration and a polished UI, Node-RED handles complex cross-device logic Home Assistant’s own automation editor struggles with. They typically connect to each other via MQTT or Home Assistant’s own Node-RED integration nodes.
Is the non-standard-port advice (using something other than 1880) worth following?
It reduces automated scanning noise but isn’t real security on its own, the same logic as changing SSH’s default port. The adminAuth and reverse-proxy steps above are what actually matter; a non-standard port is a minor extra, not a substitute.
How much VPS capacity does Node-RED actually need?
Very little for typical home/small business flows; it’s a lightweight Node.js process. 1 vCPU and 1-2GB of RAM, often shared with Mosquitto on the same small VPS, is realistic for most setups on this site.
What if I want to run Node-RED in Docker instead?
Fully supported via the official node-red Docker image, and arguably cleaner for keeping it isolated from other services. See Docker on a VPS for the general pattern.
How do I update Node-RED to a newer version safely?
Export or back up your flows.json first, then re-run the official installer script, which handles updating Node-RED and its dependencies in place. Reviewing the release notes for any breaking changes before updating production flows is worth the few minutes it takes.
Building error handling into flows from the start
A flow that works perfectly in testing can still fail in production, a device goes offline, an API call times out, an unexpected value arrives. Node-RED’s catch node, attached to a flow tab, captures errors from any node on that tab without needing individual error handling wired into every single node. A common, useful pattern: a catch node feeding into an MQTT publish to a dedicated system/errors topic, so failures become visible in the same dashboard used for everything else, rather than silently disappearing into Node-RED’s own internal debug log where nobody’s likely to notice them in time.
For flows polling external devices or APIs on a timer, a status node (separate from catch, tracking node state changes rather than thrown errors) is useful for the specific case of a device that stops responding without throwing an outright error, the MQTT node example earlier in this guide showing a grey “disconnected” status being exactly this kind of signal worth acting on rather than ignoring.
A genuinely useful starter flow: threshold alerting
Beyond the simple example earlier in this guide, a slightly more complete alerting pattern looks like this: an MQTT in node subscribed to a sensor topic, feeding into a function node checking the value against a threshold, then a second function node implementing basic debounce logic (only alert if the threshold has been crossed for, say, three consecutive readings, not a single noisy spike), before finally publishing to a notification topic. This debounce step is easy to skip when first learning Node-RED and is almost always added later, after the first round of false-positive alerts makes the need for it obvious. Building it in from the start saves that iteration.
Can multiple people edit flows at the same time?
The standard open-source Node-RED editor is single-user by design, concurrent edits from two people can conflict. For teams genuinely needing multi-user collaboration, commercial platforms built on Node-RED (such as FlowFuse) add this, but it’s outside the scope of what most projects on this site need.
Is there a performance cost to having many flow tabs open?
Minimal at the scale most projects on this site operate at; Node-RED loads all flows into memory regardless of which tab is currently visible in the editor, so tab organisation is purely for human readability, not a performance consideration.
What’s the difference between a function node and a built-in node for simple logic?
Built-in nodes (switch, change, and similar) cover most simple conditional logic visually, without writing code, and are usually easier to read at a glance. Function nodes, which use JavaScript, become worthwhile once logic gets complex enough that wiring together many small built-in nodes becomes harder to follow than a few lines of code.
Storing small amounts of state with context
Many flows need to remember something between messages, the previous reading to compare against, a running count, whether an alert has already fired. Node-RED’s context storage handles this without needing an external database for simple cases: flow context shares data across nodes on the same tab, global context shares it across the whole Node-RED instance, and by default this is held in memory but can be configured to persist to disk so it survives a restart. For the debounce pattern described above, context is exactly where the “how many consecutive readings have crossed the threshold” counter would typically live, read and updated by the function node on each message.
Can Node-RED call external APIs, not just MQTT devices?
Yes, the built-in http request node handles this directly, useful for integrating weather data, sending notifications via a messaging service’s API, or pulling data from any system that exposes a REST API, alongside the MQTT-based device integration covered throughout this guide.
Is Node-RED suitable for anything beyond home and small business automation?
Yes, it’s used in genuinely large industrial and commercial deployments too, IBM originally created it with exactly that broader scope in mind. The core security and setup principles in this guide apply at any scale; what typically changes at larger scale is the surrounding infrastructure (clustering, dedicated monitoring) rather than Node-RED’s own configuration.
Does Node-RED have a mobile app for viewing dashboards on the go?
The node-red-dashboard package produces a responsive web interface that works well in a mobile browser without a dedicated native app being necessary, accessible from anywhere the reverse-proxy setup in this guide makes it reachable.