AI & Agents

Docker Memory Limit: Sizing Containers and Preventing OOM Crashes

A Docker memory limit restricts the physical host RAM and swap space a container can allocate using the --memory and --memory-swap flags, terminating offending processes via the kernel Out-of-Memory (OOM) killer if exceeded. Without explicit limits, containers can consume all available host memory, risking system-wide instability. Sizing containers properly requires balancing hard memory caps, soft reservations, and swap controls to protect workloads and prevent exit code 137 crashes.

Derek Labian 16 min read Updated
Architecture diagram illustrating Docker container memory limits, cgroup boundaries, and kernel OOM killer enforcement

What Is a Docker Memory Limit and How Does It Work?

According to official Docker documentation, a container has no resource constraints by default and can consume as much memory as the host kernel scheduler allows. When an unconstrained container exhausts available host memory, the Linux kernel Out-of-Memory (OOM) killer abruptly terminates processes, which can destabilize adjacent containers or the container daemon itself.

A Docker memory limit restricts the physical host RAM and swap space a container can allocate using the --memory and --memory-swap flags, terminating offending processes via the kernel Out-of-Memory (OOM) killer if exceeded. Without explicit boundaries, any container process that experiences a memory leak, unbuffered file ingestion, or runaway compute can monopolize system RAM.

How Linux Control Groups Enforce Memory Limits

Docker enforces memory isolation using Linux Control Groups (cgroups). When the Docker daemon initializes a container through an Open Container Initiative (OCI) runtime like runc, it creates a dedicated cgroup directory inside the Linux virtual filesystem at /sys/fs/cgroup/memory (in cgroup v1) or /sys/fs/cgroup (in cgroup v2).

Modern Linux distributions and container engines rely on cgroup v2, which improves memory accounting over legacy cgroup v1:

  • cgroup v1: Relies on memory.limit_in_bytes to set hard caps and memory.memsw.limit_in_bytes for combined RAM and swap. Accounting between anonymous memory and filesystem caches is separated loosely, frequently causing aggressive page reclamation before hitting hard boundaries.
  • cgroup v2: Implements a unified hierarchy where memory.max acts as the absolute ceiling, memory.high serves as a throttling boundary for soft limits, and memory.swap.max directly governs swap allocation.

When a process inside a container requests memory through system calls like brk or mmap, the Linux kernel memory controller intercepts the allocation. If the allocation causes the container's total usage to cross the configured threshold, the kernel immediately initiates memory reclamation.

Resident Set Size vs Page Cache

A container's total memory footprint consists of two primary components:

  • Resident Set Size (RSS) and Anonymous Memory: Memory allocated on the process heap and stack that is not backed by files on disk. This includes application variables, in-memory buffers, runtime caches, and database connection pools. Anonymous memory cannot be discarded under pressure; it must remain in physical RAM or be swapped to disk.
  • Page Cache: Memory used by the kernel to cache files read from or written to storage. When an application reads configuration files, logs, or code repositories, the kernel retains these blocks in RAM to accelerate future I/O operations.

Under memory pressure, the Linux kernel automatically reclaims clean page cache pages with minimal overhead. However, when anonymous memory allocations alone push the container against its hard limit, the kernel has no reclaimable pages remaining. At that exact moment, the kernel invokes the OOM killer.

Configuring Docker Memory Limits with Runtime CLI Flags

Docker provides several runtime configuration flags with docker run and docker create to govern memory allocation. Configuring these flags ensures containers receive adequate resources during standard operation while preventing runaway consumption during unexpected traffic spikes.

Primary Memory Configuration Flags

Setting resource boundaries requires combining hard caps, soft reservations, and swap controls:

  • -m or --memory: Sets the hard memory limit for the container. The minimum allowed value is 6m (6 megabytes). Common unit suffixes include b (bytes), k (kilobytes), m (megabytes), and g (gigabytes). If an application attempts to allocate physical RAM beyond this figure, the process faces termination.
  • --memory-reservation: Establishes a soft memory limit that must be lower than --memory. Unlike a hard limit, a soft limit does not restrict the container during normal operating conditions. When the host kernel detects low memory or resource contention, Docker forces the container to throttle its usage down to this reservation level.
  • --memory-swap: Governs the total combined amount of physical RAM and swap memory the container can access. Setting --memory-swap requires setting --memory.
  • --memory-swappiness: Tunes the percentage of anonymous pages the kernel can swap out, accepting an integer between 0 (disables anonymous paging entirely) and 100 (maximizes swappability).
  • --oom-kill-disable: Prevents the Linux kernel from killing container processes during an OOM event. This flag should only be used alongside an explicit -m flag. Disabling the OOM killer without a hard memory limit can exhaust host RAM, forcing the kernel to kill essential system daemons.

Understanding the Memory and Swap Matrix

The interaction between --memory and --memory-swap determines how much swap space an application can access:

Configuration Flags Swap Access Total Memory Allocation Operational Behavior
--memory=2g (swap unset) Equal to RAM 4 GB (2 GB RAM + 2 GB Swap) Container uses 2 GB RAM and gets 2 GB default swap buffer on hosts with swap enabled.
--memory=2g --memory-swap=2g Disabled (0 bytes) 2 GB (2 GB RAM + 0 GB Swap) Container is strictly prevented from using swap. Prevents disk I/O thrashing.
--memory=2g --memory-swap=4g 2 GB swap 4 GB (2 GB RAM + 2 GB Swap) Container can allocate 2 GB RAM and up to 2 GB swap before OOM triggers.
--memory=2g --memory-swap=-1 Unlimited host swap 2 GB RAM + Host Swap Ceiling Container uses 2 GB physical RAM and unlimited swap up to available host swap space.

Practical Command Reference

Use these standard commands to configure, update, and monitor container memory boundaries:

  • Launch a container with a strict hard limit and swap disabled:
  docker run -d --name web-service -m 2g --memory-swap 2g nginx:alpine
 
  • Configure a soft reservation with a hard burst ceiling:
  docker run -d --name cache-store -m 4g --memory-reservation 2g redis:alpine
 
  • Dynamically update the memory limit of an active container:
  docker update --memory 3g --memory-swap 3g <container-id>
 
  • Inspect active container memory metrics in real time:
  docker stats --no-stream

Diagnosing Exit Code 137 and the Linux Kernel OOM Killer

When a Docker container terminates abruptly due to memory exhaustion, it exits with process exit code 137. Understanding why this code occurs and how to inspect system logs allows operators to distinguish between container-level limits and host-level resource starvation.

The Anatomy of Process Exit Code 137

Exit code 137 is a composite Unix exit status. Standard Unix shells calculate process termination exit codes by adding 128 to the signal number. Signal 9 represents SIGKILL, the non-catchable kill signal sent by the operating system kernel, which results in exit code 137 (128 plus 9).

When an application process consumes memory beyond its assigned cgroup limit, the Linux kernel cannot fulfill subsequent memory allocations. Because SIGKILL cannot be intercepted, handled, or ignored by application code, the process cannot write a final shutdown log, flush database connections, or complete open writes. The process terminates instantly.

Diagnosing OOM Events with Docker Inspect

To verify whether exit code 137 resulted from the kernel OOM killer or an external manual command (such as docker kill), run docker inspect:

docker inspect <container-name> --format='Status: {{.State.Status}}, ExitCode: {{.State.ExitCode}}, OOMKilled: {{.State.OOMKilled}}'

Evaluating the command output reveals the specific termination mechanism:

  • OOMKilled: true: The container process was terminated directly by the Linux kernel OOM killer because it attempted to exceed its configured --memory ceiling.
  • OOMKilled: false: The container exited with code 137, but the termination was caused by an external SIGKILL. Common triggers include an orchestrator timing out during graceful shutdown after issuing SIGTERM, an external monitoring script invoking docker kill, or a host-level system reboot.

Inspecting Host Kernel Logs

When debugging memory crashes on a Linux host, the kernel ring buffer provides granular details regarding the offending process and memory state at the moment of failure.

Execute dmesg or query journalctl:

dmesg -T | grep -i -E "oom[- ]killer|killed process"

A standard kernel log entry records the exact cgroup path, total memory pages requested, and RSS consumption:

[2026-10-05 09:14:22] Task in /docker/9a3f2b8c1d4e killed as a result of limit
[2026-10-05 09:14:22] memory: usage 2097152kB, limit 2097152kB, failcnt 4218
[2026-10-05 09:14:22] Memory cgroup out of memory: Killed process 28419 (python3) total-vm:3145728kB, anon-rss:2096896kB, file-rss:256kB

In this output, usage reached the configured limit, triggering the cgroup OOM killer. The host system remained stable, and only the offending container process was terminated.

Why AI Coding Agent Sandboxes Hit Docker Desktop Memory Limits

Modern software engineering teams increasingly run autonomous AI coding agents, such as Claude Code, OpenClaw, Codex agents, and containerized devcontainers, inside Docker containers. Sandboxing agent runtimes protects local machines from untrusted code execution, isolates package dependencies, and standardizes development toolchains. However, running AI coding agents in Docker exposes a critical failure point: default Docker Desktop virtual machine memory ceilings.

The Docker Desktop Virtual Machine Bottleneck

On macOS and Windows workstations, Docker does not run containers directly on the host kernel. Because macOS and Windows lack native Linux kernel cgroups, Docker Desktop executes containers inside a lightweight Linux virtual machine managed by the Apple Virtualization framework, Hyper-V, or WSL 2.

By default, Docker Desktop allocates half of the host machine physical RAM to this background Linux VM. On a developer workstation, the Docker VM receives only a portion of system memory, establishing a firm ceiling across all active containers.

Crucially, all containers running within Docker Desktop share this single virtual machine allocation. Even if a developer runs a container without setting explicit --memory flags, that container cannot consume more memory than the total capacity assigned to the Docker Desktop VM.

Why AI File Ingestion Triggers Exit Code 137

AI coding agents do not operate like traditional stateless microservices. To understand an enterprise codebase, answer architectural questions, or execute code refactors, an agent must ingest and analyze large numbers of repository files.

When an agent indexes a local project mounted via a Docker volume bind mount, memory consumption spikes across multiple operations:

  1. Tree-Sitter Syntax Parsing: Creating concrete syntax trees and abstract syntax trees for many source files consumes substantial in-memory heap space.
  2. Recursive File Scans: Running ripgrep or language servers across nested directories rapidly inflates file cache buffers and node runtime heaps.
  3. Local Embedding Generation: Loading local embedding models or vector caches into container RAM requires large contiguous memory buffers.
  4. Package Build Toolchains: Executing multi-stage language builds creates sudden, unbuffered memory spikes.

When an AI agent attempts to index a repository containing extensive file trees, node modules, or technical specification PDFs, its internal memory footprint quickly crosses the memory ceiling of the Docker Desktop virtual machine. The container crashes with exit code 137 mid-session, discarding all active context and leaving uncommitted file artifacts behind.

The Pitfalls of Adjusting the Docker Desktop Slider

Developers often attempt to resolve these OOM crashes by expanding the virtual machine memory allocation in Docker Desktop settings.

While this temporarily provides more headroom for the container, it starves the host operating system. The host operating system begins aggressive swap thrashing, causing the entire developer workstation to become unresponsive, increasing CPU fan noise, and accelerating battery drain. Sizing containers reliably requires decoupling heavy file storage and indexing from the ephemeral container sandbox.

Fastio features

Stop Container OOM Crashes in AI Agent Workspaces

Offload heavy repository indexing from Docker containers to persistent cloud workspaces with hybrid search and Streamable HTTP MCP. Starts with a 14-day free trial.

Decoupling Agent Compute from Storage with Remote MCP Workspaces

Preventing OOM crashes in containerized AI agent workflows requires a fundamental shift in storage architecture. Instead of forcing containers to ingest, index, and cache large document collections locally, teams separate compute execution from persistent file intelligence using cloud-hosted workspaces.

Fast.io provides shared, organization-owned workspaces designed specifically for agentic teams and developer toolchains. By moving project corpora, technical documentation, architectural designs, and client deliverables into a persistent workspace, containerized agents can access and query enterprise files without mounting local cloud stubs or buffering entire repositories in container RAM.

Offloading File Indexing via Remote MCP

Fastio exposes a remote Model Context Protocol (MCP) server over Streamable HTTP at https://mcp.fast.io/mcp/code. Containerized agents connect to the workspace using standard outbound HTTPS requests with Bearer token authentication, eliminating the need to run local background sync daemons inside Docker.

When files are uploaded or synchronized into a Fastio workspace, Intelligence Mode indexes the content automatically. Fastio provides built-in hybrid search that combines:

  • Full-Text Keyword Search: Locates exact code symbols, function signatures, variable names, and error codes.
  • Semantic Vector Search: Discovers relevant documentation and conceptual explanations based on contextual meaning rather than exact word matches.
  • Search-by-Metadata-Value: Filters documents using structured schema attributes extracted across files.

Instead of an agent downloading large documentation sets into container memory to find an API specification, the agent calls Fastio remote search tools. The heavy indexing and retrieval calculations occur entirely server-side. The workspace returns only the relevant excerpts and citations directly into the model context window, keeping the container's physical memory footprint under a few hundred megabytes.

Unified Cloud Storage Synchronization and Versioning

Development teams store critical assets across diverse repositories, including Google Drive, Dropbox, Box, and OneDrive. Fastio bridges these repositories with scheduled or on-demand server-side Cloud Sync for Google Drive, Dropbox, Box, and OneDrive.

Key platform capabilities supporting containerized developer workflows include:

  • Per-File Version History: Every document and code artifact retains complete version records. If concurrent agents make simultaneous edits, both versions land safely in history without data loss.
  • Append-Only Audit Log: Comprehensive logging tracks every file creation, read, edit, and deletion for security reviews and compliance verification.
  • Metadata Views: Teams can transform unstructured files into queryable databases with typed schemas at /product/document-data-extraction/, allowing agents to extract structured fields from contracts, invoices, and specs without manual OCR rules.
  • Collaborative Notes: Real-time co-editing canvases allow human engineers and AI agents to document architectures and review code together with visible cursors.
  • Ownership Transfer: Developers and autonomous agents can configure an organization and workspaces, then securely transfer full ownership to a human stakeholder while retaining administrative access.

Engineering teams can evaluate the platform using a 14-day free trial on monthly plans, which requires a credit card. Plans scale across Starter, Business, and Enterprise tiers depending on team seat requirements and storage needs. Learn more about configuring workspaces for agentic teams at /storage-for-agents/.

Best Practices for Container Memory Allocation and Monitoring

Establishing reliable container memory limits in production requires establishing empirical baselines, configuring declarative resource limits in orchestration files, and monitoring usage metrics over time.

Step-by-Step Memory Sizing Methodology

Follow this four-step process to size containers accurately before deploying to production:

  1. Profile Staging Workloads Without Hard Limits: Deploy the container in a staging environment without a hard -m cap, but apply a generous soft reservation. Run realistic load tests, simulated agent workflows, or batch ingestion tasks.
  2. Measure Peak Working Set Size: Monitor the container using docker stats or container metrics. Note the maximum Resident Set Size (RSS) during peak execution. Ignore reclaimable page cache buffers, which artificially inflate total memory usage.
  3. Establish the Hard Memory Boundary: Set the hard limit (--memory) with sufficient headroom above observed peak RSS, typically providing roughly one and a half times the baseline requirement to absorb unexpected traffic bursts without triggering OOM events.
  4. Configure Soft Limits and Swap: Set --memory-reservation to match the application normal operating baseline. For latency-critical applications like databases or real-time APIs, set --memory-swap equal to --memory to prevent disk swapping.

Declarative Limits in Docker Compose

In production, teams declare memory constraints directly within compose.yaml (Docker Compose Specification):

services:
  agent-runner:
    image: custom-agent:v2.1
    deploy:
      resources:
        limits:
          memory: 4096M
          pids: 200
        reservations:
          memory: 2048M
    restart: on-failure
    environment:
      - NODE_ENV=production
      - FASTIO_MCP_ENDPOINT=https://mcp.fast.io/mcp/code

In this specification:

  • limits.memory: 4096M: Enforces an absolute hard ceiling. The kernel terminates processes that exceed this threshold.
  • reservations.memory: 2048M: Guarantees the container a baseline memory level under host contention.
  • restart: on-failure: Ensures that if an unexpected OOM crash occurs (exit code 137), the container automatically restarts without manual intervention.

Monitoring Container Memory in Production

To monitor container memory and detect impending OOM failures before they disrupt workloads, track these core metrics:

  • cgroup v2 Memory Events: Inspect /sys/fs/cgroup/memory.events directly on Linux hosts. The oom counter tracks how many times memory reached limits, while oom_kill logs total process terminations.
  • cAdvisor and Prometheus: Export container_memory_working_set_bytes and container_oom_events_total. Alerting on working set memory when it approaches the configured memory limit provides advance warning before the OOM killer activates.

Sources

References used to verify factual claims in this guide.

  1. By default, a Docker container has no resource constraints and can use as much memory as the host kernel scheduler allows.

Frequently Asked Questions

How do I set a memory limit on a Docker container?

To set a memory limit on a Docker container, pass the -m or --memory flag to the docker run command followed by a positive integer and a unit suffix such as m (megabytes) or g (gigabytes). For example, running `docker run -d -m 2g nginx` sets an explicit hard memory limit. In Docker Compose, define memory boundaries under deploy.resources.limits.memory in your compose.yaml file.

What is the default memory limit for Docker containers?

By default, Docker containers have no memory limit and can consume as much memory as the host Linux kernel scheduler allows. On native Linux hosts, an unconstrained container can consume all available host physical memory and swap. On macOS and Windows workstations running Docker Desktop, containers are bounded by the total memory allocated to the Docker Desktop Linux virtual machine.

How do I fix Docker exit code 137 out of memory?

To fix Docker exit code 137 caused by out-of-memory errors, first verify that the failure was an OOM event by running docker inspect <container> --format='{{.State.OOMKilled}}'. If the output is true, resolve the issue by increasing the container hard limit with docker run -m or docker update, optimizing application memory usage, or increasing the Docker Desktop virtual machine memory allocation in Docker Desktop settings. For AI agent workloads, offload file indexing to external workspaces.

What is the difference between hard and soft memory limits in Docker?

A hard memory limit, set using -m or --memory, defines an absolute boundary that a container process can never exceed; if crossed, the kernel OOM killer terminates the process. A soft memory limit, set using --memory-reservation, acts as a flexible threshold that is only enforced when Docker detects memory contention or low memory on the host system.

Why do Docker containers crash with exit code 137 when host RAM is still free?

Docker containers crash with exit code 137 even when host RAM is free because the Linux kernel enforces cgroup memory limits strictly at the container level. If a container is launched with an explicit hard memory limit, the kernel OOM killer terminates its processes the moment it attempts to exceed that ceiling, regardless of whether the physical host machine has unused RAM available.

Can I change the memory limit of a running Docker container without restarting it?

Yes. You can dynamically adjust the memory limit of an active Docker container using the docker update command. For example, running docker update --memory 4g --memory-swap 4g <container-id> updates the cgroup memory boundaries immediately without stopping or restarting the running container process.

How does swap space interact with Docker memory limits?

Swap space in Docker is controlled using the --memory-swap flag in conjunction with --memory. If --memory-swap is set to the same value as --memory, swap is completely disabled for the container. If --memory-swap is unset, Docker allows the container to use an amount of swap equal to its --memory limit. Setting --memory-swap to -1 allows unlimited swap up to the host system capacity.

Related Resources

Fastio features

Stop Container OOM Crashes in AI Agent Workspaces

Offload heavy repository indexing from Docker containers to persistent cloud workspaces with hybrid search and Streamable HTTP MCP. Starts with a 14-day free trial.