Node-RED Flow-Based Programming Installation

Node-RED is a visual, flow-based programming tool built on Node.js that enables IoT, automation, and API integration through a browser-based editor where you wire together nodes to create workflows. This guide covers deploying Node-RED on Linux, creating flows, integrating with MQTT, building dashboards, exposing HTTP endpoints, and deploying to production.

Prerequisites

  • Ubuntu 20.04/22.04 or CentOS 8/Rocky Linux 8+
  • Node.js 18+ (LTS recommended)
  • At least 512 MB RAM
  • Root or sudo access
  • Port 1880 available

Install Node-RED

Quick install script (Ubuntu/Debian):

# Install Node.js 18 LTS (if not already installed)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

# Verify Node.js version
node --version  # Should be v18.x or higher
npm --version

# Install Node-RED globally
sudo npm install -g --unsafe-perm node-red

# Verify installation
node-red --version

CentOS/Rocky Linux:

curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
sudo dnf install -y nodejs

sudo npm install -g --unsafe-perm node-red

Raspberry Pi / Linux quick install:

# Use the official Node-RED install script (handles Node.js + Node-RED)
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

Start Node-RED:

# Start as current user (development)
node-red

# Access at http://your-server:1880

# Start with specific settings file
node-red --settings /opt/nodered/.node-red/settings.js

Secure and Configure Node-RED

Node-RED is open by default — secure it before exposing to a network:

# Initialize Node-RED (creates ~/.node-red/ config directory)
node-red &
sleep 5
kill %1

# Generate password hash for admin authentication
node -e "console.log(require('bcryptjs').hashSync('admin123', 8))"
# Or use node-red-admin tool:
sudo npm install -g node-red-admin
node-red-admin hash-pw
# Enter your password and copy the hash

# Edit settings file
nano ~/.node-red/settings.js

Key security settings in settings.js:

// ~/.node-red/settings.js
module.exports = {
    // Admin UI authentication
    adminAuth: {
        type: "credentials",
        users: [{
            username: "admin",
            password: "$2b$08$YourHashedPasswordHere",  // bcrypt hash
            permissions: "*"
        }]
    },

    // HTTP port
    uiPort: 1880,

    // Bind to specific interface only
    uiHost: "127.0.0.1",   // Only localhost (use with Nginx proxy)

    // Allow CORS for dashboard
    httpAdminCors: {
        origin: "https://nodered.example.com",
        methods: "GET,PUT,POST,DELETE"
    },

    // Security headers
    httpNodeCors: {
        origin: "*",
        methods: "GET,PUT,POST,DELETE"
    },

    // Logging
    logging: {
        console: {
            level: "info",
            metrics: false,
            audit: false
        }
    },

    // Function node timeout
    functionTimeout: 30,

    // Projects feature
    editorTheme: {
        projects: {
            enabled: true
        }
    }
};

Create Your First Flow

Node-RED flows are created in the browser editor at http://your-server:1880:

Basic HTTP flow:

  1. Drag an http in node to the canvas
  2. Set Method: GET, URL: /hello
  3. Drag a function node and connect it
  4. Double-click the function node and add:
    msg.payload = { message: "Hello from Node-RED!", time: new Date().toISOString() };
    msg.statusCode = 200;
    return msg;
    
  5. Drag an http response node and connect it
  6. Click Deploy
  7. Test: curl http://localhost:1880/hello

Flow via JSON import (import via menu > Import):

[
    {
        "id": "inject1",
        "type": "inject",
        "name": "Every 30s",
        "repeat": "30",
        "wires": [["function1"]]
    },
    {
        "id": "function1",
        "type": "function",
        "name": "Check time",
        "func": "msg.payload = { hour: new Date().getHours(), minute: new Date().getMinutes() };\nreturn msg;",
        "wires": [["debug1"]]
    },
    {
        "id": "debug1",
        "type": "debug",
        "name": "Output",
        "active": true,
        "wires": []
    }
]

MQTT Integration

Connect Node-RED to an MQTT broker to receive IoT sensor data:

# First, configure MQTT broker credentials in Node-RED:
# 1. Drag mqtt-in node to canvas
# 2. Double-click > click pencil icon next to Server
# 3. Server: localhost, Port: 1883
# 4. Security tab: Username + Password
# 5. Click Update > Done

Example sensor processing flow:

// Function node: Parse and validate sensor data
const data = JSON.parse(msg.payload);

if (!data.temperature || !data.device_id) {
    node.warn("Invalid sensor data received");
    return null;  // Drop the message
}

// Convert Fahrenheit to Celsius if needed
if (data.unit === 'F') {
    data.temperature = (data.temperature - 32) * 5/9;
    data.unit = 'C';
}

// Add processing timestamp
data.processed_at = new Date().toISOString();

msg.payload = data;
msg.topic = `sensors/${data.device_id}/processed`;

return msg;

Subscribe to MQTT topic and forward to HTTP API:

[
    {
        "id": "mqtt-in",
        "type": "mqtt in",
        "topic": "home/+/temperature",
        "qos": "1",
        "broker": "mqtt-broker-config-id",
        "wires": [["process-fn"]]
    },
    {
        "id": "process-fn",
        "type": "function",
        "func": "const d = JSON.parse(msg.payload);\nmsg.payload = JSON.stringify({ device: msg.topic.split('/')[1], temp: d.value, ts: Date.now() });\nmsg.headers = { 'Content-Type': 'application/json' };\nreturn msg;",
        "wires": [["http-post"]]
    },
    {
        "id": "http-post",
        "type": "http request",
        "method": "POST",
        "url": "http://localhost:3000/api/sensors",
        "wires": [["debug"]]
    }
]

Dashboard Nodes

Install and use the Node-RED Dashboard for visualization:

# Install dashboard nodes from the Palette Manager:
# Menu > Manage Palette > Install > search "node-red-dashboard"

# Or install via npm
cd ~/.node-red
npm install node-red-dashboard

# Restart Node-RED
# Dashboard UI available at: http://your-server:1880/ui

Dashboard flow example:

// gauge node configuration (via UI):
// Type: Gauge, Label: CPU Temperature
// Min: 0, Max: 100, Colour gradient: green-yellow-red
// Group: System Monitor

// Chart node:
// Type: Line chart
// Label: Temperature Over Time
// X-Axis: Last 1 hour

// In function node, format data for gauge:
msg.payload = context.global.get('cpu_temp') || 45;
return msg;

HTTP API Endpoints

Create REST API endpoints with Node-RED:

// HTTP In node: POST /api/trigger
// Function node:
const body = msg.payload;

if (!body.action) {
    msg.statusCode = 400;
    msg.payload = { error: "Missing 'action' field" };
    return msg;
}

// Perform action based on request
switch (body.action) {
    case 'restart_service':
        // Trigger service restart flow
        node.send([msg, null]);
        msg.payload = { status: 'triggered', action: body.action };
        break;
    case 'send_alert':
        node.send([null, msg]);
        msg.payload = { status: 'alert_sent' };
        break;
    default:
        msg.statusCode = 404;
        msg.payload = { error: 'Unknown action' };
}

return msg;

Secure an API endpoint with a token check:

// Add before processing in API flows
const authHeader = msg.req.headers['authorization'];
const expectedToken = env.get('API_TOKEN');  // Set via environment variable

if (!authHeader || authHeader !== `Bearer ${expectedToken}`) {
    msg.statusCode = 401;
    msg.payload = { error: 'Unauthorized' };
    node.send([null, msg]);  // Second output goes to http response
    return;
}

// Proceed with authenticated request
return [msg, null];

Custom Nodes

Install community nodes from the palette:

# Common useful nodes
cd ~/.node-red
npm install node-red-contrib-influxdb    # InfluxDB integration
npm install node-red-contrib-postgresql  # PostgreSQL
npm install node-red-node-email          # Email sending
npm install node-red-contrib-telegrambot # Telegram bot
npm install node-red-contrib-cron-plus   # Advanced scheduling

# List installed nodes
npm list --depth=0 | grep node-red

Create a simple custom node:

mkdir -p ~/.node-red/nodes/my-validator
cat > ~/.node-red/nodes/my-validator/my-validator.js << 'EOF'
module.exports = function(RED) {
    function ValidatorNode(config) {
        RED.nodes.createNode(this, config);
        const node = this;
        const field = config.field || 'payload';
        
        node.on('input', function(msg, send, done) {
            const value = msg[field];
            
            if (value === undefined || value === null) {
                node.warn(`Field ${field} is missing or null`);
                done();
                return;
            }
            
            msg.valid = true;
            send(msg);
            done();
        });
    }
    RED.nodes.registerType("my-validator", ValidatorNode);
};
EOF

cat > ~/.node-red/nodes/my-validator/my-validator.html << 'EOF'
<script type="text/javascript">
    RED.nodes.registerType('my-validator', {
        category: 'function',
        color: '#a6bbcf',
        defaults: { name: {value:""}, field: {value:"payload"} },
        inputs: 1,
        outputs: 1,
        label: function() { return this.name || "validator"; }
    });
</script>
EOF

Production Deployment

# Create a dedicated user
sudo useradd -r -m -d /opt/nodered -s /bin/bash nodered
sudo -u nodered npm install -g node-red

# Create systemd service
cat > /etc/systemd/system/node-red.service << 'EOF'
[Unit]
Description=Node-RED Flow-Based Programming
After=network.target

[Service]
Type=simple
User=nodered
WorkingDirectory=/opt/nodered
ExecStart=/usr/bin/node-red --settings /opt/nodered/.node-red/settings.js
Restart=on-failure
RestartSec=5
KillSignal=SIGINT

# Environment variables
Environment="NODE_ENV=production"
Environment="API_TOKEN=your-secret-api-token"
Environment="MQTT_PASSWORD=mqttpassword"

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now node-red

# Configure Nginx reverse proxy
cat > /etc/nginx/sites-available/node-red << 'EOF'
server {
    listen 443 ssl http2;
    server_name nodered.example.com;

    ssl_certificate /etc/letsencrypt/live/nodered.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nodered.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:1880;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400;
    }
}
EOF

sudo ln -s /etc/nginx/sites-available/node-red /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Troubleshooting

Node-RED won't start after installing a node:

# Check logs for errors
node-red --verbose 2>&1 | head -50
sudo journalctl -u node-red -n 50

# Remove the problematic node
cd ~/.node-red
npm uninstall node-red-contrib-problematic-node

Flows not deploying:

# Check browser console for JavaScript errors (F12)
# Verify settings.js syntax
node -e "require('./settings.js')" ~/.node-red/settings.js

# Check disk space
df -h /opt/nodered

MQTT messages not received:

# Test MQTT connection from command line
mosquitto_sub -h localhost -p 1883 -u homeassistant -P mqttpassword -t "home/#" -v

# Check Node-RED debug tab for connection errors
# Verify MQTT broker config in the node settings

Dashboard not loading:

# Verify node-red-dashboard is installed
ls ~/.node-red/node_modules | grep dashboard

# Check dashboard URL
curl http://localhost:1880/ui

# Reinstall dashboard
cd ~/.node-red && npm install node-red-dashboard
node-red-admin init

Conclusion

Node-RED's visual flow editor dramatically reduces the complexity of integrating IoT devices, APIs, and automation workflows — tasks that would require significant boilerplate code become a matter of wiring nodes together. Deploy it as a systemd service behind an Nginx reverse proxy with authentication for production use, leverage the extensive palette of community nodes for integrations, and use the Function node for custom JavaScript logic when built-in nodes are insufficient.