CPU and Memory Limits in Docker: Giving Every Container a Fair Share

CPU and Memory Limits in Docker: Giving Every Container a Fair Share

A complete, beginner-to-intermediate guide to how Docker controls how much processing power and memory a container can use, why this matters on any machine running more than one container, and what happens when a limit is actually hit.

Picture a shared kitchen in a large apartment building. If one resident is allowed to use every burner on the stove and every inch of counter space whenever they like, with no limits at all, everyone else’s cooking gets pushed aside or ruined the moment that one resident decides to make an elaborate meal. A well-run shared kitchen has house rules — each resident gets a fair, predictable amount of space and time. Docker’s CPU and memory limits play exactly this role for containers sharing a single machine’s processing power and memory. This guide explains, from first principles, how those limits actually work, what happens when they’re hit, and why setting them thoughtfully is one of the more consequential decisions in running containers reliably.

1Why Resource Limits Exist at All

Before the mechanics, let’s understand the problem limits are solving.

A single physical or virtual machine has a fixed, finite amount of processing power and memory available. Containers running on that machine, unless told otherwise, are free to use as much of that shared processing power and memory as they can get their hands on. This works fine when only one container is running, but becomes a real problem the moment several containers share the same machine — one poorly behaved or unexpectedly busy container can consume so much processing power or memory that every other container on that machine suffers, slows down, or even crashes as a direct result.

Simple Analogy

Think of processing power and memory as the electrical circuit in an old house, shared across every room. If one room plugs in enough power-hungry appliances without any limit, it can trip the breaker or starve every other room of power, even though those other rooms did nothing wrong themselves. CPU and memory limits are like assigning each room its own dedicated circuit breaker, so one room’s excess can never take down the whole house.

This problem even has a common name: the noisy neighbor problem, describing exactly this situation where one workload’s resource usage negatively affects unrelated workloads simply because they happen to share the same underlying machine. Resource limits are Docker’s primary tool for preventing this from happening.

CPU Limit

Processing Power Cap

A ceiling on how much processing capacity a single container is allowed to use.

Memory Limit

Memory Cap

A ceiling on how much memory a single container is allowed to use.

Noisy Neighbor Problem

What Limits Prevent

One workload consuming so many shared resources that unrelated workloads on the same machine are negatively affected.

Shared Machine

Why It Matters

Any machine running more than one container is a shared environment where resource limits become genuinely important.

2How This Is Actually Enforced: Control Groups

The underlying operating system feature that makes resource limits possible in the first place.

Docker doesn’t invent the mechanism for enforcing resource limits itself. Instead, it relies on a Linux kernel feature called control groups, often abbreviated as cgroups, which allows a group of processes — such as everything running inside a container — to have specific, enforceable limits placed on the resources they’re allowed to consume. When you set a CPU or memory limit on a container, Docker is really just configuring this underlying kernel feature on your behalf.

flowchart LR
    A["You set a CPU or memory limit"] --> B["Docker configures a control group"]
    B --> C["Linux kernel enforces the limit"]
    C --> D["Container's actual resource usage is capped"]
    style D fill:#fef2f2,stroke:#dc2626
        
FIG 1 — Docker’s resource limits are implemented through the Linux kernel’s own control group feature, which does the actual enforcement.

This is genuinely useful to understand, because it means resource limits aren’t a soft suggestion Docker merely tries to honor — they’re enforced directly by the operating system itself, at a fundamental level, making them a reliable, hard guarantee rather than a best-effort courtesy.

3Understanding CPU Limits: Shares vs. Hard Caps

Docker actually offers two related but distinct ways of controlling CPU usage.

CPU control comes in two meaningfully different flavors. A relative share setting doesn’t impose an absolute ceiling at all — instead, it describes a container’s priority relative to other containers whenever processing power is genuinely scarce and being contended for. A hard cap setting, by contrast, imposes a genuine, absolute ceiling on how much processing capacity a container can use, regardless of whether other containers happen to be busy or idle at the time.

ApproachWhat It Actually DoesWhen It Applies
Relative ShareSets priority relative to other containers, not an absolute ceilingOnly matters when the machine’s CPU is genuinely under contention
Hard CapSets an absolute maximum amount of processing capacityAlways enforced, regardless of whether the machine is busy or idle
Simple Analogy

A relative share is like giving certain guests priority boarding at a busy airport gate — it only matters when the gate is crowded; if there’s plenty of room, everyone boards without any difference. A hard cap is like a strict maximum baggage weight limit that applies to every single passenger, every single time, whether the flight is full or nearly empty.

This distinction genuinely matters when deciding how to control CPU usage for a given container — a relative share is well suited to expressing “this is more important than that, when things get busy,” while a hard cap is well suited to expressing “this must never use more than a specific amount, no matter what.”

4What Happens When a CPU Hard Cap Is Reached: Throttling

Unlike memory, exceeding a CPU limit doesn’t crash anything — it simply slows things down.

When a container with a hard CPU cap tries to use more processing capacity than its limit allows, the kernel doesn’t terminate anything or produce an error. Instead, it simply prevents the container’s processes from being scheduled to run for brief moments, effectively slowing them down to stay within the allowed ceiling — a behavior called throttling. From the application’s own point of view, it simply appears to run more slowly than it otherwise would, without any crash or visible error being raised.

i
Good to Know

Because CPU throttling doesn’t produce an obvious crash or error, it can be a surprisingly sneaky source of unexplained slowness. An application that seems mysteriously sluggish under load, despite the machine itself appearing to have spare capacity overall, is a common symptom worth checking against a possibly too-tight CPU limit on that specific container.

This is a fundamentally gentler failure mode than what happens when a memory limit is exceeded, which — as the next several chapters explain — behaves in a much more abrupt and consequential way.

5Understanding Memory Limits

A memory limit works very differently from a CPU limit in one crucial way.

Unlike processing power, which can be shared and divided flexibly moment to moment through throttling, memory is a much less flexible resource — once a piece of memory is being genuinely used to hold data, it generally can’t be partially “slowed down” the way processing time can. This fundamental difference is exactly why exceeding a memory limit behaves so differently from exceeding a CPU limit, and why memory limits deserve extra care when being set.

Memory Limit

An Absolute Ceiling

The maximum amount of memory a container’s processes are allowed to actually hold at once.

Memory Reservation

A Softer Target

A gentler, lower target Docker tries to respect under contention, without it being a hard, absolute ceiling.

This distinction between a genuine hard limit and a softer reservation mirrors the CPU share-versus-cap distinction from Chapter 3, and understanding both pairs together makes the overall system of resource control feel much more coherent than trying to memorize each setting in isolation.

6The Out-of-Memory Killer

What actually happens when a container tries to exceed its hard memory limit.

When a container’s processes attempt to use more memory than its configured hard limit allows, the Linux kernel intervenes directly through a mechanism often called the out-of-memory killer, or OOM killer. Rather than allowing the memory request to succeed and risk destabilizing the whole machine, the kernel instead forcibly terminates a process consuming too much memory — typically the very process that triggered the problem — immediately and without warning.

sequenceDiagram
    participant Process as Container's Process
    participant Kernel as Linux Kernel

    Process->>Kernel: Requests more memory than the limit allows
    Kernel->>Kernel: Out-of-memory killer intervenes
    Kernel->>Process: Process forcibly terminated
    Note over Process: No graceful shutdown opportunity given
        
FIG 2 — Exceeding a hard memory limit triggers immediate, forceful termination by the kernel’s out-of-memory killer, with no graceful shutdown involved.
!
Serious Difference From CPU Throttling

Unlike CPU throttling, described in Chapter 4, which merely slows a container down without crashing anything, exceeding a hard memory limit results in an abrupt, forceful termination with absolutely no opportunity for graceful shutdown at all. This is a meaningfully harsher consequence, and it’s precisely why setting memory limits thoughtfully deserves extra care.

7Recognizing an Out-of-Memory Termination: Exit Code 137

Connecting this behavior back to the container states and exit codes covered elsewhere.

As discussed in the context of container states, an exit code left behind after a container stops is a valuable clue about how and why it stopped. A container terminated by the out-of-memory killer characteristically leaves behind exit code 137, one of the strongest, most reliable signals that a container hit its memory limit and was forcibly killed, rather than exiting through any kind of graceful, voluntary shutdown.

i
Good to Know

Seeing exit code 137 repeatedly for a particular service is a strong, specific hint worth investigating directly: either that service’s memory limit is genuinely too tight for its real workload, or the application itself has a memory-related problem, such as gradually consuming more and more memory over time without ever releasing it.

8Memory Limits and Swap Space

An additional, related setting that affects exactly how much memory pressure a container is allowed to withstand before being killed.

Swap space is a portion of disk storage a machine can use as a slower, overflow extension of its actual memory, when genuine memory runs low. Docker allows configuring how much additional swap space, beyond the hard memory limit itself, a container is permitted to use before the out-of-memory killer intervenes, giving a small amount of additional breathing room under temporary memory pressure, at the cost of significantly slower performance while that swap space is actively being used.

SettingWhat It Controls
Memory LimitThe genuine ceiling on fast, real memory usage
Swap AllowanceAdditional, much slower overflow space permitted beyond that ceiling

Relying heavily on swap space is generally a sign that a memory limit may be set too tightly for a workload’s genuine needs, since the performance cost of frequently spilling over into slow disk-backed swap is usually far worse than the cost of simply allocating a somewhat higher genuine memory limit in the first place.

9Security: Limits as Protection, Not Just Fairness

Resource limits aren’t only about performance — they’re a genuine security boundary too.

Beyond the everyday “noisy neighbor” problem described in Chapter 1, resource limits also serve a meaningful security function. A container that’s been compromised, or one running unexpectedly malicious code, could otherwise attempt to consume every available resource on a machine deliberately, effectively denying service to every other legitimate workload sharing that same machine. Properly configured resource limits cap the damage any single container — compromised or otherwise misbehaving — can inflict on the rest of the system.

!
Worth Remembering

A container with no resource limits configured at all is, from a security standpoint, trusted with unrestricted access to the entire machine’s processing power and memory. On any machine running more than one container, or any container handling genuinely untrusted workloads, leaving resource limits unset is a real risk, not merely an efficiency oversight.

10Monitoring Resource Usage

Setting limits well requires actually understanding a container’s genuine, real-world resource usage first.

Choosing sensible CPU and memory limits isn’t a matter of guessing — it depends on observing how much a container genuinely uses under realistic conditions, including its typical usage and its occasional peaks, over a meaningful period of time. Monitoring resource usage continuously, both to inform initial limit choices and to catch a workload’s needs drifting over time, is an essential companion practice to setting limits in the first place.

Typical Usage
What a container consumes under normal conditions
Peak Usage
The highest usage seen during genuine load
Throttling Events
How often a CPU limit is actually being hit

Limits set without any real observation behind them are essentially a guess, and guesses tend to be wrong in one of two costly directions: either too tight, causing throttling or out-of-memory terminations under genuinely normal conditions, or too loose, providing little real protection against the noisy neighbor problem they were meant to solve in the first place.

11Design Patterns for Setting Limits

A few recurring, sensible strategies teams use when deciding on resource limits.

Observe First, Then Set

A new service runs unrestricted for an initial observation period specifically to gather genuine usage data, as discussed in Chapter 10, before any hard limits are actually applied based on real evidence rather than guesswork.

Generous Headroom Above Typical Usage

Limits are set with meaningful headroom above genuinely observed typical and peak usage, reducing the chance of routine throttling or terminations from ordinary, expected fluctuations in workload.

Tighter Limits for Untrusted or Experimental Workloads

Containers running less-trusted code, or genuinely experimental workloads whose resource needs aren’t yet well understood, are given deliberately tighter limits as a protective, precautionary default.

12CPU and Memory Limits Together: A Combined View

Seeing how the two kinds of limits relate, side by side, ties the whole picture together.

AspectCPU LimitsMemory Limits
Soft VersionRelative share, priority-basedReservation, a softer target
Hard VersionHard cap, absolute ceilingHard limit, absolute ceiling
Consequence of ExceedingThrottling, gradual slowdownForceful termination by the out-of-memory killer
Warning SignsUnexplained sluggishness under loadExit code 137, as covered in Chapter 7

Seeing these two side by side highlights an important asymmetry worth internalizing: getting a CPU limit slightly wrong tends to produce a recoverable, if annoying, slowdown, while getting a memory limit meaningfully wrong tends to produce an abrupt, unrecoverable crash — which is precisely why memory limits generally deserve more conservative, carefully considered headroom than CPU limits do.

13Best Practices and a Common Anti-Pattern

A short checklist for setting resource limits that hold up under real, everyday conditions.

1

Set both CPU and memory limits for every production container

As explained in Chapter 9, unrestricted containers are both a performance and a genuine security risk on any shared machine.

2

Base limits on genuinely observed usage, not guesses

As covered in Chapter 10, real monitoring data leads to far more reliable limits than assumptions.

3

Treat exit code 137 as a specific, actionable signal

As discussed in Chapter 7, it points directly toward a memory limit issue worth investigating rather than a mysterious, unrelated crash.

4

Be more conservative with memory headroom than CPU headroom

As explained in Chapter 12, the consequence of exceeding a memory limit is meaningfully harsher than exceeding a CPU limit.

ANTI-PATTERN-01 Avoid
Problem

A team runs every container on a shared machine with no CPU or memory limits configured at all, reasoning that the machine “has plenty of resources” overall.

Why It’s Harmful

As explained in Chapters 1 and 9, having plenty of resources overall doesn’t prevent one busy or misbehaving container from temporarily consuming far more than its fair share, degrading or crashing every other container sharing that machine — precisely the noisy neighbor problem resource limits exist to solve, and a real, unaddressed security exposure alongside it.

Correct Approach

Set deliberate, evidence-based CPU and memory limits on every container sharing a machine, regardless of how much spare capacity the machine appears to have overall, since the problem is about fair distribution and containment, not raw total capacity.

14Real-World and Industry Examples

Resource limits are a standard, expected practice across production container deployments in nearly every industry.

Multi-Tenant Platforms

Isolating Different Customers’ Workloads

Platforms hosting workloads for many different customers on shared infrastructure rely heavily on resource limits to guarantee one customer’s activity can never degrade another’s experience.

Continuous Integration

Predictable Build Environments

Build servers running many concurrent build containers apply resource limits so a single unusually resource-hungry build doesn’t slow down or starve every other build running alongside it.

Financial Services

Guaranteed Performance for Critical Services

Latency-sensitive services are given generous, carefully tuned resource guarantees precisely to protect them from being negatively affected by less critical workloads sharing the same infrastructure.

Development Teams

Local Testing of Production Limits

Teams intentionally test their applications under the same resource limits planned for production, specifically to catch memory-related crashes or CPU throttling issues before they reach real users.

15Frequently Asked Questions

Q1What happens if a container tries to use more CPU than its limit allows?

As explained in Chapter 4, it’s throttled — gently slowed down by the kernel — rather than crashed or terminated.

Q2What happens if a container tries to use more memory than its limit allows?

As covered in Chapters 6 and 7, the out-of-memory killer forcibly and immediately terminates the offending process, typically leaving behind exit code 137.

Q3What’s the difference between a memory limit and a memory reservation?

As explained in Chapter 5, a limit is a genuine, absolute ceiling, while a reservation is a softer target respected mainly under contention, not strictly enforced.

Q4Does relying on swap space fix a too-tight memory limit?

Not really — as discussed in Chapter 8, heavy swap usage tends to signal the memory limit itself is too tight, since swap comes with a significant performance cost of its own.

Q5Are resource limits only about performance, or do they matter for security too?

Both — as explained in Chapter 9, limits also cap the damage a compromised or misbehaving container can inflict on the rest of a shared machine.

Q6How should I decide what limits to actually set?

As covered in Chapter 10, base limits on genuinely observed usage data over a meaningful period, rather than guessing, and build in reasonable headroom as discussed in Chapter 11.

16Summary and Key Takeaways

CPU and memory limits are Docker’s tools for keeping containers from starving one another out on a shared machine, enforced directly through the Linux kernel’s own control group feature. CPU limits and memory limits, while conceptually similar, behave very differently when exceeded — CPU pressure results in gentle throttling, while memory pressure results in abrupt, forceful termination via the out-of-memory killer, recognizable by exit code 137. Setting these limits thoughtfully, based on genuinely observed usage rather than guesswork, protects both the performance and the security of every workload sharing a machine, making resource limits one of the more consequential, easy-to-overlook details in running containers reliably at any real scale.

Key Takeaways

  • Resource limits prevent the noisy neighbor problem — stopping one container from starving out others sharing the same machine.
  • Limits are enforced by the Linux kernel’s control groups — a hard, reliable guarantee, not a soft suggestion.
  • CPU has both relative shares and hard caps — priority-based versus an absolute ceiling.
  • Exceeding a CPU limit causes throttling, not a crash — a gentler, but sometimes sneaky, consequence.
  • Exceeding a memory limit triggers the out-of-memory killer — an abrupt, forceful termination with no graceful shutdown.
  • Exit code 137 is a specific, actionable signal — strongly pointing toward a memory limit issue.
  • Limits are also a genuine security boundary — capping the damage a compromised container can do to a shared machine.