<figure class="wp-block-image aligncenter"><img src="https://flowgenius.in/wp-content/uploads/2026/01/n8n-worker-memory-ownership.png" alt="Step by Step Guide to solve n8n worker memory ownership" /> <figcaption style="text-align: center;">Step by Step Guide to solve n8n worker memory ownership</p>
<hr />
</figcaption></figure>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for: </strong> Platform engineers and DevOps specialists who run n8n in production and need reliable memory‑management for high‑throughput workflows. <strong>We cover this in detail in the </strong>n8n Production Readiness & Scalability Risks Guide.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Featured Snippet</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">In n8n each worker runs in its own Node.js process with a private V8 heap. Memory is allocated per‑workflow execution and released when the worker exits or when the built‑in <code>worker‑pool</code> garbage‑collects idle workers. Prevent OOM errors by:</p>
<ul style="margin-bottom: 1.5em; line-height: 1.9;">
<li>Setting <code>EXECUTIONS_PROCESS=worker</code> and a sensible <code>EXECUTIONS_WORKER_TIMEOUT</code>.</li>
<li>Monitoring <code>process.memoryUsage()</code>.</li>
<li>Capping the V8 heap (<code>NODE_OPTIONS="--max-old-space-size=3072"</code>) and matching Docker/Kubernetes memory limits.</li>
</ul>
<blockquote style="margin: 0 0 2em 2em; padding-left: 1em; border-left: 4px solid #ddd;">
<p style="margin-bottom: 0; line-height: 1.9;"><em>In production, you’ll see the “heap out of memory” message the first time a worker hits its limit – it’s a clear sign you need to adjust one of the knobs above.</em></p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis</h2>
<p><strong>If you encounter any </strong><a href="/event-loop-starvation-in-n8n">event loop starvation in n8n </a><strong>resolve them before continuing with the setup.</strong></p>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Symptom</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Likely Cause</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Immediate Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">JavaScript heap out of memory in logs</td>
<td style="padding: 13px; border: 1px solid #ddd;">A worker exceeds its V8 heap (≈1.5 GB) because of large payloads or long loops</td>
<td style="padding: 13px; border: 1px solid #ddd;">Reduce <code>EXECUTIONS_WORKER_TIMEOUT</code>, split the workflow, or raise the worker memory limit (<code>docker run --memory=4g …</code>).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Workers never recycle, memory climbs continuously</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>EXECUTIONS_WORKER_TIMEOUT=0</code> (disabled) or custom code holding globals</td>
<td style="padding: 13px; border: 1px solid #ddd;">Set <code>EXECUTIONS_WORKER_TIMEOUT=1800</code> (30 min) <strong>or</strong> add explicit <code>global.gc()</code> after heavy steps.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Host OOM while workers report low usage</td>
<td style="padding: 13px; border: 1px solid #ddd;">Host‑level cgroup limit lower than the sum of worker limits</td>
<td style="padding: 13px; border: 1px solid #ddd;">Align host resources with <code>resources.limits.memory</code> in Kubernetes or <code>mem_limit</code> in Docker‑Compose.</td>
</tr>
</tbody>
</table>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. How n8n Workers Allocate and Own Memory?</h2>
<p><strong>If you encounter any </strong><a href="/long-json-payloads-n8n-performance">long json payloads n8n performance </a><strong>resolve them before continuing with the setup.</strong></p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">1.1 Worker Process Model</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Each worker is launched as an independent Node.js process, guaranteeing isolation from the main process and from other workers.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Internal command used by n8n to start a worker
node ./packages/workers/src/worker.js \
--executionId=12345</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">The worker owns its own V8 heap, so memory used inside one worker cannot be accessed by another.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">1.2 Memory Segments per Worker</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Segment</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Description</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Approx. Size (default)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><strong>V8 Heap</strong></td>
<td style="padding: 13px; border: 1px solid #ddd;">JavaScript objects, arrays, strings</td>
<td style="padding: 13px; border: 1px solid #ddd;">1.5 GB (controlled by <code>--max-old-space-size</code>)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><strong>Native Buffers</strong></td>
<td style="padding: 13px; border: 1px solid #ddd;">Binary data (files, HTTP responses)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Unlimited, limited by OS cgroup</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><strong>Node.js Event Loop</strong></td>
<td style="padding: 13px; border: 1px solid #ddd;">Callbacks, timers, handles</td>
<td style="padding: 13px; border: 1px solid #ddd;">< 50 MB</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><strong>Process Overhead</strong></td>
<td style="padding: 13px; border: 1px solid #ddd;">Node executable, libraries</td>
<td style="padding: 13px; border: 1px solid #ddd;">~30 MB</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 2em; padding-left: 1em; border-left: 4px solid #ddd;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA Note</strong> – Align <code>--max-old-space-size</code> with your container’s memory limit; otherwise the OS may kill the worker (SIGKILL) without a graceful error.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Configuring Memory Ownership – From Zero to Production‑Ready</h2>
<p><strong>If you encounter any </strong><a href="/n8n-redis-latency-impact">n8n redis latency impact </a><strong>resolve them before continuing with the setup.</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;">Before diving into the YAML, remember that the settings you choose here will be the ones you’ll be tweaking when a workflow suddenly starts to lag. It’s normal to iterate a few times.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.1 Core Environment Variables</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Variable</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Default</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Production Recommendation</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">EXECUTIONS_PROCESS</td>
<td style="padding: 13px; border: 1px solid #ddd;">main</td>
<td style="padding: 13px; border: 1px solid #ddd;">worker (enables multi‑process isolation)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">EXECUTIONS_WORKER_TIMEOUT</td>
<td style="padding: 13px; border: 1px solid #ddd;">0 (no timeout)</td>
<td style="padding: 13px; border: 1px solid #ddd;">1800 (30 min) or lower for high‑throughput</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">EXECUTIONS_WORKER_MAX_MEMORY</td>
<td style="padding: 13px; border: 1px solid #ddd;">null</td>
<td style="padding: 13px; border: 1px solid #ddd;">Set to 2GB‑4GB matching container limit</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">NODE_OPTIONS</td>
<td style="padding: 13px; border: 1px solid #ddd;">“”</td>
<td style="padding: 13px; border: 1px solid #ddd;">–max-old-space-size=3072 (3 GB)</td>
</tr>
</tbody>
</table>
<h4 style="margin-bottom: 45px; line-height: 1.3;">Docker‑Compose Example</h4>
<p style="margin-bottom: 2em; line-height: 1.9;">Below is a minimal snippet that applies the recommended settings.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">services:
n8n:
image: n8nio/n8n:latest
environment:
- EXECUTIONS_PROCESS=worker
- EXECUTIONS_WORKER_TIMEOUT=1800</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"> environment:
- EXECUTIONS_WORKER_MAX_MEMORY=3072
- NODE_OPTIONS=--max-old-space-size=3072
deploy:
resources:
limits:
memory: 4g</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">*The two <code>environment</code> blocks are shown separately to keep each code block under five lines.*</p>
<h4 style="margin-bottom: 45px; line-height: 1.3;">Kubernetes Manifest (Memory‑Aware)</h4>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
spec:
replicas: 3
template:
spec:
containers:
- name: n8n
image: n8nio/n8n:latest</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"> env:
- name: EXECUTIONS_PROCESS
value: "worker"
- name: EXECUTIONS_WORKER_TIMEOUT
value: "1800"
- name: NODE_OPTIONS
value: "--max-old-space-size=3072"
resources:
limits:
memory: "4Gi"
requests:
memory: "3Gi"</pre>
<blockquote style="margin: 0 0 2em 2em; padding-left: 1em; border-left: 4px solid #ddd;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA Warning</strong> – If <code>EXECUTIONS_WORKER_MAX_MEMORY</code> exceeds the pod’s <code>limits.memory</code>, the Linux OOM killer will terminate the worker abruptly, causing lost executions. Keep the two values in sync.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Diagnosing Memory Leaks Inside a Worker</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.1 Real‑Time Monitoring</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Add a lightweight logger at the start of any node (debug mode) to emit memory stats every five seconds.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">const { memoryUsage } = process;
setInterval(() => {
const { rss, heapUsed } = memoryUsage();
console.log(
`[Memory] RSS:${(rss / 1e6).toFixed(1)}MB Heap:${(heapUsed / 1e6).toFixed(1)}MB`
);
}, 5_000);</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.2 Common Leak Patterns</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Pattern</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Why It Leaks</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Global variables (<code>global.myCache = …</code>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Persist across executions</td>
<td style="padding: 13px; border: 1px solid #ddd;">Use node‑cache scoped to the node or <code>this.getWorkflowStaticData('node')</code>.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Uncleared timers (<code>setInterval</code> without <code>clearInterval</code>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Keeps event‑loop handles alive</td>
<td style="padding: 13px; border: 1px solid #ddd;">Return a cleanup function from the node’s <code>execute</code> method.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Large Buffers in <code>binary</code> property without <code>binary.clear()</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Buffers survive until worker exit</td>
<td style="padding: 13px; border: 1px solid #ddd;">Call <code>item.binary = undefined</code> after the upload step.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Recursive <code>for…in</code> over huge objects</td>
<td style="padding: 13px; border: 1px solid #ddd;">Creates hidden references</td>
<td style="padding: 13px; border: 1px solid #ddd;">Iterate with <code>Object.keys()</code> or shallow‑copy before looping.</td>
</tr>
</tbody>
</table>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.3 Leak‑Detection Checklist</h3>
<ul style="margin-bottom: 1.5em; line-height: 1.9;">
<li>No <code>global</code> assignments in custom nodes.</li>
<li>All <code>setTimeout</code>/<code>setInterval</code> cleared in a <code>finally</code> block.</li>
<li>Binary data removed after use (<code>delete item.binary</code>).</li>
<li>Use <code>memwatch-next</code> (dev only) to spot unexpected heap growth.</li>
</ul>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">npm i memwatch-next --save-dev</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">// dev‑only: attach a leak listener in a custom node
const memwatch = require('memwatch-next');
memwatch.on('leak', info => console.warn('Memory leak detected', info));</pre>
<blockquote style="margin: 0 0 2em 2em; padding-left: 1em; border-left: 4px solid #ddd;">
<p style="margin-bottom: 0; line-height: 1.9;">*Most teams run into these patterns after a few weeks of steady traffic, not on day one.*</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Advanced Scenarios – When Workers Need Shared State</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.1 External Caching with Redis</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Because workers do <strong>not</strong> share memory, any cross‑execution cache must live outside the process.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
async function getCached(key) {
const cached = await redis.get(key);
return cached ? JSON.parse(cached) : null;
}</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.2 When to Reuse Workers vs. Isolate</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Use‑Case</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Recommended Setting</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">High‑frequency small payloads (≤ 100 KB)</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>EXECUTIONS_WORKER_TIMEOUT=300</code> (5 min) to keep workers warm.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Large file processing (> 50 MB)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Disable timeout (<code>0</code>) <strong>and</strong> enforce <code>EXECUTIONS_WORKER_MAX_MEMORY</code>.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Real‑time webhook bursts</td>
<td style="padding: 13px; border: 1px solid #ddd;">Increase <code>WORKER_CONCURRENCY</code> (default 5) and watch the CPU‑to‑memory ratio.</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 2em; padding-left: 1em; border-left: 4px solid #ddd;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA Insight</strong> – In Kubernetes, a Horizontal Pod Autoscaler that scales on <code>memory > 80 %</code> adds more n8n pods, each with its own worker pool. This scales memory horizontally without requiring shared state.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Best‑Practice Checklist – Controlling Memory Ownership</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Checklist Item</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Why It Matters</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Run n8n in <strong>worker mode</strong> (<code>EXECUTIONS_PROCESS=worker</code>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Guarantees process isolation and deterministic memory release.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Set <code>EXECUTIONS_WORKER_TIMEOUT</code> ≤ 30 min for most workloads</td>
<td style="padding: 13px; border: 1px solid #ddd;">Stops runaway workers that hoard RAM.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Cap the V8 heap with <code>NODE_OPTIONS=--max-old-space-size</code> matching container limits</td>
<td style="padding: 13px; border: 1px solid #ddd;">Aligns JavaScript memory with OS limits, avoiding silent OOM.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Externalize large data (Redis, S3, DB) instead of keeping it in <code>binary</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Reduces per‑worker RAM footprint.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Continuously monitor <code>process.memoryUsage()</code> via logs or Prometheus exporter</td>
<td style="padding: 13px; border: 1px solid #ddd;">Detects memory creep early.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Enable graceful shutdown (<code>SIGTERM</code> handler) to flush pending executions</td>
<td style="padding: 13px; border: 1px solid #ddd;">Guarantees no data loss when a pod is terminated.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Run health‑checks (<code>/healthz</code>) that also verify worker count</td>
<td style="padding: 13px; border: 1px solid #ddd;">Detects worker‑pool starvation before it impacts memory.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Pin to a stable n8n release (≥ 1.30) where worker‑pool bugs are fixed</td>
<td style="padding: 13px; border: 1px solid #ddd;">Prevents regressions from newer, untested versions.</td>
</tr>
</tbody>
</table>
<h2 style="margin-bottom: 45px; line-height: 1.3;"></h2>
<hr style="margin: 55px 0;" />
<h3 style="margin-bottom: 45px; line-height: 1.3;">Bottom Line</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Memory ownership in n8n workers is <strong>process‑level</strong>, not shared. By configuring worker timeouts, capping the V8 heap, externalizing large payloads, and continuously monitoring <code>process.memoryUsage()</code>, you ensure each worker releases its memory cleanly. The result is a stable, production‑grade automation platform that avoids OOM crashes and scales predictably.</p>
Step by Step Guide to solve n8n worker memory ownership
Who this is for: Platform engineers and DevOps specialists who run n8n in production and need reliable memory‑management for high‑throughput workflows. We cover this in detail in the n8n Production Readiness & Scalability Risks Guide.
Featured Snippet
In n8n each worker runs in its own Node.js process with a private V8 heap. Memory is allocated per‑workflow execution and released when the worker exits or when the built‑in worker‑pool garbage‑collects idle workers. Prevent OOM errors by:
Setting EXECUTIONS_PROCESS=worker and a sensible EXECUTIONS_WORKER_TIMEOUT.
Monitoring process.memoryUsage().
Capping the V8 heap (NODE_OPTIONS="--max-old-space-size=3072") and matching Docker/Kubernetes memory limits.
In production, you’ll see the “heap out of memory” message the first time a worker hits its limit – it’s a clear sign you need to adjust one of the knobs above.
Before diving into the YAML, remember that the settings you choose here will be the ones you’ll be tweaking when a workflow suddenly starts to lag. It’s normal to iterate a few times.
2.1 Core Environment Variables
Variable
Default
Production Recommendation
EXECUTIONS_PROCESS
main
worker (enables multi‑process isolation)
EXECUTIONS_WORKER_TIMEOUT
0 (no timeout)
1800 (30 min) or lower for high‑throughput
EXECUTIONS_WORKER_MAX_MEMORY
null
Set to 2GB‑4GB matching container limit
NODE_OPTIONS
“”
–max-old-space-size=3072 (3 GB)
Docker‑Compose Example
Below is a minimal snippet that applies the recommended settings.
EEFA Warning – If EXECUTIONS_WORKER_MAX_MEMORY exceeds the pod’s limits.memory, the Linux OOM killer will terminate the worker abruptly, causing lost executions. Keep the two values in sync.
3. Diagnosing Memory Leaks Inside a Worker
3.1 Real‑Time Monitoring
Add a lightweight logger at the start of any node (debug mode) to emit memory stats every five seconds.
EXECUTIONS_WORKER_TIMEOUT=300 (5 min) to keep workers warm.
Large file processing (> 50 MB)
Disable timeout (0) and enforce EXECUTIONS_WORKER_MAX_MEMORY.
Real‑time webhook bursts
Increase WORKER_CONCURRENCY (default 5) and watch the CPU‑to‑memory ratio.
EEFA Insight – In Kubernetes, a Horizontal Pod Autoscaler that scales on memory > 80 % adds more n8n pods, each with its own worker pool. This scales memory horizontally without requiring shared state.
Run n8n in worker mode (EXECUTIONS_PROCESS=worker)
Guarantees process isolation and deterministic memory release.
Set EXECUTIONS_WORKER_TIMEOUT ≤ 30 min for most workloads
Stops runaway workers that hoard RAM.
Cap the V8 heap with NODE_OPTIONS=--max-old-space-size matching container limits
Aligns JavaScript memory with OS limits, avoiding silent OOM.
Externalize large data (Redis, S3, DB) instead of keeping it in binary
Reduces per‑worker RAM footprint.
Continuously monitor process.memoryUsage() via logs or Prometheus exporter
Detects memory creep early.
Enable graceful shutdown (SIGTERM handler) to flush pending executions
Guarantees no data loss when a pod is terminated.
Run health‑checks (/healthz) that also verify worker count
Detects worker‑pool starvation before it impacts memory.
Pin to a stable n8n release (≥ 1.30) where worker‑pool bugs are fixed
Prevents regressions from newer, untested versions.
Bottom Line
Memory ownership in n8n workers is process‑level, not shared. By configuring worker timeouts, capping the V8 heap, externalizing large payloads, and continuously monitoring process.memoryUsage(), you ensure each worker releases its memory cleanly. The result is a stable, production‑grade automation platform that avoids OOM crashes and scales predictably.