Deployment guide

n8n out of memory: diagnosis and fix on a VPS

Deploy on a VPS Cloud →

Tutorial

n8n out of memory: diagnosis and fix on a VPS

Automation9 min read7 steps

The message `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory` kills n8n with no prior warning. Many teams add more RAM and see no change — because the V8 heap cap is independent of the server's physical memory and must be set explicitly. This article covers the three real causes of the OOM crash, the environment variable that fixes each one, and the worker/webhook separation that prevents recurrence.

Contents· Why n8n crashes on memory, and not for the reason you think1/8
  1. 01Why n8n crashes on memory, and not for the reason you think
  2. 02Signals that confirm an n8n OOM crash
  3. 03Prerequisites before intervening
  4. 04Fixing OOM: from diagnosis to stable configuration
  5. 05Limit execution payload size
  6. 06Post-fix configuration: useful environment variables
  7. 07Troubleshooting — real errors and their causes
  8. 08Resources and next steps

Why n8n crashes on memory, and not for the reason you think

V8, the JavaScript engine bundled with Node.js, has a heap cap that is independent of physical RAM. By default it ranges from 512 MB to 1.5 GB depending on the Node version and platform — on a 4 GB or 8 GB VPS, the machine is not out of memory, but the V8 process is. Adding more RAM to the server changes nothing without NODE_OPTIONS=--max-old-space-size.

A second common cause: in main mode (the default), n8n executes workflows in the same process that serves webhooks and the API. A data-heavy workflow — CSV transformation, aggregation of thousands of rows, looping GPT calls — monopolises the heap during execution. If several pile up, the V8 cap is hit and the process is killed.

A third, subtler cause: jobs accumulate in memory when queue mode is enabled without dedicated workers. The queue (Redis or BullMQ) offloads executions from the main process, but if no worker actually consumes the jobs, they pile up, callbacks stay pending and the heap grows.

Signals that confirm an n8n OOM crash

  • Exact exit message: FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory in container logs (docker logs n8n)
  • Silent crash under systemd: the service restarts automatically without leaving a trace if Restart=always is set — check journalctl -u n8n --since "1 hour ago"
  • Kernel OOM kill: dmesg | grep -i oom shows Killed process <pid> (node) before the restart, independently of Node
  • Correlation with a heavy workflow: the crash always occurs during executions of type "file processing" or "loop over thousands of items"
  • Heap stable for hours then sudden spike: a periodically triggered workflow accumulates unreleased closures — the heap rises on each run and does not fully come back down
  • Normal memory load in htop: system RAM is not saturated at the time of the crash, confirming the problem is V8, not the machine

Prerequisites before intervening

These steps assume n8n is already running on your VPS via Docker Compose. If that is not the case, the installer-n8n-vps article covers the complete deployment from scratch — come back here once the instance is up.

What you need to apply the fixes:

- Root SSH access to the VPS and an editable docker-compose.yml
- Available memory: a V8 cap of 4,096 MB (--max-old-space-size=4096) requires at least 6 GB of RAM on the VPS to leave headroom for the operating system, workers and Redis
- Redis already deployed if you switch to queue mode — redis:7-alpine is sufficient for single-VPS use
- n8n version 1.0 or higher: main/worker separation has been available since version 0.214 but is only stable in production from 1.0 onwards
- Database backup before any Compose modification — credentials and execution tables are not part of the Docker image

Fixing OOM: from diagnosis to stable configuration

  1. Confirm the cause in logs

    Read the last 200 lines of the container at the time of the crash:

    docker logs n8n --tail 200 2>&1 | grep -E "FATAL|heap|OOM|Killed"

    If you see FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory, it is a V8 crash. If you see Killed alone without a Node message, it is the kernel OOM killer — both can coexist on the same incident.

  2. Set the V8 cap via NODE_OPTIONS

    In your docker-compose.yml, add the NODE_OPTIONS environment variable to the n8n service. Recommended value based on VPS RAM:

    - VPS 4 GB: --max-old-space-size=2048
    - VPS 8 GB: --max-old-space-size=4096
    - VPS 16 GB: --max-old-space-size=8192

    Rule: reserve roughly half the RAM available after the operating system and ancillary services (Redis, proxy). Do not exceed 70% of total RAM.

    services:
      n8n:
        image: n8nio/n8n:latest
        environment:
          - NODE_OPTIONS=--max-old-space-size=4096
          # ... other variables
  3. Verify the value is actually read

    After docker compose up -d, verify Node reads the cap:

    docker exec n8n node -e "const v8=require('v8'); console.log(v8.getHeapStatistics().heap_size_limit / 1024 / 1024, 'MB')"

    The displayed value should be close to your --max-old-space-size. If it still shows 512 or 1500, the environment variable is not being passed to the process — verify that NODE_OPTIONS is in the environment: section of the service, not in env_file: with a badly parsed value.

  4. Switch to queue mode with Redis

    main mode (default) runs everything in a single process. For instances handling more than 20 simultaneous workflows or large payloads, separate the roles:

    services:
      redis:
        image: redis:7-alpine
        restart: unless-stopped
        volumes:
          - redis_data:/data
    
      n8n:
        image: n8nio/n8n:latest
        environment:
          - NODE_OPTIONS=--max-old-space-size=2048
          - EXECUTIONS_MODE=queue
          - QUEUE_BULL_REDIS_HOST=redis
          - QUEUE_BULL_REDIS_PORT=6379
        depends_on:
          - redis
        ports:
          - "5678:5678"
    
      n8n-worker:
        image: n8nio/n8n:latest
        command: worker
        environment:
          - NODE_OPTIONS=--max-old-space-size=4096
          - EXECUTIONS_MODE=queue
          - QUEUE_BULL_REDIS_HOST=redis
          - QUEUE_BULL_REDIS_PORT=6379
        depends_on:
          - redis
        scale: 2
    
    volumes:
      redis_data:

    The n8n service becomes the main process (API + UI + webhooks) with a moderate cap. The n8n-worker service handles executions with a higher cap. The scale: 2 directive starts two workers — adjust to your needs.

  5. Separate the webhook process if traffic requires it

    On instances receiving many parallel webhooks, the main process can be saturated even without executing workflows. n8n offers a dedicated webhook mode:

      n8n-webhook:
        image: n8nio/n8n:latest
        command: webhook
        environment:
          - NODE_OPTIONS=--max-old-space-size=1024
          - EXECUTIONS_MODE=queue
          - QUEUE_BULL_REDIS_HOST=redis
          - QUEUE_BULL_REDIS_PORT=6379
          - N8N_DISABLE_UI=true
        depends_on:
          - redis
        ports:
          - "5679:5678"

    Then configure your reverse proxy to route /webhook/ to port 5679 and the rest to port 5678 of the main process. Webhook mode is available from n8n 1.0.

  6. Enable explicit garbage collection for heavy workflows

    For workflows that process large files or long loops, you can help V8 release memory more aggressively:

    NODE_OPTIONS="--max-old-space-size=4096 --expose-gc"

    This exposes global.gc() — n8n can call it between workflow steps. Combine it with EXECUTIONS_DATA_SAVE_ON_SUCCESS=none if you do not need the execution history: retained execution data often represents 30–50% of the heap.

  7. Monitor the heap after the fix

    Enable n8n metrics to observe heap evolution without manual intervention:

    N8N_METRICS=true
    N8N_METRICS_PREFIX=n8n_

    The /metrics endpoint (port 5678) then exposes nodejs_heap_size_used_bytes and nodejs_heap_size_total_bytes, compatible with Prometheus. A basic Grafana dashboard on these two metrics will alert you well before the next crash.

Limit execution payload size

The EXECUTIONS_DATA_MAX_SIZE parameter (in bytes, default: no limit) cuts an execution before it can overflow the heap. Recommended value for general-purpose instances: 16777216 (16 MB). A workflow exceeding this threshold fails cleanly instead of killing the entire process. Combine it with EXECUTIONS_DATA_PRUNE=true and EXECUTIONS_DATA_MAX_AGE=168 (one week) to avoid accumulation of past execution data.

Post-fix configuration: useful environment variables

Once OOM is resolved, these variables consolidate instance stability:

- N8N_DEFAULT_BINARY_DATA_MODE=filesystem — stores binary files on disk rather than in memory; essential for workflows handling large CSV or PDF files
- OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true — manual executions (triggered from the editor) also go through workers, avoiding heap pressure on the main process during testing
- N8N_RUNNERS_ENABLED=true and N8N_RUNNERS_MAX_CONCURRENCY=5 — enables the experimental task runner (n8n 1.10+) which isolates each execution in a subprocess, preventing a single workflow from consuming all available heap
- DB_POSTGRESDB_* — migrating from SQLite to PostgreSQL on high-volume instances: SQLite serialises all reads/writes and can block workers, amplifying memory pressure

Troubleshooting — real errors and their causes

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Direct cause: the V8 heap has reached its cap. Solution: add NODE_OPTIONS=--max-old-space-size=<n> in the container environment variables, with a value calibrated to available RAM (see step 2).

Killed in logs, without a Node message
Cause: the Linux kernel OOM killer terminated the process before V8 could emit its message. Occurs when physical memory is actually exhausted — different from a pure V8 crash. Check with dmesg | grep -i oom. Reduce --max-old-space-size or add RAM, and enable EXECUTIONS_DATA_SAVE_ON_SUCCESS=none to lighten the footprint.

Workers do not consume jobs despite EXECUTIONS_MODE=queue
Common cause: DB_TYPE and database variables are not passed to workers. Each Compose service must have its own connection variables — the worker does not inherit the main process configuration. Check with docker exec n8n-worker env | grep DB_.

Heap climbs after each run and does not come back down
Cause: a closure holds a reference to a large array between executions. Enable --expose-gc in NODE_OPTIONS and add EXECUTIONS_DATA_SAVE_ON_SUCCESS=none. If the behaviour persists, switch to task runner mode (N8N_RUNNERS_ENABLED=true), which isolates each workflow.

Error: Redis connection failed after switching to queue mode
Cause: QUEUE_BULL_REDIS_HOST points to localhost instead of the Docker service name. In a Compose network, the main process and workers reach Redis by its service name (redis in the example above), not 127.0.0.1.

Resources and next steps

The official n8n documentation on memory errors (docs.n8n.io/hosting/scaling/memory-errors) details recommended --max-old-space-size values based on available RAM and lists scaling parameters. The GitHub issue n8n-io/n8n#17461 (OOM in production, opened March 2026, 80+ comments) documents real-world cases — including the correlation between CSV processing workflows and heap crashes — and configurations that have stabilised instances similar to yours.

If you manage multiple n8n instances for different clients, the main/worker separation described here is also the foundation of a multi-tenant architecture: each client can have their own worker pool with an independent V8 cap, without a heavy workflow from one account impacting others.

Deploy n8n on a dedicated VPS

A VPS with root access, dedicated IPv4 and choice of OS to host your n8n instance in queue mode, without workflow or webhook limits.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab