n8n memory leak in Docker – why RAM keeps growing and how to fix it

Step by Step Guide to solve n8n uses more memory every day leak or design issue 
Step by Step Guide to solve n8n uses more memory every day leak or design issue


Quick Diagnosis

1. Monitor – Log process.memoryUsage() (RSS/heap) for 24 h.
2. Profile – Run node --inspect-brk with Chrome DevTools or clinic.js.
3. Check Config – Verify N8N_EXECUTIONS_PROCESS, N8N_MAX_EXECUTION_TIMEOUT, and Docker --memory limits.
4. Apply Fixes – Reduce V8 heap, switch to queue execution, add periodic GC.
5. Validate – Memory should stay within 10 % drift over 48 h; otherwise treat it as a leak and file an issue.

Who this is for: Operators of self‑hosted n8n (Docker, Kubernetes, or bare‑metal) who see daily RSS growth and need a systematic way to decide whether the cause is a leak or a normal load pattern.


We cover this in detail in the n8n Performance Degradation & Stability Issues Guide.

Symptom Immediate Check Likely Category
RSS climbs > 50 % daily, then crashes docker stats <container> or pm2 listMemory column Potential leak
Spikes only while a specific workflow runs Enable workflow‑level logs (N8N_LOG_LEVEL=debug) and isolate the workflow Design‑induced load
Memory plateaus after restart, then rises again docker restart n8n and watch for repeat pattern Leak
Steady under low traffic, jumps under high concurrency Simulate load with k6 or artillery Design/scale issue

Quick actionable solution (run once)

Capture a baseline memory snapshot:

docker exec -it n8n node -e "console.log(process.memoryUsage())"

Restart with tighter limits:

docker run -d \
  -e NODE_OPTIONS="--max-old-space-size=256 --expose-gc" \
  -e N8N_EXECUTIONS_PROCESS=queue \
  --memory=512m \
  --restart=unless-stopped \
  n8nio/n8n

If memory stabilises, the problem was over‑allocation; otherwise move to profiling (section 2).


1. How n8n Manages Memory Internally?

If you encounter any n8n workflows slow after weeks in production root cause analysis resolve them before continuing with the setup.
n8n runs on Node.js v18 LTS. Its memory usage falls into three buckets:

Bucket What it stores Typical size
Heap JS objects, workflow definitions, execution data 50‑200 MB
RSS Native C++ addons, V8 engine, OS buffers 100‑300 MB
External DB connections, file handles, Docker overlay 20‑80 MB

Execution models and their memory profile

Mode Description Memory impact
main (default) All steps run in the same process Large heap for long‑running workflows
queue Steps delegated to BullMQ workers (child processes) Lower per‑process heap, higher overall RSS
process Each execution spawns a fresh Node.js process Near‑zero retention, best for leak‑prone workloads

EEFA note: For > 10 concurrent executions, queue is the production‑grade choice; process eliminates most leak vectors at the cost of extra CPU.


2. Identify a Real Memory Leak

2.1 Continuous monitoring (24‑48 h)

Add a lightweight logger to your Docker Compose file:

services:
  n8n:
    image: n8nio/n8n
    environment:
      - NODE_OPTIONS=--max-old-space-size=256
    command: >
      sh -c "while true; do
        node -e \"console.log(JSON.stringify(process.memoryUsage()))\"
        sleep 300
      done & exec n8n start"

Collect the JSON logs, plot **RSS** vs. **heapUsed**, and look for a linear upward trend that does not reset after a graceful restart.

2.2 Heap snapshot with clinic

npm i -g clinic               # install once
clinic doctor -- node ./packages/cli/src/cli.js start

Run typical traffic for 30 min, then open clinic.html. Focus on:

  • Detached objects (rare in n8n)
  • Large arrays such as executionData that remain after a workflow finishes
  • Unclosed DB connections (MongoClient, PostgresPool)

EEFA warning: Run clinic only in a staging environment; the profiler adds ~15 % CPU overhead. If you encounter any why n8n execution time increases over time resolve them before continuing with the setup.

2.3 Automated leak‑detection script (cron)

#!/usr/bin/env bash
THRESHOLD=0.10
BASE=$(docker exec n8n node -e "console.log(process.memoryUsage().heapUsed)")
sleep 21600   # 6 h
CURRENT=$(docker exec n8n node -e "console.log(process.memoryUsage().heapUsed)")
DIFF=$(node -e "console.log((${CURRENT}-${BASE})/ ${BASE})")
if (( $(echo "$DIFF > $THRESHOLD" | bc -l) )); then
  docker restart n8n
fi

Schedule it with crontab -e (e.g., 0 */6 * * * /path/to/script.sh).


3. Design Patterns That Inflate Memory

Pattern Why it inflates memory Mitigation
Large JSON payloads kept in executionData Full payload is retained for downstream nodes Enable “Clear execution data after each node” or trim payload with a Set node
Recursive sub‑workflows (Execute Workflow node) Each sub‑workflow creates a new execution context that lives until the parent finishes Limit depth via N8N_MAX_SUB_WORKFLOW_DEPTH and run sub‑workflows in a separate process
Infinite loops in Function nodes JavaScript loops that never break keep objects alive Add explicit break/return and enforce N8N_MAX_EXECUTION_TIMEOUT
Heavy file buffers (Read Binary File) Files are fully loaded into memory before being passed downstream Use the streaming variant or off‑load to external storage (S3, MinIO)
Unbounded concurrency (N8N_MAX_EXECUTIONS default 100) Simultaneous executions multiply heap usage Reduce to a realistic value (e.g., 20) and rely on a queue worker pool

EEFA tip: For SaaS‑style deployments, set N8N_EXECUTIONS_PROCESS=process to isolate each user’s workflow in its own process.


4. Configuration Tweaks to Contain Memory

Env variable Recommended value Effect
NODE_OPTIONS –max-old-space-size=256 –expose-gc Caps V8 heap, enables manual GC
N8N_EXECUTIONS_PROCESS queue (or process for high‑risk) Moves heavy steps to workers
N8N_MAX_EXECUTION_TIMEOUT 300000 ms (5 min) Forces GC after timeout
N8N_WORKER_TIMEOUT 60000 ms (1 min) Auto‑kill idle workers
N8N_MAX_WORKERS 4 (adjust to CPU cores) Limits concurrent workers
N8N_LOG_LEVEL error (prod) Reduces log‑buffer pressure
Docker –memory 512m‑1g (depends on load) Enforces cgroup limit, triggers OOM if exceeded

Docker Compose example (split for readability)

services:
  n8n:
    image: n8nio/n8n
    environment:
      - NODE_OPTIONS=--max-old-space-size=256 --expose-gc
    environment:
      - N8N_EXECUTIONS_PROCESS=queue
      - N8N_MAX_EXECUTION_TIMEOUT=300000
      - N8N_MAX_WORKERS=4
    deploy:
      resources:
        limits:
          memory: 1g
    restart: unless-stopped

EEFA warning: Setting --max-old-space-size too low can cause “JavaScript heap out of memory” crashes. Validate with a staging load before production rollout. If you encounter any n8n slows down even with low cpu usage resolve them before continuing with the setup.


5. Automated Cleanup & Garbage Collection

5.1 Manual GC trigger (Node ≥ 14)

docker exec -it n8n node -e "
if (global.gc) {
  global.gc();
  console.log('GC run');
} else {
  console.log('Run with --expose-gc');
}"

Schedule the command (e.g., every 4 h) via a cron job inside a side‑car container.

5.2 Periodic worker restart (queue mode)

# Gracefully kill all BullMQ workers; they will respawn automatically
docker exec n8n pkill -f "worker.js"

5.3 PM2 auto‑restart on memory threshold

pm2 start n8n --name n8n \
  --max-memory-restart 300M \
  --node-args="--max-old-space-size=256"

PM2 restarts the process once RSS exceeds 300 MB, preventing OOM cascades.


6. When to Scale Instead of Fix

Situation Recommended action
Memory stabilises after config changes (≤ 10 % drift) Keep settings, continue monitoring
Heap repeatedly hits --max-old-space-size ceiling Increase heap **and** move to process mode
> 30 concurrent executions cause RSS > 800 MB Deploy a horizontal n8n cluster behind a load balancer; use Redis for BullMQ
Persistent leak after profiling Open a GitHub issue with heap snapshots; consider contributing a fix

Scaling checklist

  • Deploy Redis (REDIS_HOST) for BullMQ persistence.
  • Enable N8N_CLUSTER_MODE=true and set N8N_CLUSTER_INSTANCE_ID.
  • Set Kubernetes replicas: 3 (or more) with a shared DB.
  • Configure health checks (/healthz) to auto‑evict unhealthy pods.

Conclusion

If memory stabilises after applying tighter heap limits, queue‑based execution, and periodic garbage collection, you are dealing with a design‑related consumption pattern that can be mitigated with configuration and workflow‑design changes. Persistent linear growth despite those measures indicates a real memory leak that requires heap‑snapshot analysis and, ultimately, code fixes upstream. Start with the quick‑diagnosis steps, move to systematic monitoring and profiling, and only then decide whether to adjust configuration, refactor workflows, or scale the deployment. This disciplined approach keeps n8n reliable in production while avoiding unnecessary over‑provisioning.

Leave a Comment

Your email address will not be published. Required fields are marked *