I’ve been running Node-RED in my homelab for about two years now, mostly for connecting sensors to Home Assistant and triggering automations when things happen. It works well for that. The visual flow interface is genuinely useful when you’re wiring together five different services and don’t want to think in YAML, though I’ll admit the learning curve is steeper than the marketing suggests. This is the Docker Compose setup I settled on after trying a few variations that either ate memory or lost their flows on restart.
Why Docker Compose for Node-RED
Node-RED runs fine standalone, but Docker gives you isolation, version pinning, and the ability to tear it down and rebuild without losing anything. I’m not precious about it โ plenty of people run it on bare metal on a Raspberry Pi โ but once you have Docker running other services, adding one more container is trivial. The main downside is that talking to local hardware (serial ports, GPIO) requires explicit volume mounts and device sharing, which adds friction.
I also wanted persistent storage for flows and configuration, and I wanted to sit it behind a reverse proxy with basic auth instead of relying on Node-RED’s own authentication, which I don’t fully trust. That setup is what I’m sharing below.
The Docker Compose File, Explained
version: '3.8'
services:
node-red:
image: nodered/node-red:3.1.5
container_name: node-red
restart: unless-stopped
ports:
- "127.0.0.1:1880:1880"
environment:
- NODE_RED_ENABLE_PROJECTS=false
- NODE_RED_ENABLE_SAFE_MODE=true
- NODE_RED_CREDENTIAL_SECRET=${CREDENTIAL_SECRET}
- TZ=Europe/London
volumes:
- node-red-data:/data
- /etc/localtime:/etc/localtime:ro
networks:
- homelab
labels:
- "com.example.description=Node-RED flow editor"
- "traefik.enable=true"
- "traefik.http.routers.node-red.rule=Host(`node-red.lab.local`)"
- "traefik.http.routers.node-red.entrypoints=websecure"
- "traefik.http.routers.node-red.tls=true"
- "traefik.http.middlewares.node-red-auth.basicauth.users=admin:$$2y$$10$$redacted_bcrypt_hash"
- "traefik.http.routers.node-red.middlewares=node-red-auth"
- "traefik.http.services.node-red.loadbalancer.server.port=1880"
volumes:
node-red-data:
driver: local
networks:
homelab:
external: true
Let me walk through the important bits. The image is pinned to 3.1.5 rather than latest because I learned the hard way that Node-RED sometimes ships breaking changes in minor versions, especially around credential encryption. You don’t want your flows to suddenly not decrypt on a casual update.
The port binding is 127.0.0.1:1880:1880, not 0.0.0.0:1880. This means Node-RED only listens on localhost, which forces all external traffic through Traefik. That’s the reverse proxy handling auth and HTTPS. If you’re not using Traefik, this won’t make sense, but the principle is sound: don’t expose Node-RED directly to the network.
NODE_RED_ENABLE_PROJECTS=false disables the projects feature, which I don’t use and which adds complexity. NODE_RED_ENABLE_SAFE_MODE=true means if something in your flows crashes, Node-RED will start in safe mode rather than going down completely. That’s saved me more than once.
NODE_RED_CREDENTIAL_SECRET is the encryption key for your credentials. If you don’t set this, Node-RED generates a random one on first run, which means you can’t migrate between containers or hosts. I use a 32-character random string from openssl rand -base64 24 and keep it in my .env file.
The volumes line is the critical part. node-red-data:/data mounts a named Docker volume to persist your flows, packages, and credentials. Without this, restarting the container erases everything. The second volume, /etc/localtime:/etc/localtime:ro, syncs the host’s timezone so timestamps in logs and flow outputs aren’t in UTC when your system isn’t.
The Traefik labels are what wire this up to your reverse proxy. If you use Nginx Proxy Manager or something else, your labels will look different, but the concept is the same: define a route, bind it to basic auth middleware, and point it at the container. The basicauth users line is a bcrypt hash. You can generate one with htpasswd -B or various online tools. Yes, it’s ugly in the config, but it works.
Environment Variables and Secrets
Your .env file should look something like this:
CREDENTIAL_SECRET=your-32-character-random-string-here
That’s it. I keep it minimal. Node-RED has about thirty other environment variables you can set, but most of them aren’t worth touching unless you’re doing something unusual like running behind a non-standard proxy or disabling authentication entirely (don’t do that).
One thing I did experiment with was setting NODE_PATH=/data/node_modules to allow custom packages, but that’s outside the scope of a basic setup. The default package management through the UI is fine for most flows.
Adding AI Nodes to Your Setup
This is where Node-RED gets interesting if you have Ollama or OpenAI access in your homelab. The UI has a palette manager where you can install community nodes. The Ollama node and the OpenAI node are both maintained and reasonably stable. Just search for them in the palette and click install.
I use the Ollama node to trigger summarization of sensor data without touching my main LLM service. There’s a slight latency compared to a direct API call, but it’s clean in the flow editor. One caveat: if your Ollama service isn’t running or is slow, the entire flow will hang unless you add a timeout node upstream. I learned that the hard way during a library update that took longer than expected.
To add a node, just restart the container after installing it, or Node-RED will complain about missing dependencies on the next flow reload. The nodes persist in the volume, so you only need to do this once per installation.
Networking and Service Communication
Node-RED lives on a Docker network called homelab (change the name if you use something else). Any other service on that network is reachable by container name. So if you have a Home Assistant container named home-assistant, you can point a webhook or HTTP request node at http://home-assistant:8123 and it will work. This is cleaner than trying to use IP addresses.
For services on the host machine, use the gateway IP or host.docker.internal if you’re on Mac or Windows. On Linux, you need to either add --add-host=host.docker.internal:host-gateway to the Docker run command or use the host’s actual IP. It’s annoying. Most people just use the IP.
If you’re calling external APIs, you probably want to store your API keys in Node-RED’s credential system rather than hardcoding them in flows. Inject them via context variables or use the credentials UI. This keeps them out of your git history if you version control your flows.
Common Issues and What I’d Do Differently
The first time I set this up, I didn’t mount the timezone file and had all my timestamps in UTC. Seems minor until you’re debugging why an automation fired at the wrong time. The TZ environment variable works too, but the volume mount is more reliable across different host configurations.
Memory usage is fine for small to medium flows. A single instance uses about 100-150 MB idle, and stays reasonable unless you’re running something that processes thousands of events per minute. If you’re doing that, you’ve probably outgrown Node-RED anyway and should be thinking about a proper streaming platform.
I initially tried to bind Node-RED’s port directly to the network instead of through Traefik. This worked until I wanted to add authentication, and then it became a mess. The reverse proxy approach is better in every way, even if it adds a container. You get TLS, rate limiting, and multiple backends for free.
One thing that still surprises me is how long some flows take to load if you have dozens of them in a single workspace. The editor doesn’t seem to lazy-load. Breaking them into separate files helps, but that’s a workflow thing, not a configuration thing.
Testing Your Setup
After spinning up the container with docker-compose up -d, wait a few seconds and check the logs:
docker-compose logs -f node-red
You’re looking for lines that say Started flows and don’t see any error messages about missing nodes or credential decryption failures. If you see Error: Cannot decrypt credentials, your CREDENTIAL_SECRET is wrong or missing.
Then hit your reverse proxy URL (mine is https://node-red.lab.local) and you should get a basic auth prompt, followed by the Node-RED UI. If the UI loads but you see a blank canvas, the flows didn’t load. Check the container logs again.
Create a simple test flow: inject node feeding into a debug node, then deploy and watch the console. If you see your message in the debug panel, everything is connected properly.
What still feels a bit brittle to me is the credential encryption. If your CREDENTIAL_SECRET ever changes, all stored credentials become unreadable. I keep mine written down in my password manager specifically because I never want to discover this the hard way. A better approach would be to use HashiCorp Vault or something, but that’s overkill for a homelab unless you’re already running it.
FAQ
Can Node-RED run on a Raspberry Pi?
Yes. Node-RED runs fine on Pi 3 and newer, though a Pi 4 with at least 2GB RAM is more comfortable if you’re running multiple flows. On older hardware, the UI can feel sluggish when you have many nodes, but it works.
How much RAM does Node-RED actually need?
About 100-150 MB idle for a basic installation. Heavy flows with lots of active timers or event processing can push that to 300-400 MB. If you’re consistently using more than 500 MB, you probably have a memory leak in a custom node or an inefficient flow.
Do I have to use Traefik for reverse proxy auth?
No. You can use Nginx Proxy Manager, Caddy, or plain Nginx. The principle is the same: listen on the network, add basic or OAuth auth, proxy to Node-RED on localhost. Traefik is just what I use. Node-RED’s own auth works too, but I prefer offloading it.
What if I want to use Node-RED with Home Assistant?
Put both on the same Docker network and use the Home Assistant Companion node in Node-RED. Or call Home Assistant’s REST API directly via HTTP request nodes. Both work fine. The companion node is cleaner if you’re doing a lot of back-and-forth.
How do I back up my Node-RED flows?
The named volume node-red-data contains everything. You can export flows from the UI or just back up the Docker volume with docker run --rm -v node-red-data:/data -v /backup:/backup alpine tar czf /backup/node-red.tar.gz -C / data. Or use whatever backup system you have for your other services.
Explore Node-RED in our AI Homelab Toolkit.
Recommended Hardware & Hosting
Build your homelab with hardware tested and used by our team.
Affiliate links โ we may earn a small commission at no extra cost to you.