Why self-host your automation engine
No-code/low-code automation platforms like Zapier or Make bill per execution and impose quotas that quickly balloon. By self-hosting n8n or Node-RED on a VPS, you remove these limits: unlimited number of executions, data that never leaves your server, and full access to community nodes. n8n is oriented toward application integrations (CRM, databases, AI, webhooks) with a visual workflow logic close to Make. Node-RED, from IBM, manipulates JSON messages via an event-flow system, perfect for wiring up MQTT sensors, transforming payloads or orchestrating hardware. Both run in a container and are driven from a browser.
What you gain by self-hosting
- Unlimited executions with no per-task billing, unlike automation SaaS.
- Data and secrets (API tokens, credentials) stored exclusively on your VPS.
- Access to community nodes/integrations not available in restricted cloud offerings.
- The ability to run custom code (JavaScript, Python) with no sandbox restriction.
- Direct integration with your internal services via the private Docker network.
- Full control of versions and updates, with no imposed price change.
| Criterion | n8n | Node-RED |
|---|---|---|
| Main use case | SaaS API integration, business workflows | IoT, MQTT, real-time stream processing |
| Execution model | Triggered workflow (cron, webhook, trigger) | Message-oriented event flow |
| Integration catalog | 400+ ready-to-use application nodes | Node library via the community palette |
| Custom code | Built-in Code nodes (JS/Python) | Function nodes in JavaScript |
| Database | SQLite by default, PostgreSQL recommended | JSON files on disk |
| Recommended RAM | 1 to 2 GB | 512 MB to 1 GB |
| License | Sustainable Use License (fair-code) | Apache 2.0 |
| Learning curve | Accessible, geared toward non-developers | Flow logic, more technical |
Prerequisites depending on the tool chosen
For n8n, plan for a VPS with 2 vCPU and 2 GB of RAM, and above all PostgreSQL rather than SQLite as soon as you exceed a few active workflows: SQLite locks the database under concurrent load. Count on 5 GB of disk for the database and the execution binaries. Node-RED is more frugal: 1 vCPU and 1 GB of RAM are enough, its flows being stored in simple JSON files. In both cases: Docker Compose v2, a dedicated subdomain (n8n.mydomain.com or flows.mydomain.com) and an HTTPS reverse proxy are indispensable, because these interfaces expose secrets and must never run over plain HTTP.
Deploy n8n (or Node-RED) on your VPS
Provision Docker and the network
On the VPS, create a dedicated Docker network: docker network create automation. It will isolate n8n/Node-RED from the database and the reverse proxy.
Start PostgreSQL for n8n
In the docker-compose, add a postgres:16 service with a persistent volume and credentials as environment variables. For Node-RED, this step is unnecessary: a data:/data volume is enough.
Configure the application container
For n8n, use the docker.n8n.io/n8nio/n8n image with N8N_HOST, WEBHOOK_URL=https://n8n.mydomain.com/ and DB_TYPE=postgresdb. For Node-RED, the nodered/node-red:latest image exposed on port 1880 is enough.
Start and verify
Run docker compose up -d. n8n listens on 5678, Node-RED on 1880. Check the logs with docker compose logs -f and confirm that the database is properly connected for n8n.
Secure behind a reverse proxy
Route the traffic through Caddy or Traefik with an automatic Let's Encrypt certificate. Enable authentication: N8N_BASIC_AUTH_ACTIVE=true for n8n, and the adminAuth directive in settings.js for Node-RED. Without this, the editor is publicly accessible.
Set up backups
Schedule a daily pg_dump for the n8n database, or a simple snapshot of the /data volume for Node-RED, deposited on external storage via cron.
For n8n in production, enable queue mode with Redis and separate worker containers: the main process then only orchestrates, while the workers execute the nodes in parallel. You thus absorb webhook spikes without blocking the editor. On the Node-RED side, isolate the Function nodes in a VM context with a restricted functionGlobalContext to prevent a poorly written flow from crashing the whole runtime.