Node-RED: Visual Automation That Connects Everything in Your Homelab
Your homelab has a temperature sensor publishing data over MQTT, a media server that should pause downloads when bandwidth gets tight, a security camera that needs to trigger notifications, and a dashboard that should display it all in real time. You could write a Python script for each connection, maintain a cron job for each check, and debug a different logging format for each piece. Or you could wire it all together visually in an afternoon.
Photo by Logan Voss on Unsplash
Node-RED is a flow-based programming tool that lets you connect hardware devices, APIs, and services by dragging wires between nodes on a canvas. Originally built by IBM for IoT prototyping, it has grown into a general-purpose automation engine with over 5,000 community-contributed nodes covering everything from MQTT brokers to Slack bots to machine learning pipelines.
Where tools like n8n focus on SaaS-to-SaaS integrations and Home Assistant focuses on smart home control, Node-RED sits in a unique spot: it is equally comfortable talking to a temperature sensor on a Raspberry Pi, processing webhooks from GitHub, and building a custom dashboard with live charts. If your automation needs cross the boundary between physical hardware and software services, Node-RED is probably the right tool.

Node-RED vs the Alternatives
| Feature | Node-RED | n8n | Home Assistant Automations | Huginn |
|---|---|---|---|---|
| Primary focus | IoT + general automation | SaaS workflow automation | Smart home control | Web scraping + agents |
| Interface | Flow canvas (wires + nodes) | Visual workflow editor | YAML + visual editor | Web UI (agent config) |
| Protocol support | MQTT, TCP, UDP, WebSocket, serial | HTTP/webhooks | Zigbee, Z-Wave, MQTT, HTTP | HTTP |
| Community extensions | 5,000+ nodes | 400+ integrations | 2,000+ integrations | ~50 agent types |
| Custom code | JavaScript (function nodes) | JavaScript + Python | Jinja2 templates, Python | Ruby |
| Dashboard/UI | Built-in dashboard nodes | No | Built-in dashboards | No |
| Hardware access | Serial, GPIO, USB | No | Via add-ons | No |
| Real-time data | Yes (WebSocket, MQTT streams) | Polling/webhook-based | Yes (state changes) | Polling-based |
| Self-hosted | Yes (free, Apache 2.0) | Yes (free, source-available) | Yes (free, Apache 2.0) | Yes (free, MIT) |
| Learning curve | Low-Medium | Low | Medium (YAML-heavy) | High |
| Resource usage | ~100-250 MB RAM | ~200-400 MB RAM | ~500 MB-2 GB RAM | ~500 MB RAM |
When to choose Node-RED
Pick Node-RED when your automations involve MQTT, hardware sensors, serial devices, or real-time data streams. It excels at IoT scenarios where you need to subscribe to a topic, transform a message, and route it somewhere else in milliseconds. The function nodes give you full JavaScript when visual wiring is not enough, and the built-in dashboard lets you create monitoring UIs without a separate tool.
Node-RED is also the right choice when you want a lightweight automation layer that does not need a database. Out of the box, it stores everything as flat JSON files. You can have it running in 30 seconds on a Raspberry Pi Zero.
When to choose something else
If your primary need is connecting SaaS products (Slack to Google Sheets, Stripe webhooks to a database), n8n has better pre-built integrations and a more polished credential management system.
If you are automating a smart home with Zigbee/Z-Wave devices and want presence detection, room-aware logic, and a mature mobile app, Home Assistant is purpose-built for that. Node-RED is a popular companion to Home Assistant (there is an official add-on), but it is not a replacement for the device management layer.
If you need autonomous web scraping agents that monitor websites and act on changes over days or weeks, Huginn is designed around that agent model. Node-RED can do web scraping, but it was not designed for long-running stateful agents.
Installation via Docker Compose
# docker-compose.yml
services:
nodered:
image: nodered/node-red:latest
ports:
- "1880:1880"
volumes:
- nodered_data:/data
environment:
- TZ=America/Los_Angeles
restart: unless-stopped
volumes:
nodered_data:
docker compose up -d
Node-RED is available at http://your-server:1880. The flow editor loads immediately with no setup wizard.
Adding an MQTT broker
Most Node-RED deployments pair with an MQTT broker. Add Mosquitto to the same compose file:
# docker-compose.yml
services:
nodered:
image: nodered/node-red:latest
ports:
- "1880:1880"
volumes:
- nodered_data:/data
environment:
- TZ=America/Los_Angeles
depends_on:
- mosquitto
restart: unless-stopped
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
volumes:
- mosquitto_data:/mosquitto/data
- mosquitto_config:/mosquitto/config
restart: unless-stopped
volumes:
nodered_data:
mosquitto_data:
mosquitto_config:
Create a Mosquitto config before starting:
mkdir -p mosquitto_config
cat > mosquitto_config/mosquitto.conf <<EOF
listener 1883
allow_anonymous false
password_file /mosquitto/config/passwords
persistence true
persistence_location /mosquitto/data
EOF
# Create a password file
docker run --rm -v $(pwd)/mosquitto_config:/mosquitto/config \
eclipse-mosquitto:2 mosquitto_passwd -c /mosquitto/config/passwords myuser
In Node-RED, configure the MQTT nodes to connect to mosquitto:1883 (using the Docker service name) with the credentials you just created.
Core Features
The flow editor
The flow editor is a browser-based canvas where you build automations by dragging nodes from the palette, dropping them onto the workspace, and wiring outputs to inputs. Every node has a specific job: receive an MQTT message, parse JSON, apply a filter, send an HTTP request, write to a file.
Flows read left to right. A trigger on the left fires a message, processing nodes in the middle transform it, and output nodes on the right deliver the result. You can branch flows, merge them, add delays, and route messages conditionally.
Double-click any node to configure it. Click Deploy to push your changes live. Node-RED hot-reloads flows without restarting the runtime.
MQTT integration
MQTT is Node-RED's strongest protocol. The mqtt-in and mqtt-out nodes handle subscriptions and publishing with QoS levels, retained messages, and wildcard topic subscriptions.
A typical IoT flow:
mqtt-in (sensors/temperature/#)
→ json (parse payload)
→ switch (route by sensor ID)
→ change (extract value)
→ dashboard chart (display)
→ mqtt-out (alerts/high-temp) [if threshold exceeded]
This subscribes to all temperature sensor topics, parses the incoming JSON, routes messages by sensor, displays values on a dashboard, and publishes an alert if a reading exceeds a threshold. Building this takes about five minutes in the editor.
HTTP endpoints
Node-RED can create REST APIs with the http-in and http-response nodes. This is useful for building webhook receivers, simple APIs, or proxy endpoints:
http-in (POST /api/webhook)
→ function (validate + transform)
→ http request (forward to another service)
→ http-response (return 200)
You get a fully functional HTTP endpoint without writing a server. This is particularly handy for accepting webhooks from services that do not support MQTT.
Dashboard UI nodes
Install node-red-dashboard (or the newer node-red-dashboard-2 for the FlexDash-based version) from the palette manager to get UI widgets:
- Gauges for live sensor readings
- Charts for time-series data
- Buttons for triggering actions
- Sliders for adjusting set points
- Text inputs for user configuration
- Notification popups for alerts
- Template nodes for custom HTML/CSS
The dashboard is accessible at http://your-server:1880/ui and updates in real time over WebSocket. No page refreshes, no polling.
To install from the editor: go to Menu > Manage palette > Install, search for node-red-dashboard, and click install.
Function nodes
When visual wiring is not enough, function nodes give you full JavaScript:
// Example: aggregate sensor readings over 5 minutes
const readings = context.get('readings') || [];
readings.push(msg.payload.temperature);
if (readings.length >= 30) {
const avg = readings.reduce((a, b) => a + b) / readings.length;
context.set('readings', []);
msg.payload = { average: avg, samples: 30 };
return msg;
}
context.set('readings', readings);
return null; // suppress output until we have enough samples
Function nodes have access to three context scopes:
context— local to that node instanceflow— shared across the current flow tabglobal— shared across the entire Node-RED instance
This is where Node-RED bridges the gap between no-code and code. You drag wires for the simple stuff and drop into JavaScript when you need logic that would be awkward to express as wired nodes.
5,000+ community nodes
The palette manager lets you install community nodes directly from npm. Some popular ones:
node-red-contrib-home-assistant-websocket— full Home Assistant integrationnode-red-contrib-influxdb— write to InfluxDB for time-series storagenode-red-contrib-telegrambot— Telegram bot integrationnode-red-node-email— send and receive emailnode-red-contrib-discord— Discord bot supportnode-red-contrib-modbus— Modbus TCP/RTU for industrial devicesnode-red-contrib-cron-plus— advanced scheduling with cron expressionsnode-red-contrib-postgresql— PostgreSQL queries
Install any of these from the palette manager or by running npm install inside the Node-RED data directory.
Like what you're reading? Subscribe to Self-Hosted Weekly — free weekly guides in your inbox.
Advanced Usage
Subflows
Subflows are reusable flow components. If you find yourself building the same pattern repeatedly (parse MQTT, check threshold, send alert), wrap it in a subflow:
- Select the nodes you want to reuse
- Go to Menu > Subflows > Selection to Subflow
- Define input/output ports and configurable properties
- Use the subflow like any other node across your flows
Subflows keep complex deployments maintainable. Instead of 50 copies of the same alert logic, you have one subflow and 50 instances.
Environment variables
Node-RED supports environment variables for configuration that should not be hardcoded:
# docker-compose.yml
environment:
- TZ=America/Los_Angeles
- MQTT_BROKER=mosquitto
- MQTT_PORT=1883
- [email protected]
Reference them in function nodes with env.get("MQTT_BROKER") or in node configuration fields using ${MQTT_BROKER} syntax.
This lets you use the same flow definitions across development and production environments by changing environment variables instead of editing flows.
Projects feature (Git integration)
Enable the projects feature to version-control your flows with Git:
- Set
editorTheme.projects.enabled: truein yoursettings.js - Restart Node-RED
- The editor prompts you to create or clone a project
With projects enabled, Node-RED tracks your flows, credentials (encrypted), and package dependencies in a Git repository. You can push to a remote, pull changes, view diffs, and manage branches from inside the editor.
To enable in Docker, mount a custom settings file:
volumes:
- nodered_data:/data
- ./settings.js:/data/settings.js
And in settings.js:
module.exports = {
editorTheme: {
projects: {
enabled: true
}
}
}
Custom node development
If the 5,000+ community nodes do not cover your use case, building a custom node involves two files:
- An HTML file defining the editor UI (configuration form, help text, appearance)
- A JavaScript file defining the runtime behavior
The official documentation walks through the process. A minimal custom node takes about 30 minutes to build. Package it as an npm module to share with the community or install across your own instances.
Credential management
Node-RED encrypts credentials (passwords, API keys, tokens) stored in node configurations using a key derived from credentialSecret in settings.js. Set this to a strong random value:
module.exports = {
credentialSecret: "your-random-secret-key-here"
}
Generate one with openssl rand -hex 32. Without this setting, credentials are encrypted with a default key, which is better than plaintext but not secure against someone with access to your flow files.
When exporting flows, credentials are excluded by default. This prevents accidental leaks when sharing flows with others.
Resource Requirements
| Setup | CPU | RAM | Storage | Notes |
|---|---|---|---|---|
| Small (< 20 flows, no dashboard) | 1 core | 128 MB | 100 MB | Runs on a Pi Zero |
| Medium (20-100 flows, dashboard, MQTT) | 1-2 cores | 256-512 MB | 500 MB | Typical homelab |
| Large (100+ flows, heavy function nodes) | 2+ cores | 512 MB-1 GB | 1 GB+ | Add InfluxDB for time-series |
| With Mosquitto broker | +0 cores | +20-50 MB | +100 MB | Mosquitto is extremely lightweight |
Node-RED is one of the lighter automation tools you can run. The Node.js runtime starts at around 80 MB and grows with the number of active flows and installed nodes. The dashboard adds another 30-50 MB when clients are connected.
Honest Limitations
Flow spaghetti is real
Node-RED's visual editor becomes hard to navigate when you have complex logic. Fifty nodes with crossing wires on a single tab is not fun to debug or maintain. The mitigation is discipline: use subflows, organize with named tabs, add comment nodes, and resist the urge to put everything on one canvas.
Debugging is not always intuitive
The debug sidebar shows message payloads flowing through your wires, which is great for simple flows. But when you have parallel branches, asynchronous callbacks in function nodes, and messages firing every second from MQTT, it can be hard to trace which message caused which behavior. Adding debug nodes at strategic points helps, but it is not the same as stepping through code with a debugger.
Node.js memory behavior
Node-RED runs on Node.js, which has a garbage collector that does not always release memory aggressively. Long-running function nodes that accumulate data in context, or flows that process large payloads (images, files), can cause memory to climb over time. Monitor with docker stats and set container memory limits as a safety net.
Security needs explicit attention
Out of the box, Node-RED has no authentication. Anyone who can reach port 1880 can modify your flows. You must enable admin authentication in settings.js:
module.exports = {
adminAuth: {
type: "credentials",
users: [{
username: "admin",
password: "$2b$08$your-bcrypt-hash-here",
permissions: "*"
}]
}
}
Generate the password hash with:
docker exec nodered npx node-red-admin hash-pw
Beyond admin auth, also consider:
- Putting Node-RED behind a reverse proxy with HTTPS
- Restricting access to your local network or VPN
- Enabling
httpNodeAuthif you expose HTTP endpoints publicly - Disabling the palette manager in production to prevent unauthorized node installation
No built-in execution history
Unlike n8n, Node-RED does not keep a log of every flow execution. There is no "show me what ran at 3 AM and what data it processed." You can add logging with debug nodes or write to an external database, but it is not built in. For critical automations, build explicit logging into your flows.
Practical Tips
Start with one flow and expand. Wire up your most annoying manual task first. Get comfortable with the editor, then tackle more complex automations.
Use link nodes for clean routing. Link-in and link-out nodes let you jump between flow tabs without drawing wires across the entire canvas. Name them clearly.
Set up a catch-all error handler. Add a catch node to each flow tab that sends errors to your notification channel. Without this, errors vanish silently.
Use context stores wisely. Flow and global context persist in memory by default (lost on restart). If you need persistence, configure a file-based or Redis-backed context store in settings.js:
contextStorage: {
default: { module: "memory" },
persistent: { module: "localfilesystem" }
}
Then use flow.get('key', 'persistent') and flow.set('key', value, 'persistent') in function nodes.
Back up your flows regularly. The flow files live in /data inside the container. Your Docker volume handles persistence, but export flows periodically as insurance. You can do this from the editor (Menu > Export > All flows) or by copying the flows.json file from the volume.
Pin your Docker image version. Use nodered/node-red:4.0 instead of latest in production. Node-RED major version upgrades can break community nodes. Upgrade deliberately, not accidentally.
Pair with InfluxDB and Grafana for time-series. Node-RED is great at collecting and routing data but not designed for long-term storage or complex visualization. Send your data to InfluxDB with the contrib node, then build dashboards in Grafana for historical analysis. Use the Node-RED dashboard for real-time monitoring and quick controls.
Resources
- Node-RED official documentation — comprehensive guides, tutorials, and API reference
- Flow library — community-shared flows and nodes
- Node-RED cookbook — practical recipes for common tasks
- Node-RED forums — active community for troubleshooting
- GitHub repository — source code and issue tracker
- Awesome Node-RED — curated list of resources, nodes, and flows
The Bottom Line
Node-RED fills a specific gap in the self-hosted automation landscape: it connects the physical world to the digital one with minimal friction. If your automations involve MQTT, hardware sensors, serial devices, or real-time data streams, Node-RED handles those natively in a way that SaaS-focused tools simply do not. The visual flow editor makes simple integrations approachable, function nodes provide an escape hatch to full JavaScript when you need it, and the resource footprint is small enough to run on hardware you probably already have sitting in a drawer. Pair it with an MQTT broker and you have an event-driven automation backbone for your entire homelab.
