Both @kjooleng and @ClearStack hit the nail on the head regarding the OOM crash and the danger of duplicate email sends.
When you process 1000 items in a single linear execution or loop, Node.js accumulates all node input/output states in memory. Garbage Collection cannot clean up objects until the entire parent workflow execution completes, leading to host OOM kills.
Here is the 3-step production pattern to solve both the memory leak and the duplicate email risk:
### 1. The Sub-Workflow Batching Pattern (Solves OOM)
Instead of running all 1000 leads in one loop in the parent workflow:
1. Use a **Split In Batches Node** (batch size: 50-100 items).
2. Pass each batch to a secondary worker workflow using the **Execute Workflow Node**.
3. **Why this works:** When each sub-workflow execution finishes, n8n immediately flushes its RAM footprint and triggers the Node.js Garbage Collector, keeping memory usage flat over a 6-hour run.
### 2. Idempotency Check (Prevents Duplicate Email Spam)
As @ClearStack pointed out, if run #1 fails at lead 260 and you restart, leads 1-260 get emailed twice.
* Inside your sub-workflow, right before the Email Send Node, add an **If / Filter Node** that checks `already_contacted === true` (or queries your DB for `email_sent_at IS NOT NULL`).
* If `true` → Skip.
* If `false` → Send email and immediately update the DB status to `contacted`.
### 3. Increase Node.js Heap Limit (If Self-Hosted / Docker)
If you are self-hosting n8n via Docker or PM2, the default Node.js heap memory limit is around 2GB. For long-running batch jobs, increase it in your environment variables:
```bash
NODE_OPTIONS=“–max-old-space-size=4096”
```
*(This allocates up to 4GB RAM to the n8n process).*
Combining **Sub-workflows + Batching + Pre-send Idempotency Checks** is the standard way to run 10k+ lead pipelines without crashing n8n or spamming users.