Storage I/O Throttling in Docker

Storage I/O Throttling in Docker

How Docker uses the Linux kernel's cgroups to cap how fast a container can read from and write to disk — and why one noisy container without a limit can quietly starve every other workload on the same host.

A single container running an aggressive batch job — reindexing a database, decompressing a large archive, or writing verbose logs in a tight loop — can, without anyone changing a single line of application code, make every other container on the same Docker host feel sluggish. The disk itself has not gotten any bigger or smaller; it is simply being asked to do more work than it can handle at once, and by default Docker lets any container ask for as much of that work as it wants. Storage I/O throttling is the feature that fixes this: a way of telling Docker, and through Docker the Linux kernel, “this particular container may read or write disk data no faster than X,” so that no single workload can monopolize a shared, physical resource that every other container on the machine also depends on. This guide covers what storage I/O throttling actually is, how the kernel enforces it, and how to use it well in real multi-tenant systems.

Chapter One

ACore Concepts

Before looking at flags and numbers, it helps to understand what “I/O” means at the kernel level, and why disks in particular need this kind of protection in a way that CPU and memory sometimes do not.

What “I/O” means here

Input/output, or I/O, refers to any operation that moves data between a program and a storage device — reading a file, writing a log line, appending to a database’s data file, or flushing a cache to disk. Every one of these operations ultimately becomes a request sent down to the block device (the physical or virtual disk) beneath the filesystem. Storage I/O throttling operates at exactly that layer: the block device request layer, not the filesystem layer, which is why it can enforce limits consistently no matter which filesystem, database engine, or application is generating the requests.

Everyday Analogy

Picture a shared office printer with one paper tray. Without any rule, whichever employee sends the biggest print job first can occupy the printer for the next twenty minutes, leaving everyone else’s single-page memo stuck in the queue. Storage I/O throttling is like installing a rule that says “no single employee’s job may print faster than 10 pages per minute” — big jobs still get done eventually, but no one job can hog the printer and starve everyone else waiting behind it.

Why disks need this more than most resources

CPU time can be sliced into extremely small intervals and shared fairly with almost no downside; memory can be reserved per-container with hard boundaries that are simple to enforce. A spinning or solid-state disk, by contrast, is a single physical device with a genuinely finite number of operations it can perform per second, and that ceiling is shared by every container on the host regardless of how the containers are otherwise isolated from each other. Without an explicit limit, the kernel’s default behavior is simply “first come, first served, as fast as the hardware allows,” which means one container’s disk-heavy workload can silently degrade every other container’s disk performance even though those other containers never asked for more resources than usual.

i
Two Kinds Of Limits

Docker’s storage I/O throttling actually offers two related but distinct kinds of caps: throughput limits, measured in bytes per second, and operation-rate limits, measured in I/O operations per second (IOPS). A workload doing a few enormous sequential reads and a workload doing thousands of tiny random reads can consume the same throughput while placing very different loads on the device, which is exactly why both units exist as separate controls.

Where this sits among Docker’s resource controls

Storage I/O throttling is one member of a small family of resource controls Docker exposes alongside CPU quotas and memory limits, all of which are really just a friendly interface over the same underlying Linux kernel feature: control groups, or cgroups. Where a CPU quota caps how much processing time a container’s processes may consume, and a memory limit caps how many bytes of RAM a container may hold, an I/O throttle caps how much traffic a container’s processes may send to a specific block device — the same governing philosophy applied to a different, equally finite, shared resource.

2
THROTTLE UNITS (BPS AND IOPS)
2
DIRECTIONS (READ AND WRITE)
1
KERNEL SUBSYSTEM DOING THE ENFORCEMENT (CGROUPS)

Chapter Two

BArchitecture & Components

Several layers cooperate to turn a limit you set on a container into an actual, enforced ceiling on disk traffic.

flowchart TB
    APP["Container Process
(read/write syscall)"] VFS["Virtual Filesystem Layer"] BLK["Block Layer / I/O Scheduler"] CG["cgroup blkio / io Controller
(per-device limits)"] DEV["Physical Block Device"] APP --> VFS --> BLK BLK --> CG CG -->|allowed| DEV CG -.->|queued / delayed if over limit| BLK
Fig. 1 — Where the cgroup I/O controller intercepts a container’s disk requests before they reach hardware
Kernel Subsystem

cgroup blkio / io Controller

The Linux kernel component that tracks and enforces per-device I/O limits for a group of processes, available as the older “blkio” controller (cgroup v1) or the newer unified “io” controller (cgroup v2).

Grouping Mechanism

cgroup

Every container’s processes are automatically placed into their own cgroup when the container starts, giving Docker a ready-made handle to attach resource limits — I/O, CPU, and memory alike — to exactly that set of processes.

Docker Daemon

Runtime Flag Translator

Docker translates the limits you pass at container start time into the specific cgroup filesystem entries the kernel expects, then writes those values before the container’s first process begins running.

Device Identity

Major:Minor Device Number

Because a host may have several physical or virtual disks, every I/O limit is scoped to one specific block device, identified by its kernel-assigned major and minor number, not applied globally across all storage at once.

Storage Driver

overlay2 / other graph drivers

The layer that implements a container’s writable filesystem on top of its read-only image layers; I/O generated by container writes still passes down through the block layer and is throttled the same way as any other disk traffic from that container.

Scope Note

Bind Mounts and Volumes

I/O against a bind-mounted host directory or a named volume is generated by the same container process and cgroup, so the same throttle applies to it exactly as it applies to the container’s own writable layer.

cgroup v1 versus cgroup v2

Two generations of this kernel subsystem exist, and Docker supports both, though the exact controls differ slightly. Under cgroup v1, the relevant controller is called blkio, and it exposes throughput and IOPS limits as separate read and write values per device. Under the newer, unified cgroup v2 hierarchy — now the default on most modern Linux distributions — the equivalent controller is simply called io, and it consolidates resource control into a single hierarchy shared with CPU and memory limits, which also enables more advanced proportional-weighting behavior alongside hard throttling. Docker abstracts most of this difference away behind the same command-line flags, detecting at runtime which cgroup version the host kernel is using.

Chapter Three

CInternal Working

How does the kernel actually slow a container down without simply rejecting its I/O requests outright?

From container start to enforced limit

When a container is started with an I/O limit configured, Docker first creates the container’s cgroup as it normally would, then writes the requested throughput or IOPS value into the appropriate cgroup control file for the target block device, scoped by that device’s major and minor number. From that moment forward, every read or write request the container’s processes issue against that specific device is intercepted by the kernel’s block layer before it reaches the device driver, checked against the cgroup’s configured limit, and either allowed through immediately or held back.

Throttling by delay, not by rejection

A critical detail: the kernel does not throttle a container by failing its read or write calls with an error. Instead, it throttles by delaying the request — the container’s process simply blocks, waiting, until enough time has passed that granting the request would keep the container’s average rate under its configured ceiling. From the application’s point of view, a throttled disk operation looks exactly like a slow disk operation, not a failed one; the system call eventually returns successfully, just later than it would have without the limit. This is deliberate: throttling is meant to simulate a slower device, not an unreliable one, so well-behaved applications experience degraded performance under pressure rather than errors they were never designed to handle.

sequenceDiagram
    participant P as Container Process
    participant BL as Block Layer
    participant CG as cgroup io Controller
    participant D as Disk

    P->>BL: write() request
    BL->>CG: check current rate vs limit
    alt Under limit
        CG->>D: forward request immediately
        D-->>P: write() returns
    else Over limit
        CG->>CG: queue and delay request
        Note over CG: wait until rate drops
under configured ceiling CG->>D: forward request once allowed D-->>P: write() returns (later) end
Fig. 2 — The kernel delays over-limit requests rather than rejecting them, preserving normal application error handling

Throughput limits versus IOPS limits under the hood

A bytes-per-second limit is enforced by tracking a rolling count of bytes transferred and delaying any request that would push that rolling total above the configured ceiling within the measurement window. An IOPS limit works the same way but counts discrete operations rather than bytes, which matters because a device’s true bottleneck is sometimes the number of separate operations it can service per second (especially for spinning disks doing random access) rather than the raw volume of data. A container capped only on throughput could still overwhelm a device by issuing an enormous number of tiny operations that individually move very little data but collectively saturate the device’s operation-handling capacity — which is exactly why Docker lets you configure both kinds of limits independently, and why production systems protecting a shared spinning-disk array often care more about the IOPS ceiling than the throughput ceiling.

Per-device, per-direction granularity

Limits are not a single number covering all disk activity; they are configured per block device and per direction — read throughput, write throughput, read IOPS, and write IOPS can each be set independently, and a container can have different limits on different devices if it has access to more than one. This granularity matters in practice: a container might reasonably need generous read throughput to serve cached data quickly while still needing a tight write throughput cap to prevent a logging bug from filling the host’s disk write bandwidth.

Chapter Four

DData Flow & Lifecycle

Following a single write operation from application code to physical disk, and tracing how limits are configured and can change across a container’s lifetime.

The lifecycle of a throttled request

1

Container Starts With Limits

Docker creates the container’s cgroup and writes the configured device I/O limits into the kernel’s control files before the container’s process begins running.

2

Application Issues I/O

The application inside the container calls an ordinary read or write function, unaware that any limit exists — no code changes or special libraries are required.

3

Block Layer Interception

The kernel’s block layer identifies which cgroup issued the request and checks the current usage rate for that container against its configured limit for the target device.

4

Allow or Delay

Requests under the limit pass straight through to the device; requests that would exceed it are queued and released only once the rolling rate has room again.

5

Completion Returned

Once the device services the request, its result is handed back up through the block layer to the application exactly as an unthrottled request would be, just with added latency if it was delayed.

6

Container Stops

The cgroup and its associated I/O limit configuration are torn down along with the container, freeing the full device bandwidth back to the remaining containers on the host.

“Throttling does not make a disk faster or slower — it makes one tenant’s share of a fixed-speed disk predictable, at the deliberate cost of that tenant’s own ceiling.”

Changing limits on a running container

Because the limit lives in the cgroup’s control files rather than being baked into the container image or its startup configuration permanently, an operator can update a running container’s I/O limits without restarting it, and the new ceiling takes effect on the very next I/O request the kernel evaluates. This makes I/O throttling a genuinely dynamic tool: a limit can be tightened temporarily during a known high-traffic period across the rest of the host, then relaxed again afterward, all without any container downtime.

Interaction with the host’s own I/O

It is worth noting that cgroup-based I/O throttling only governs processes running inside a cgroup — meaning it constrains containers relative to each other and relative to any other cgroup-scoped workload, but does not, by itself, throttle processes running directly on the host outside any container. A host process performing heavy disk activity outside the container cgroup hierarchy can still compete for the same physical device bandwidth that containers are being carefully rationed from, which is why comprehensive I/O governance on a busy host usually involves placing host-level services into their own cgroups as well, not just the containers.

Chapter Five

EAdvantages, Disadvantages & Trade-offs

Throttling trades away a container’s maximum possible speed in exchange for predictability for everyone sharing the same disk — understanding that exchange is the whole point of using the feature well.

Advantages

  • Prevents one “noisy neighbor” container from starving disk access for every other workload on the host
  • Makes disk-related latency for other services predictable and testable under load
  • Lets operators safely co-locate a bursty batch job with latency-sensitive services on the same host
  • Changes take effect on running containers without requiring a restart
  • Degrades gracefully — throttled I/O is slow, not broken, so applications keep functioning

Disadvantages

  • A limit set too low can bottleneck a legitimate, healthy workload and cause misleading latency alerts
  • Requires knowing the underlying device’s real capacity — a limit set above hardware capability protects nothing
  • Adds a layer of indirection that makes disk performance harder to reason about during debugging
  • cgroup v1 and v2 differ enough that limits configured on one host may not translate identically to another
  • Does not, by itself, guarantee fairness among containers that have no explicit limit set at all

Hard limits versus proportional weighting

Storage I/O throttling as described in this guide is a hard ceiling: a ceiling a container simply cannot cross regardless of how idle the rest of the host’s disk capacity currently is, even if no other container is using the disk at that exact moment. This is different from proportional weighting, a related but distinct cgroup feature that instead says “when contention exists, give this container twice the share of available bandwidth as that one,” without capping either container’s absolute maximum when the disk is otherwise idle. The trade-off is simple: a hard limit gives the strongest guarantee against worst-case interference but can waste idle capacity, while proportional weighting uses capacity more efficiently but offers a weaker guarantee under simultaneous heavy contention. Production systems facing predictable, adversarial noisy-neighbor risk generally prefer hard limits; systems optimizing for overall throughput under normal, cooperative conditions often prefer weighting instead.

i
Practical Guidance

Set limits based on measured, real device capacity — benchmark the actual disk or cloud volume rather than trusting a manufacturer’s marketing figure — and leave meaningful headroom below that measured ceiling when dividing capacity across containers, since real workloads rarely run in perfect isolation from host-level I/O like log rotation or the storage driver’s own background housekeeping.

Chapter Six

FSecurity

Storage I/O throttling is as much a security and availability control as it is a performance-tuning knob, particularly on hosts running workloads from different trust levels.

Throttling as a denial-of-service defense

Any multi-tenant Docker host that runs containers from different teams, customers, or trust levels without I/O limits is implicitly exposed to a disk-based denial-of-service condition: a single misbehaving or maliciously crafted container can issue an unbounded volume of disk writes, degrading or effectively halting disk-dependent operations for every other container sharing that device, without needing any special privilege beyond ordinary filesystem access. Because this can happen entirely by accident — a runaway logging loop, a misconfigured cache eviction policy, a bug that repeatedly rewrites the same large file — treating I/O limits purely as a performance nicety rather than a security boundary understates the risk they mitigate.

Security Analogy

Unthrottled shared disk access is like a shared apartment building’s single water main with no per-unit flow restrictor. One tenant leaving a tap running at full pressure can drop water pressure for every other unit in the building, even though nothing was technically stolen or broken — the resource was simply consumed faster than the shared infrastructure could keep up with everyone’s needs at once.

Limits do not protect data, only capacity

It is worth being precise about scope: I/O throttling constrains how fast a container can move data, not what data it is allowed to read or write, and not whether it can read another container’s files. A container with a very tight I/O limit is just as capable of reading sensitive files it has legitimate filesystem access to as one with no limit at all — it will simply do so more slowly. Teams sometimes conflate resource throttling with access control; they solve different problems, and a well-secured multi-tenant host needs both filesystem-level access restrictions (volumes, mount permissions, user namespaces) and I/O throttling working together, not either one alone.

ADR-021: Default I/O Ceiling For All Untrusted Batch JobsAdopted
Context

A shared build-and-test host regularly ran ad-hoc containers submitted by many different engineering teams, several of which had, in the past, unintentionally saturated the shared disk with aggressive test-data generation scripts.

Decision

Every container launched through the shared job runner was given a conservative default write-throughput and write-IOPS ceiling unless a team explicitly justified and requested a higher limit for a specific job.

Consequence

Occasional individual jobs run measurably slower than their theoretical best case, but the host’s overall disk-related incident rate dropped sharply, since no single submitted job could again degrade every other team’s concurrently running jobs.

Observing whether a throttle is actively shaping behavior — and distinguishing a throttled container from a genuinely slow one — requires looking at the right layer of the stack.

Signals that indicate active throttling

The clearest direct signal that a container is being throttled is a rising count of “throttled” or “queued” events reported by the kernel’s cgroup I/O statistics for that container’s cgroup, alongside a measured throughput or IOPS value that sits persistently at, or just under, the configured limit rather than fluctuating naturally with workload demand. A container whose I/O usage graph shows a flat, hard ceiling exactly matching its configured limit, with request latency spiking whenever the application tries to push past that ceiling, is a textbook throttling signature rather than a symptom of a genuinely overloaded or failing disk.

Observed SymptomLikely Cause: ThrottlingLikely Cause: Genuine Disk Overload
Throughput ceilingFlat, exactly matches configured limitVariable, tracks overall host contention
Other containers on hostUnaffected, performing normallyAlso degraded simultaneously
cgroup throttle countersActively incrementingNot present or not incrementing
Removing the limitImmediately resolves the symptomNo change in behavior

Building dashboards around throttling

Effective dashboards separate two related but distinct questions: “how much I/O is this container using” and “how often is this container being held back by its limit.” The first is answered by raw throughput and IOPS metrics per container; the second requires the cgroup’s own throttling counters, which many general-purpose container monitoring tools surface only if explicitly configured to collect cgroup I/O statistics rather than just aggregate disk usage. Teams that track only raw usage often miss the more actionable signal — a container consistently pinned at its ceiling with a growing throttle-event count is telling you its limit may need to be raised, well before application-level latency alarms would otherwise fire.

!
Alerting Pitfall

An alert defined as “page us if container write latency exceeds N milliseconds” can fire constantly and unhelpfully for a container that is simply operating correctly against an intentionally conservative I/O limit. Pair any latency-based alert with a check against the container’s configured throttle so on-call engineers can immediately distinguish “working as configured” from “something is actually broken.”

Chapter Eight

HDesign Patterns & Anti-patterns

Recurring, well-tested ways teams apply I/O throttling in production, and the recurring mistakes that undermine it.

Pattern

Tiered Limits By Workload Class

Latency-sensitive services get a generous, rarely-hit ceiling; background batch and cleanup jobs get a deliberately conservative one, so the host’s disk capacity is allocated in line with actual business priority rather than arrival order.

Pattern

Burst-Then-Throttle Onboarding

New batch jobs are first run with no limit in a controlled test environment to measure their natural I/O profile, and a production limit is then set just above that measured baseline, avoiding both over-restriction and under-protection.

Pattern

Per-Tenant Ceiling on Shared Hosts

Every tenant’s containers on a shared, multi-tenant host receive an identical default I/O ceiling as a baseline fairness guarantee, independent of what any individual tenant’s workload happens to be doing.

Anti-pattern

Copy-Pasted Limits Across Different Disks

Applying the same numeric throughput limit to containers running on hardware with very different real disk capacities, producing limits that are meaninglessly loose on fast storage and crippling on slow storage.

Anti-pattern

Setting a Limit and Never Revisiting It

Leaving an I/O ceiling chosen years earlier in place after the workload, the disk hardware, or the number of co-located containers has changed substantially, quietly turning a once-reasonable limit into an active bottleneck.

Design pattern in practice: database plus backup sidecar

A common production pattern gives the primary database container a generous I/O ceiling to preserve query latency, while a companion backup or archival sidecar container performing large sequential writes runs under a much tighter ceiling — ensuring a nightly backup job can never compete meaningfully with live query traffic for the same underlying disk, even if both happen to run at the same time.

Concrete guardrails for setting and maintaining I/O limits that actually protect a host rather than just existing on paper.

Do

Benchmark before choosing a number

Measure the real, sustained throughput and IOPS capacity of the actual disk or cloud volume in use before setting any limit, rather than guessing or reusing a figure from unrelated hardware.

Do

Set both throughput and IOPS limits together

A throughput-only limit leaves a container free to overwhelm a device with a flood of tiny operations, so pair it with an IOPS ceiling whenever the workload’s access pattern is not purely large sequential transfers.

Do

Re-baseline limits after hardware or topology changes

Revisit configured ceilings whenever the underlying storage is upgraded, downgraded, or when the number of co-located containers on a host changes meaningfully.

Avoid

Treating throttling as a substitute for capacity planning

A host that is chronically over-subscribed on disk capacity will simply have every container running at a throttled ceiling permanently; limits manage contention, they do not create capacity that does not exist.

Avoid

Ignoring host-level I/O outside containers

Forgetting that log rotation, backup agents, or monitoring tools running directly on the host also consume the same physical disk bandwidth that container limits are trying to protect.

Avoid

Setting identical limits for read and write without justification

Read and write workloads often have genuinely different real-world requirements; applying one blanket number to both directions can under-protect one and over-restrict the other.

Checklist Before finalizing an I/O limit for a production container, confirm: (1) the limit is based on a real, measured benchmark of the target device, (2) both throughput and IOPS are addressed where relevant, (3) the limit accounts for other containers and host processes sharing the same device, and (4) monitoring can distinguish “operating correctly under its limit” from “genuinely degraded.”

Chapter Ten

JReal-World & Industry Examples

Storage I/O throttling shows up wherever a single physical disk is shared by workloads that must not be allowed to interfere with one another.

Cloud Platforms

Multi-Tenant Container Hosting

Large-scale container hosting platforms apply per-tenant I/O ceilings as a core part of their fairness guarantees, ensuring one customer’s workload cannot degrade disk performance for every other customer’s containers on the same physical node.

CI/CD Systems

Shared Build Runners

Continuous-integration platforms running many concurrent build containers on shared build machines commonly throttle each job’s disk I/O to keep build times predictable regardless of how many other builds happen to be running at the same moment.

Data Platforms

Mixed OLTP and Batch Workloads

Organizations running latency-sensitive transactional databases alongside heavy analytical or batch-processing jobs on shared infrastructure use I/O throttling to keep the transactional workload’s latency stable during analytical processing windows.

Orchestration

Kubernetes Ephemeral Storage Limits

Kubernetes builds its own storage resource controls for pods directly on top of the same underlying cgroup I/O mechanism this article describes, exposing it through higher-level pod resource specifications.

1
PHYSICAL DISK, MANY CONTAINERS
2
SEPARATE AXES: THROUGHPUT AND IOPS
0
CODE CHANGES REQUIRED IN THE APPLICATION

Chapter Eleven

KFrequently Asked Questions

Q1Does an I/O throttle apply to a container’s read-only image layers, or only to its writable layer?
It applies to all disk activity generated by that container’s processes against the throttled device, including reads from its image layers, its writable layer, and any mounted volumes on that same device.
Q2Will a throttled container’s application see errors once it hits its limit?
No. The kernel delays over-limit requests rather than failing them, so the application experiences added latency, not I/O errors, assuming no unrelated timeout elsewhere in the system is triggered by that added delay.
Q3Can a limit be applied to network-attached or cloud block storage the same way as local disk?
Yes, as long as the storage is presented to the host as a standard block device with its own major and minor device number, the same cgroup mechanism applies regardless of whether the underlying storage is local or network-attached.
Q4Is I/O throttling the same thing as disk quota (limiting how much space a container can use)?
No, they address different problems. I/O throttling limits how fast data moves; disk space quotas limit how much total data a container can store, and the two are configured through entirely separate mechanisms.
Q5Do I/O limits get inherited automatically by child containers or processes spawned inside the container?
Yes. Because the limit is attached to the container’s cgroup rather than to any single process, every process running inside that container, including ones spawned after the container starts, is bound by the same limit.
Q6Can throttling limits differ between cgroup v1 and cgroup v2 hosts running the same Docker configuration?
The configured numeric values behave the same conceptually, but the underlying enforcement mechanics differ slightly between the two cgroup versions, so behavior at the margins under heavy contention can vary depending on which version the host kernel is using.

Chapter Twelve

LSummary and Key Takeaways

Storage I/O throttling turns a shared, finite physical resource into something every container on a host can rely on predictably.

Key Takeaways

  • Storage I/O throttling caps how fast a container’s processes may read or write to a specific block device, enforced by the Linux kernel’s cgroup I/O controller rather than by Docker itself.
  • Limits can be set independently for throughput (bytes per second) and operation rate (IOPS), and independently for reads and writes, giving fine-grained control over different workload profiles.
  • The kernel enforces limits by delaying over-limit requests rather than rejecting them, so throttled applications experience added latency, not errors.
  • Without explicit limits, a single disk-heavy container can silently degrade performance for every other container sharing the same physical device — the classic “noisy neighbor” problem.
  • Effective limits require measuring real device capacity first; a limit set without benchmarking either protects nothing or needlessly bottlenecks a healthy workload.
  • This mechanism underlies fairness guarantees across multi-tenant cloud hosting, shared CI/CD build runners, and orchestration platforms like Kubernetes, all built on the same underlying cgroup I/O primitive.