# Node.js Max Old Space Size: Increasing Heap Limits for Large Datasets

The --max-old-space-size flag in Node.js configures the maximum memory allocation limit in megabytes for V8's old generation heap, preventing out-of-memory crashes during data processing. While 64-bit Node.js sets default heap memory limits between 2GB and 4GB depending on available physical RAM, intensive workflows require explicit tuning. This guide covers configuring runtime heap flags, avoiding container OOM kills, and offloading massive files to persistent workspaces.

Source: https://fast.io/resources/node-max-old-space-size/
Author: [Derek Labian](https://fast.io/authors/derek-labian/)
Last reviewed: 2026-10-05

## What Is Node.js Max Old Space Size and How Does V8 Manage Heap Memory?

Node.js configures the maximum memory size of V8's old memory section through the `--max-old-space-size` flag, where approaching the limit triggers increased garbage collection and exceeding it causes the runtime to crash with `FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`. Under official Node.js documentation, the runtime notes that as memory consumption approaches the limit, V8 spends more time on garbage collection in an effort to free unused memory, and on a machine with 2 GiB of memory, setting this to 1536 MiB leaves memory for other system tasks and avoids swapping. When an application attempts to process large datasets, run intensive build pipelines, or transform thousands of documents in memory, hitting this ceiling terminates the Node.js process immediately.

The `--max-old-space-size` flag in Node.js configures the maximum memory allocation limit (in megabytes) for V8's old generation heap, preventing early out-of-memory crashes during memory-intensive file processing. To understand why this flag exists and how it operates, developers must look inside Google V8, the underlying JavaScript engine that powers Node.js.

### Generational Garbage Collection in the V8 Engine

The V8 engine manages memory using a generational garbage collection strategy grounded in the weak generational hypothesis: the observation that most allocated objects die shortly after creation. To optimize allocation throughput and minimize collection pauses, V8 splits its managed heap into distinct logical spaces:

* **New Space (Young Generation):** A compact memory region reserved for newly allocated entities such as temporary variables, loop iterators, and short-lived function arguments. New Space is divided into two contiguous semi-spaces known as the From space and To space. Allocations occur sequentially in the From space. When it fills, V8 executes a fast Minor Garbage Collection cycle (known as Scavenge). Active objects that survive the collection are copied into the To space, and the roles of the two semi-spaces reverse. Objects that survive two consecutive scavenge cycles are promoted, or tenured, into Old Space.
* **Old Space (Old Generation):** A significantly larger memory partition designated for objects that survive initial scavenge passes. Long-lived objects, application singletons, database connection pools, module-level state, and cached documents reside here. Because Old Space is large, running frequent scavenge passes would be prohibitively expensive. V8 instead manages Old Space with a Major Garbage Collector using Mark-Sweep and Mark-Compact algorithms. Mark-Sweep identifies surviving memory nodes and reclaims disconnected memory blocks. When memory fragmentation rises, Mark-Compact shifts surviving objects together to produce contiguous blocks of free space.
* **Large Object Space:** Certain allocations, such as large typed arrays or substantial strings that exceed the size capacity of New Space semi-spaces, bypass Young Generation entirely. V8 allocates these directly in Large Object Space to prevent the costly overhead of copying multi-megabyte objects back and forth during scavenge cycles.
* **Code Space and Map Space:** Specialized memory segments dedicated to JIT-compiled machine code vectors and hidden classes (Maps) that define the structural shapes of JavaScript objects.

When developers pass `--max-old-space-size`, the flag specifically governs the upper allocation boundary of Old Space. It dictates when V8's Major Garbage Collector must run and sets the ultimate allocation ceiling beyond which the engine refuses to allocate additional heap memory.

### Default 64-Bit Memory Allocation and Programmatic Inspection

On 64-bit operating systems, Node.js historically defaulted to an Old Space limit of approximately 1.4 GB to 2 GB. In modern releases of Node.js, V8 dynamically evaluates available physical memory on startup, setting the default heap limit between 2 GB and approximately 4 GB on systems equipped with sufficient RAM. On 32-bit systems, process memory address space constraints limit the heap allocation to approximately 1 GB.

Rather than guessing how much heap memory a running instance has been assigned, developers can inspect active heap boundaries programmatically using the built-in `v8` module:

```javascript
import v8 from 'node:v8';

// retrieve live heap memory metrics
const heapStats = v8.getHeapStatistics();
console.log({
  totalHeapSizeMB: Math.round(heapStats.total_heap_size / 1024 / 1024),
  usedHeapSizeMB: Math.round(heapStats.used_heap_size / 1024 / 1024),
  heapSizeLimitMB: Math.round(heapStats.heap_size_limit / 1024 / 1024),
  totalPhysicalSizeMB: Math.round(heapStats.total_physical_size / 1024 / 1024),
  totalAvailableSizeMB: Math.round(heapStats.total_available_size / 1024 / 1024)
});
```

In this inspection output:

* `heap_size_limit`: The absolute ceiling that V8 will not exceed. This value reflects either the system default or the exact number configured by `--max-old-space-size`.
* `total_heap_size`: The current quantity of memory committed by V8 for the heap.
* `used_heap_size`: The memory currently occupied by live JavaScript objects.
* `total_available_size`: The remaining capacity before the process reaches its limit and risks an out-of-memory crash.

When `used_heap_size` approaches `heap_size_limit`, the engine initiates aggressive, synchronous Mark-Sweep-Compact cycles. If those cycles fail to free enough memory to satisfy an incoming allocation, Node.js aborts the process.

## How to Configure Max Old Space Size via CLI and Environment Variables

Configuring V8 heap memory can be accomplished through several mechanisms depending on how your Node.js application is launched, deployed, or managed.

### Direct Command-Line Flags

The simplest way to increase the heap limit is passing the flag directly to the Node.js executable at invocation:

```bash
node --max-old-space-size=4096 server.js
node --max-old-space-size=8192 worker.js
```

The value is always provided in megabytes. For instance, specifying a 4096 MB parameter allocates 4 GB of heap memory, and 8192 MB provides an 8 GB allocation limit. When invoking standalone scripts or ad-hoc data migration tools, this direct CLI flag provides immediate control over the process memory budget.

### Environment Variable Export via NODE_OPTIONS

In production systems, build pipelines, and complex project setups, scripts are rarely invoked with a bare `node` command. Developers invoke build tools, test runners, and framework CLIs such as Next.js, Vite, Webpack, Prisma, or TypeScript's `tsc`. Because these utilities spawn Node.js internally, passing command-line flags directly to the binary is often difficult or blocked by tooling wrappers.

The standard cross-platform solution is the `NODE_OPTIONS` environment variable. Node.js inspects `NODE_OPTIONS` on startup and prepends any defined options to its argument list:

```bash
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build
```

On Windows environments using PowerShell, declare the variable accordingly:

```powershell
$env:NODE_OPTIONS="--max-old-space-size=4096"
npm run build
```

To configure this permanently in `package.json` across varied developer environments, developers frequently embed `NODE_OPTIONS` into build scripts:

```json
{
  "name": "data-pipeline",
  "version": "1.0.0",
  "scripts": {
    "build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 next build",
    "process:dataset": "node --max-old-space-size=8192 scripts/import-corpus.js"
  }
}
```

### Process Managers and Dynamic Percentage Configuration

When running background worker services under process managers like PM2, pass memory flags via the `node_args` configuration property:

```javascript
// ecosystem.config.cjs
module.exports = {
  apps: [
    {
      name: 'batch-transformer',
      script: './dist/worker.js',
      node_args: '--max-old-space-size=6144',
      max_memory_restart: '7G',
      env: {
        NODE_ENV: 'production'
      }
    }
  ]
};
```

Node.js also supports `--max-old-space-size-percentage`, which allows configuring the Old Space ceiling as a percentage of total host memory. When both `--max-old-space-size` and `--max-old-space-size-percentage` are provided, the percentage-based flag takes precedence.

### Workload Allocation Recommendations

Selecting an arbitrary heap value can lead to severe system instability. Setting `--max-old-space-size` beyond available physical RAM triggers severe OS swap thrashing and degraded I/O performance. When V8 attempts to execute a Mark-Compact pass across memory pages that have been swapped to disk, disk thrashing saturates the storage bus and can freeze the host system for minutes.

The following table provides verified operational recommendations based on workload characteristics:

| Workload Profile | Recommended Allocation | Configuration Method | Operational Target | Primary Memory Risk |
| :--- | :--- | :--- | :--- | :--- |
| **Lightweight API Gateway** | 1024 MB (1 GB) | `--max-old-space-size=1024` | Stateless routing, JSON proxying | Low footprint, preserves host RAM |
| **Standard Web Service** | 2048 MB (2 GB) | `--max-old-space-size=2048` | REST APIs, ORM queries, sessions | Baseline default for 64-bit hosts |
| **Frontend Bundler / Compiler** | 4096 MB (4 GB) | `NODE_OPTIONS="--max-old-space-size=4096"` | Webpack, Next.js, TypeScript builds | High transient memory during compilation |
| **Batch File ETL Pipeline** | 8192 MB (8 GB) | `--max-old-space-size=8192` | Parsing multi-megabyte CSV and XML | Extended garbage collection pauses |
| **In-Memory Embedding Generation** | 16384 MB (16 GB) | `--max-old-space-size=16384` | AST graphs, vector index generation | Requires dedicated host RAM to avoid swap |

Understanding these workload baselines prevents both premature out-of-memory crashes and unnecessary memory over-allocation.

## Docker Containers, Kubernetes cgroups, and Non-Heap Memory Traps

The most common production failure when tuning `--max-old-space-size` occurs inside containerized environments like Docker, Amazon ECS, and Kubernetes. Developers allocate 4 GB of RAM to a container and configure the max-old-space-size heap memory limit, only to watch the container process crash unexpectedly under load.

### Linux Control Groups (cgroups) and Kernel SIGKILL

Inside a container, memory limits are enforced not by the JavaScript engine, but by the Linux kernel using control groups (cgroups v1 `memory.limit_in_bytes` or cgroups v2 `memory.max`).

When total container memory consumption exceeds the assigned cgroup boundary, the Linux Out-Of-Memory (OOM) killer intervenes immediately. The kernel issues an uncatchable `SIGKILL` (signal 9) to the container's primary process, terminating it with exit code 137.

This termination mode has critical consequences:

* **No Error Logging:** Because the kernel terminates the process externally, Node.js cannot execute `process.on('uncaughtException')` or `process.on('exit')` listeners. No stack trace is written to standard error.
* **No Diagnostic Artifacts:** Flags such as `--heap-snapshot-on-oom` or diagnostic core dumps fail to generate because the runtime is killed before its internal crash handlers can execute.
* **Abrupt Disconnections:** Active database connections, in-flight HTTP requests, and open file descriptors are severed instantly without graceful termination handshakes.

### The Resident Set Size (RSS) Equation

The fatal mistake is assuming that `--max-old-space-size` controls the total memory footprint of the Node.js process. In reality, `--max-old-space-size` governs only V8's Old Space heap.

Total operating system memory, known as Resident Set Size (RSS), is governed by a broader equation:

$$\text{Total Process RSS} = \text{V8 Heap} + \text{Off-Heap Buffers} + \text{Native C++ Addons} + \text{Libuv Thread Pool} + \text{Thread Stacks}$$

1. **Off-Heap Buffers:** In Node.js, `Buffer` instances allocated via `Buffer.alloc()` or `Buffer.from()` allocate memory outside the V8 heap using internal C++ bindings. Reading massive files, processing image assets, or handling network sockets inflates native buffer allocations without registering in V8's `used_heap_size`.
2. **Native Modules:** Libraries that compile C++ or Rust bindings allocate memory directly on the system heap via `malloc()`, completely invisible to V8's garbage collector.
3. **Libuv Thread Pool:** Asynchronous filesystem operations, DNS lookups, and crypto primitives use worker threads managed by libuv. Each thread consumes dedicated stack memory.
4. **V8 Internal Spaces:** Code Space, Map Space, and New Space consume additional memory alongside Old Space.

If a Docker container has a hard memory limit of 4 GB, and the developer sets the heap limit to 4096 MB, any memory consumed by off-heap buffers or libuv pushes the total process RSS past 4 GB. The Linux kernel terminates the container immediately.

### Sizing Heuristics for Kubernetes Deployments

To deploy Node.js safely inside containers, developers must leave defensive headroom between V8 Old Space and the container's physical cgroup limit.

A dependable production heuristic is reserving at least one-quarter of the container memory budget for non-heap overhead: allocate no more than three-quarters of the container memory limit to the heap flag, reserving the remainder for native buffers, thread stacks, and OS runtime processes.

Here is an example production Dockerfile and Kubernetes deployment pattern:

```dockerfile
FROM node:22-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=3072"
CMD ["node", "dist/server.js"]
```

In the corresponding Kubernetes manifest, align memory requests and limits to provide predictable scheduling and prevent node-level memory pressure:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: document-processor
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: processor
        image: document-processor:latest
        resources:
          requests:
            memory: "2Gi"
            cpu: "1000m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        env:
        - name: NODE_OPTIONS
          value: "--max-old-space-size=3072"
```

Reserving 1024 MB of container memory for off-heap buffers and native runtimes prevents kernel OOM termination, giving V8 the opportunity to manage heap allocations predictably.

## Streaming Alternatives and Architectural Patterns for Large Datasets

While increasing `--max-old-space-size` solves immediate out-of-memory errors, expanding the heap is frequently an architectural band-aid that creates new production liabilities.

### The Latency Cost of Massive Heaps

JavaScript execution in Node.js is fundamentally single-threaded. When V8 performs a major garbage collection pass, it executes a Stop-The-World pause, halting the main event loop to safely trace object graphs and sweep dead memory.

On a standard 2 GB heap, major garbage collection pauses typically complete in tens of milliseconds. As the heap expands to 8 GB, 16 GB, or 32 GB, the duration of Mark-Sweep and Mark-Compact phases grows proportionally:

* **Event Loop Starvation:** Stop-The-World pauses can stretch to several seconds on multi-gigabyte heaps containing millions of small objects.
* **Network Timeouts:** While the event loop is paused, the runtime cannot accept new incoming TCP connections, process HTTP requests, or respond to ping frames.
* **Kubernetes Liveness Failures:** If a major GC pause freezes the event loop longer than the `timeoutSeconds` configured on a Kubernetes liveness or readiness probe, the cluster concludes the pod has deadlocked and restarts the container.

Furthermore, increasing heap limits masks true memory leaks. If an application continuously accumulates objects in global arrays, unpruned caches, or unclosed event emitter listeners, raising the heap limit merely postpones the inevitable crash from two hours to eight hours.

### Stream-Based Processing with Backpressure

When ingesting large datasets such as log archives, CSV exports, or JSON data feeds, the correct pattern is streaming data in discrete chunks rather than buffering entire payloads in memory.

Consider an application that reads a multi-gigabyte JSON dataset. Buffering the entire file with `fs.readFileSync()` requires several gigabytes of heap memory and risks immediate exhaustion. Streaming the file using Node.js stream pipelines processes the dataset with a steady, predictable memory footprint under 60 MB:

```javascript
import fs from 'node:fs';
import readline from 'node:readline';
import { pipeline } from 'node:stream/promises';

async function processLargeDataset(filePath) {
  const readStream = fs.createReadStream(filePath, { highWaterMark: 64 * 1024 });
  const rl = readline.createInterface({ input: readStream, crlfDelay: Infinity });
  let recordCount = 0;
  for await (const line of rl) {
    if (!line.trim()) continue;
    const record = JSON.parse(line);
    await handleRecord(record);
    recordCount++;
  }
  console.log(`Processed ${recordCount} records without heap expansion.`);
}

async function handleRecord(record) {
  // business logic executed here
}
```

By leveraging `readline` and Node.js readable streams, the process operates on one record at a time. Once a record is handled, it becomes eligible for immediate collection in New Space during minor scavenge passes, never promoting into Old Space and eliminating the need to raise `--max-old-space-size`.

### Handling Backpressure with Pipeline Utilities

When data must be transformed and written to downstream storage or network destinations, managing backpressure is essential. If a readable stream reads faster than a writable destination can write, unhandled chunks queue up in memory buffers, triggering the exact heap exhaustion streaming was designed to avoid.

The Node.js `stream/promises` module provides the `pipeline` utility, which automatically regulates backpressure across transform stages:

```javascript
import fs from 'node:fs';
import zlib from 'node:zlib';
import { Transform } from 'node:stream';
import { pipeline } from 'node:stream/promises';

const sanitizeStream = new Transform({
  objectMode: true,
  transform(chunk, encoding, callback) {
    const record = JSON.parse(chunk.toString());
    delete record.privateKey;
    callback(null, JSON.stringify(record).concat(String.fromCharCode(10)));
  }
});

async function compressAndExport(inputPath, outputPath) {
  await pipeline(
    fs.createReadStream(inputPath),
    sanitizeStream,
    zlib.createGzip(),
    fs.createWriteStream(outputPath)
  );
  console.log('Stream pipeline completed with automated backpressure.');
}
```

The `pipeline` function coordinates reading, transforming, compressing, and writing, pausing the readable stream whenever downstream writable buffers fill up.

## Offloading Large Document Corpora to Persistent Workspaces for AI Agents

The challenge of managing large datasets in memory extends directly into modern AI agent workflows. Autonomous agents built on frameworks like Claude Code, OpenClaw, Cursor, and custom Node.js runtimes frequently need to analyze vast corpora of technical documentation, legal contracts, or multi-gigabyte log collections.

### Context Ceilings and the Multi-File Corpus Problem

Developers often encounter friction when attempting to feed large document sets directly into AI assistants. For example, Claude chat sessions accept up to 20 files at up to 500MB each, while Claude Projects accept files up to 30MB each with no fixed file-count cap, bounded only by the model's context window. Claude Projects has no fixed file-count cap, so any fixed project file count is false. The practical ceiling on a project is the context window, and reaching it is when people with a large corpus look for another path.

When an AI agent or developer tries to buffer dozens of technical manuals, PDF archives, or data tables into a single session, two structural bottlenecks emerge:

1. **Context Saturation:** Feeding entire multi-megabyte files into prompt context rapidly consumes available tokens, increasing inference costs and degrading model reasoning quality.
2. **Local Memory Exhaustion:** Node.js agent processes attempting to download, extract, and parse large document archives locally quickly hit V8 heap boundaries, triggering out-of-memory crashes before inference even begins.

### The Large-Corpus Architecture on Fast.io

The solution is decoupling file storage and document indexing from the agent's local memory runtime. Instead of increasing local heap limits to absorb massive datasets, teams offload their document corpus to persistent, organization-owned Fast.io workspaces.

Fast.io provides an intelligent workspace environment built specifically for human and AI agent collaboration:

* **Direct Multi-Gigabyte Ingestion:** Files enter workspaces through reliable chunked uploads supporting up to 25 GB on Starter, 50 GB on Business, and 100 GB on Enterprise plans. Alternatively, teams connect existing enterprise storage using Cloud Sync for Google Drive, Dropbox, Box, and OneDrive (supporting scheduled or on-demand one-way or two-way synchronization, including SharePoint libraries through the OneDrive connector).
* **Automatic Indexing with Intelligence Mode:** Once files land in a workspace, [Intelligence Mode](/product/ai/) indexes them automatically for hybrid search (combining full-text lexical search, semantic vector retrieval, and search-by-metadata-value). AI agents query specific passages on demand, receiving citation-backed answers rather than buffering full documents into local heap memory.
* **Remote Model Context Protocol (MCP) Integration:** Agents connect to Fast.io workspaces over Streamable HTTP at `https://mcp.fast.io/mcp/tools` (or `https://mcp.fast.io/mcp/code` for coding agents) following the [setup documentation](https://mcp.fast.io/docs). Interactive clients sign in with OAuth in the browser and carry no API key, while headless runtimes send an `Authorization: Bearer <api key>` header on the connection. Through standard MCP actions, agents inspect directories, search indexed files, and retrieve relevant excerpts across persistent [workspaces](/product/workspaces/) without drawing down local memory. For implementation patterns, see [Storage for AI Agents](/storage-for-agents/).
* **Structured Data Extraction with Metadata Views:** Rather than writing complex Node.js parsing scripts that buffer massive files to extract dates, invoice totals, or contract counterparties, teams deploy [Metadata Views](/product/document-data-extraction/). Metadata Views turn unstructured PDFs, spreadsheets, and scanned documents into typed, queryable databases (with field types including Text, Integer, Decimal, Boolean, URL, JSON, and Date & Time) that agents query via MCP.
* **Concurrency and Advisory File Leases:** In multi-agent environments, concurrent writes are managed through advisory per-file leases acquired, heartbeated, and released via the MCP storage_manage tool (`lock-acquire`, `lock-status`, `lock-release`), while per-file version history preserves every revision.
* **Activity Monitoring via Events Feed:** To keep distributed agents synchronized without polling heavy files, Fast.io provides a workspace events feed accessible via WebSocket or long-poll subscription, allowing agents to react to file additions and modifications on demand.
* **Branded Deliverables and Real-Time Notes:** Teams collaborate with agents using real-time Collaborative Notes, while finished deliverables are shared through branded Send, Receive, or Exchange portals featuring access expiration and granular permissions.

### Transparent Pricing and Team Plans

Fast.io operates on a paid subscription model. Monthly plans start with a 14-day free trial, which requires a credit card. Subscriptions include Starter, Business, and Enterprise tiers designed for scaling agentic teams:

| Subscription Tier | Monthly Billing | Storage Capacity | Team Seats Included | Monthly AI Credits |
| :--- | :--- | :--- | :--- | :--- |
| **Starter** | $9.99/mo | 250 GB | 3 seats | 100,000 credits |
| **Business** | $49.99/mo | 5 TB | 10 seats | 600,000 credits |
| **Enterprise** | $199.99/mo | 25 TB | 30 seats | 3,000,000 credits |

Fast.io workspace subscription plans also support annual billing options. By offloading document corpora to persistent, indexed workspaces, engineering teams eliminate the need to inflate Node.js heap allocations, prevent container memory termination, and provide AI agents with scalable, semantic access to organizational knowledge.

## Frequently asked questions

### What is the default max-old-space-size in Node.js?

On 64-bit operating systems, Node.js dynamically calculates the default V8 heap limit on startup based on available physical memory, typically defaulting between 2 GB and 4 GB. On 32-bit systems, process memory address space constraints limit the heap allocation to approximately 1 GB. You can inspect your active limit programmatically using v8.getHeapStatistics().heap_size_limit.

### How do I set max-old-space-size permanently?

To configure max-old-space-size permanently across an environment, set the NODE_OPTIONS environment variable to export NODE_OPTIONS='--max-old-space-size=4096' in your shell profile (.bashrc, .zshrc) or system environment settings. For containerized applications, declare ENV NODE_OPTIONS='--max-old-space-size=4096' inside your Dockerfile or container orchestration manifest.

### Can I configure max-old-space-size to 8GB or higher for large heap memory allocations?

Yes, on 64-bit systems you can set max-old-space-size to 8192 MB (8 GB), 16384 MB (16 GB), or higher, provided the host machine possesses sufficient physical RAM. However, setting heap sizes beyond available physical memory triggers severe OS swap thrashing, and larger heaps lead to longer Stop-The-World garbage collection pauses that can freeze the Node.js event loop.

### What causes the Ineffective mark-compacts near heap limit error in Node.js?

This fatal error occurs when V8's Old Space reaches its allocated memory limit and successive Major Garbage Collection passes fail to reclaim enough memory to satisfy an incoming allocation. When V8 determines that continuous garbage collection cycles are recovering negligible memory, it aborts the process to prevent an unrecoverable system hang.

### What is the difference between V8 heap size and Resident Set Size (RSS)?

V8 heap size measures the memory allocated for JavaScript objects, arrays, and closures managed by the V8 garbage collector. Resident Set Size (RSS) represents the total physical RAM occupied by the entire Node.js process, which includes the V8 heap plus off-heap Node.js Buffers, native C++ addons, thread stacks, and the libuv worker pool.

### Why does Kubernetes terminate Node.js with exit code 137 despite setting max-old-space-size?

Exit code 137 indicates that the Linux kernel Out-Of-Memory killer terminated the container via SIGKILL because total container memory exceeded the Kubernetes cgroup limit. This happens when max-old-space-size is set equal to or near the container limit, leaving insufficient headroom for off-heap Buffers, native modules, and OS overhead. Always reserve at least one-quarter of container memory for non-heap usage.

### Does increasing max-old-space-size slow down Node.js performance?

Increasing max-old-space-size does not slow down normal JavaScript execution, but it can significantly increase the duration of Major Garbage Collection cycles. When a multi-gigabyte heap fills up, V8 must trace, sweep, and compact a vastly larger object graph, causing Stop-The-World pauses that can last several seconds and trigger request timeouts.

### When should an engineering team offload files to an external workspace instead of increasing heap limits?

Teams should offload files when processing large document corpora, multi-file datasets, or long-term archives for AI agents. Rather than buffering gigabytes of files in local Node.js memory or cramming text into LLM context windows, offloading to persistent Fast.io workspaces indexes files for semantic search and structured data extraction via remote MCP.

## Sources

- [Node.js: Command-line options documentation](https://nodejs.org/api/cli.html): Node.js configures default V8 JavaScript heap memory limits and process Old Space allocation through the max-old-space-size command flag, where approaching the limit triggers garbage collection and exceeding available physical RAM or container limits causes out-of-memory crashes.
- [Claude Help Center: Upload files to Claude](https://support.claude.com/en/articles/8241126-upload-files-to-claude): Claude chat sessions accept up to 20 files at up to 500MB each, while Claude Projects accept files up to 30MB each with no fixed file-count cap, bounded only by the model's context window across multi-file workspace uploads and document storage plans.

## About Fast.io

Fast.io provides shared workspaces where people and AI agents work on the same files, with built-in semantic search and citation-backed chat over what they hold. Agents reach it through a remote MCP server, a REST API at https://api.fast.io/current/, and a command line client published on npm as @vividengine/fastio-cli. MCP setup is at https://mcp.fast.io/docs: Claude and most MCP clients connect to https://mcp.fast.io/mcp/tools, ChatGPT to https://mcp.fast.io/mcp/operations, and coding agents to https://mcp.fast.io/mcp/code.
