Storage Performance Tuning in Kubernetes

Storage Performance Tuning in Kubernetes

Why a database that flies on a laptop can crawl in a cluster, what IOPS, throughput and latency really mean, and how to find and fix the slow spot between your Pod and the disk.

Picture a busy restaurant kitchen. The chefs are quick, the stoves are hot and the recipes are perfect. Yet customers wait forty minutes for a plate of pasta. Where is the delay? It is not in the cooking. It is in the pantry. The ingredients sit in a storeroom down a long corridor, shared with three other restaurants, and a single door lets only one person through at a time. The chefs spend most of their day standing and waiting for flour.

Applications in Kubernetes often live exactly like those chefs. The processor is idle, the memory is fine, the code is clean, and still every request is slow. The reason is that the application is waiting for its data. Storage is the pantry, and in a cluster the pantry is rarely next to the kitchen. It may be a disk attached over a network, a shared file server, or a distributed system that copies every write to three machines before saying “done”.

Storage performance tuning is the craft of making that corridor shorter, the door wider and the pantry better organised. It is also the craft of knowing when the pantry is already fast enough and the real problem lies elsewhere. In this guide you will learn the words, the measurements, the moving parts and the habits that turn “the database feels slow” into a specific, fixable finding. Every new term is explained the first time it appears, and nothing is assumed.

1What Is Storage Performance, and Why Does It Matter?

Begin with the plain meaning, then add detail one layer at a time.

When an application saves or loads data, it asks the operating system to read from or write to a storage device. Storage performance describes how fast and how smoothly that request is answered. It is not one number. It is a small family of numbers that describe different kinds of speed, and a system can be excellent at one and poor at another. A road is a good comparison: you can ask how many cars pass per minute, how many tonnes of cargo move per hour, and how long one car takes to travel from the start to the end. All three questions are legitimate, and the answers can differ.

In Kubernetes, storage performance matters more than in older setups for a simple reason: Pods move. A traditional server had its own disk, bolted to the same machine as the program. A Pod is scheduled onto whichever node has room, and its data often lives somewhere else entirely. That separation brings great flexibility, since a Pod can restart on another node and find its data waiting. The price is a longer path between the application and its bytes, and every extra step on the path can add delay or limit the flow.

Speed 1

IOPS

How many separate read or write requests the storage can handle each second. It matters most for many small operations, such as a busy database.

Speed 2

Throughput

How many bytes per second can flow. It matters most for big transfers, such as backups, analytics scans and video.

Speed 3

Latency

How long one request takes from start to finish. It matters most when a person or another service is waiting for each answer.

Everyday analogy

Think of a water supply. Throughput is how much water per second comes out of the tap. Latency is how long you wait between turning the handle and the first drop arriving. IOPS is how many separate glasses you can fill per minute. A huge pipe with a slow valve gives lots of water but a long wait. A tiny pipe with a quick valve gives a fast first drop but little water. Real workloads need the right mix.

3
core measures: IOPS, throughput, latency
6+
layers between a Pod and the physical disk
1
slowest layer sets the speed of the whole path

Why tuning is different from buying

The first instinct when storage is slow is to buy a faster disk. Sometimes that is correct. Often it is wasteful, because the real limit sits somewhere else: a per-machine cap on bandwidth, an application that asks for one block at a time, a filesystem option that forces extra work, or a startup step that recursively changes permissions on millions of files. Tuning means finding the actual limit first and then spending effort or money exactly there.

i
Tip

Before changing anything, write down the one sentence that describes the pain, for example “checkout writes take 80 milliseconds at the 99th percentile and should take 10”. A measurable goal tells you when to stop.

2The Vocabulary of Storage Speed

A handful of words explain almost every storage conversation. Learn them once and the rest becomes easy.

IOPS: input and output operations per second

One I/O operation is one request to read or write a chunk of data. IOPS counts how many of these the storage completes in a second. A small database that updates many rows needs a high IOPS number. The size of each chunk is called the block size, and it changes the meaning of IOPS completely. Four thousand requests of 4 kilobytes each is a very different job from four thousand requests of 1 megabyte each, even though the IOPS count is identical.

Throughput: bytes per second

Throughput, also called bandwidth, is how much data moves each second, usually in megabytes per second. It is linked to IOPS by simple arithmetic: throughput is IOPS multiplied by block size. If a disk delivers 10,000 IOPS at 4 kilobytes, that is only about 40 megabytes per second. The same disk reading 1-megabyte blocks might reach its bandwidth ceiling at a few hundred IOPS. This is why a vendor can quote a huge IOPS figure and a database can still starve: the two numbers apply to different block sizes.

Latency: how long one request takes

Latency is the time from issuing a request to receiving the answer. It is measured in milliseconds or microseconds. Local flash storage often answers in well under a millisecond, while network-attached disks commonly take one to several milliseconds, depending on the provider and the load. Latency is the number a waiting user feels.

Percentiles, not averages

An average hides pain. If ninety-nine requests take 1 millisecond and one takes 500, the average looks like 6 milliseconds, yet one in a hundred users had a terrible time. Engineers therefore look at percentiles. The 99th percentile, written p99, is the time that 99 percent of requests beat. When people talk about “tail latency” they mean these slow outliers, and tail latency is where storage problems usually show first.

Queue depth: how many requests are in flight

Queue depth is the number of requests the application has sent but not yet seen finished. Modern storage is built to work on many requests at once. A program that sends one request, waits, and then sends the next, has a queue depth of one and cannot use the full power of the device. Higher queue depth usually raises IOPS up to a point, and then only raises latency.

i
A handy rule

Roughly, IOPS equals queue depth divided by latency in seconds. With a latency of 1 millisecond and a queue depth of 1, you can do at most about 1,000 IOPS, no matter how fast the disk claims to be. With a queue depth of 32 and the same latency, up to about 32,000 IOPS are possible, if the device allows it.

Random and sequential access

Sequential access reads or writes data in order, like reading a book from page one. Random access jumps around, like looking up words in a dictionary. Spinning disks hate random access because the head must move. Flash storage handles it far better, but sequential access is still usually cheaper. Databases and key-value stores mix both, and knowing which dominates your workload shapes every later decision.

Reads, writes and the mix between them

Reads can often be served from memory caches. Writes must eventually reach durable storage, and many applications also demand that a write is truly safe before they continue. The command that forces this is called sync or flush, and it is expensive on distributed storage because every copy must confirm. A workload that is ninety percent reads behaves very differently from one that is ninety percent writes.

TermPlain meaningUnit you will see
IOPSNumber of separate requests handled per secondoperations per second
ThroughputBytes moved per secondmegabytes per second
LatencyTime for one requestmilliseconds or microseconds
Block sizeSize of each requestkilobytes
Queue depthRequests in flight at the same timecount
p99Time that 99 percent of requests beatmilliseconds
Sync writeA write that waits until data is safely storedoperations per second

3The Storage Path From Pod to Disk

To tune a journey, you must first see every stop on it.

When an application inside a container writes a file, the request does not leap straight to a disk. It travels through a stack of layers. Each layer does useful work, and each layer can be a source of delay or a limit on flow. The map below shows the typical stops for a volume that lives on a network-attached disk.

flowchart TD
  A["Application in the container"] --> B["Container filesystem view and volume mount"]
  B --> C["Node operating system: page cache, filesystem, block layer"]
  C --> D["Node device driver or network storage client"]
  D --> E{"Where does the data live?"}
  E -->|"Local disk"| F["Physical flash or disk in the same machine"]
  E -->|"Network block"| G["Network, then storage service, then replicated disks"]
  E -->|"Network file"| H["Network, then file server or distributed filesystem"]
    
Figure 1. The layers a storage request passes through

Walking down the stack

1

The application

The code decides block sizes, whether to wait for each write, and how many requests to send at once. Many performance problems are born here.

2

The mount inside the container

Kubernetes arranges for a directory in the container to point at the volume. The mount carries options that influence caching and behaviour.

3

The node’s page cache and filesystem

The operating system keeps recently used data in memory, merges small writes and organises files on the device. Filesystem choice and settings matter here.

4

The block layer and driver

Requests are queued, ordered and sent to the device or to a network storage client. The I/O scheduler and queue settings live here.

5

The network, if any

For remote disks, every request crosses a network that has its own bandwidth, latency and sharing rules. Many cloud machines cap their storage bandwidth separately from the disks.

6

The storage system and the media

Finally the request reaches a service that may copy it to several devices before answering. The type of media and the replication rules set the floor for latency.

A storage path is only as fast as its narrowest and slowest stop.
i
Tip

When someone says “the disk is slow”, ask which stop they mean. The disk itself, the machine’s cap on storage bandwidth, the network, the filesystem and the application each need different cures.

4Kubernetes Storage Building Blocks

A short, friendly refresher on the objects that describe storage, because tuning means changing them.

Kubernetes separates the request for storage from the supply of storage, much like a customer who orders a table and a restaurant that decides which table to give. That split is expressed through a few objects.

Request

PersistentVolumeClaim

A claim, written by the application team, saying how much space is needed, what access style is required and which class of storage is wanted.

Supply

PersistentVolume

The actual piece of storage in the cluster. It may be created by hand or created automatically when a claim appears.

Menu

StorageClass

A named recipe describing how to create storage: which driver, which disk type, which options. The class is the main performance dial that platform teams control.

Plug-in

CSI driver

The Container Storage Interface driver is the software that talks to the storage system on behalf of Kubernetes. Each vendor supplies its own driver.

Access modes

A claim also states how the volume may be shared. ReadWriteOnce means one node at a time can mount it for reading and writing, which suits network block disks. ReadWriteMany means many nodes may use it together, which needs a shared file system. ReadOnlyMany allows many readers. Newer versions also include a single-Pod variant of the first mode. The mode is not just a rule. It decides what kind of storage is even possible, and therefore what performance is available.

Dynamic provisioning

In dynamic provisioning, a developer creates a claim naming a class. The driver then creates the real disk in the background, and Kubernetes binds the claim to it. Because the class carries the parameters, changing the class changes the performance of every new volume. Existing volumes do not change by themselves, a point that surprises many teams.

The life of a volume, briefly

1

Claim

An application asks for storage of a class and size.

2

Provision

The driver creates the disk in the storage system.

3

Attach

When a Pod is placed on a node, the disk is attached to that node.

4

Mount

The node formats the disk if needed and mounts it into the Pod.

5

Use and move

The Pod reads and writes. If the Pod moves, the disk is detached and attached elsewhere.

Notice how many steps happen before the first byte is read. Chapter 17 returns to this, because slow Pod starts are a performance problem too.

5Volume Types and Their Speed Personalities

Not all volumes are equal. Each kind has a character, and choosing well is the cheapest tuning you will ever do.

KindWhere the data livesTypical strengthTypical weakness
emptyDir on node diskThe node’s own disk, tied to the Pod’s lifeSimple and quick, no networkLost when the Pod leaves; shares the node disk with everything else
emptyDir in memoryThe node’s RAMExtremely fastCounts against memory limits; lost on restart
hostPathA directory on the nodeDirect access to local speedTies the Pod to one node; weak isolation and security risks
Local persistent volumeA dedicated local disk, declared to KubernetesLowest latency, highest IOPSData is lost if the node is lost; the Pod is pinned to that node
Network block volumeA cloud or array disk attached over the networkSurvives node loss; easy to moveHigher latency; caps per disk and per machine
Network file shareA file server or managed file serviceShared by many PodsMetadata-heavy work is slow; latency varies
Distributed storage clusterReplicated across many nodesResilient, flexible, self-managedWrite amplification and network cost
Object storage through an APIA remote bucket serviceHuge scale, cheap capacityNot a filesystem; high per-request latency
Everyday analogy

Local storage is a desk drawer: instant, but if you move offices the drawer stays behind. A network block disk is a private locker in the basement: a short trip, and it follows you if you change floors. A file share is the office filing room: everyone can use it, but you queue at busy times. Object storage is the warehouse across town: huge and cheap, and you do not fetch one sheet at a time.

Matching the volume to the job

  • Scratch space for caches, sorting and temporary files: an emptyDir, perhaps on a fast local disk.
  • A primary database: a network block volume with provisioned performance, or a local volume with replication handled by the database itself.
  • Shared media or content used by many web Pods: a file share, with attention to small-file behaviour.
  • Backups, logs and archives: object storage, accessed by a client library instead of a mount.
!
Common mistake

Treating a network file share like a local disk. A program that creates, opens and closes tens of thousands of tiny files will feel fine on a laptop and painful on a share, because every one of those operations is a round trip over the network.

6Local Storage Versus Network Storage

This is the biggest fork in the road, and it is a trade between speed and freedom.

A disk inside the same machine as the Pod answers in microseconds and has no network to share. A disk reached over the network answers in milliseconds and may be limited by the bandwidth of the machine. On the other hand, a network disk is independent of any one node. When the node dies, Kubernetes can start the Pod elsewhere and re-attach the disk. A local disk disappears with its node, so the data must be protected in another way, such as the application’s own replication.

Why choose local

  • Lowest latency and highest IOPS per cost.
  • No network bandwidth shared with other traffic.
  • Predictable performance, with no remote throttling.
  • Ideal for databases that already replicate across nodes.

Why avoid local

  • Pods are pinned to one node, which complicates scheduling.
  • Node failure loses the data unless it is copied elsewhere.
  • Resizing and moving are manual or driver-dependent.
  • Capacity is fixed by the machine type.

How local volumes work in Kubernetes

A local persistent volume declares which node it belongs to. The scheduler reads that and only places the Pod there. Because the placement must be known before the claim is bound, local volumes depend on a binding mode that waits for the Pod, which Chapter 8 explains. A helper program called a local static provisioner can discover disks on nodes and publish them as volumes automatically.

Making the decision

QuestionIf the answer is yesIf the answer is no
Does the application replicate its own data across nodes?Local storage is a strong optionPrefer network storage with its own durability
Is single-digit-millisecond latency required for writes?Consider local or high-end provisioned disksStandard network disks may be enough
Must Pods move freely between nodes?Network storageLocal storage can work
Can you rebuild lost data quickly from other replicas?Local storage is saferDo not risk it

A common production pattern

A team runs a three-node search or messaging cluster, where each node already holds a copy of the data. They place each member on a node with a fast local disk. Writes are quick, and when a node fails, the application rebuilds the lost copy from the other two. The same company runs its single-instance billing database on a network block volume, because there no second copy exists to rebuild from.

7StorageClass: The Main Performance Dial

The class is where platform teams turn performance up or down for everyone.

A StorageClass carries a driver name and a set of parameters that the driver understands. The parameters differ by vendor, and the documentation of your driver is the final authority, but a few themes repeat everywhere.

SettingWhat it controlsPerformance effect
Disk typeThe kind of media and service tierThe largest single effect on latency and cost
Provisioned IOPS and throughputExplicit performance purchased for the volume, where the provider offers itRaises the ceiling independent of size on some platforms
Filesystem typeHow the disk is formatted when first usedAffects large-file, small-file and parallel behaviour
Mount optionsFlags used when mountingCan reduce metadata work or alter caching
Volume binding modeWhen the disk is created and placedPrevents cross-zone mismatches that add latency or block scheduling
Allow volume expansionWhether claims may be enlarged laterLets size-linked performance grow without rebuilds
Reclaim policyWhat happens to the disk when the claim is deletedSafety rather than speed, but important for data protection

Offering a menu rather than one choice

A single default class forces every team into the same compromise. Mature platforms publish several classes with clear names, such as a standard tier for general use, a high-performance tier for databases, and a throughput-optimised tier for analytics. Each class documents what it costs and what it promises. Application teams then pick by need, and finance can see who is paying for premium performance.

!
Easy to miss

Changing a StorageClass does not change volumes that already exist. Many parameters are read only at creation time. To move an existing workload to a better class you normally create a new volume and copy the data, or use a driver feature that modifies a volume in place, where one exists.

Naming and ownership

Treat classes as a product. Give them clear names, publish expected numbers from your own benchmarks, state which workloads they suit, and assign an owner. A well-run class catalogue prevents the common pattern where everyone copies the biggest class “just to be safe” and quietly multiplies the storage bill.

8Topology and Volume Binding: Keep Data and Pod Close

A fast disk in the wrong place is a slow disk.

Clouds are divided into zones, which are separate groups of data centres within a region. A network disk usually lives in exactly one zone. A Pod in a different zone cannot attach it at all. Even where cross-zone access is possible, the extra distance adds latency and sometimes cost. Kubernetes handles this through topology information on volumes and nodes, and through the volume binding mode of the class.

flowchart LR
  subgraph IM["Immediate binding"]
    A1["Claim created"] --> A2["Disk created in a zone chosen at once"]
    A2 --> A3["Pod scheduled later"]
    A3 --> A4{"Same zone?"}
    A4 -->|"No"| A5["Pod stuck pending"]
    A4 -->|"Yes"| A6["Works"]
  end
  subgraph WF["Wait for first consumer"]
    B1["Claim created"] --> B2["Wait"]
    B2 --> B3["Pod scheduled to a node"]
    B3 --> B4["Disk created in that node's zone"]
    B4 --> B5["Works"]
  end
    
Figure 2. Two ways of deciding where the disk goes

Why waiting is usually wiser

With immediate binding, the disk is created the moment the claim appears, before anyone knows where the Pod will run. The zone is chosen blindly. Later, the scheduler may find that the Pod must run in another zone because of CPU, memory or other rules, and the Pod then cannot start. With wait for first consumer binding, the system holds the claim until a Pod that uses it is placed. The disk is then created in the right zone. This one setting prevents a large share of “Pod stuck pending” incidents for zonal storage.

Spreading replicas across zones

For resilience you often want database members in different zones. Each member then gets its own disk in its own zone. Use topology spread rules so the replicas do not pile into one zone, and remember that a single-writer database still sends its replication traffic across zones, which has latency and cost consequences of its own.

Node-level limits that look like topology problems

Each node can attach only a limited number of disks, and the limit depends on the machine type and the driver. A Pod that needs a volume may be unschedulable on an otherwise empty node because the attach limit was reached. Kubernetes learns these limits from the driver and takes them into account when scheduling, but it is worth knowing the numbers for your machine types when you plan a dense layout.

i
Tip

Make wait-for-first-consumer the default for every class that uses zonal disks or local volumes. It costs nothing and removes a whole category of scheduling surprises.

9Choosing a Filesystem and Mount Options

A filesystem is the filing system on top of the raw disk. Its design shapes how the disk behaves under your workload.

A raw disk is just a long row of numbered blocks. A filesystem adds names, folders, permissions and the bookkeeping that keeps everything consistent. When Kubernetes first uses a new volume in filesystem mode, the node formats it with the type named in the class, commonly ext4 or XFS. Both are mature and widely used, and for most workloads the difference is smaller than the difference between a good and a bad disk tier. Still, the choice has consequences worth knowing.

FilesystemOften a good fitPoints to remember
ext4General purpose, small to medium volumes, wide tool supportThe long-standing default in many distributions; predictable and well understood
XFSLarge files, large volumes, many parallel writersScales well with concurrency; a volume formatted with it cannot be shrunk
Others, such as btrfs or ZFSSpecial needs like snapshots and checksumsMore tuning knobs and more to learn; check your driver and operating system support
i
Good to know

Some databases publish a recommendation for the filesystem they are best tested on. Following the vendor’s advice is usually wiser than chasing tiny theoretical gains.

Mount options that matter

A class can pass mount options, which are flags used when the volume is attached to the node. The ones that most often matter are small, and each deserves a moment of thought rather than copying from a blog.

  • Access time updates. By default, every read may cause a small write to record when the file was last read. Options such as noatime or relatime reduce this. Modern kernels already default to a gentle version, but explicit settings on busy shared volumes can still help.
  • Discard or trim. Telling flash storage which blocks are free helps it manage itself. Doing it on every delete can add latency. Periodic trimming on a schedule is often kinder to latency-sensitive workloads.
  • Barriers and journalling modes. These protect data after a crash. Turning them off is a classic trap: speed rises, and so does the chance of corruption when power or a node fails. Leave safety features on unless you fully understand the consequences.
  • Network filesystem options. For shared file systems, read and write sizes, number of connections and caching rules can change results dramatically. Chapter 23 covers them.

Inodes: the quiet limit

A filesystem tracks each file with a small record called an inode. The number of inodes is often fixed when the disk is formatted. A volume holding millions of tiny files can run out of inodes while still having free space, and writes then fail strangely. Watch inode usage as well as bytes used, particularly for caches, mail queues and build systems.

Safety options are not performance overhead. They are the price of getting your data back after a bad day.

10Filesystem Mode Versus Raw Block Mode

Sometimes the best way to speed up the filing system is to remove it.

Most volumes are presented in filesystem mode: the application sees a directory and works with files. Kubernetes also supports raw block mode, in which the Pod receives the disk itself as a device, with no filesystem in between. The application must then manage the layout of data on its own.

Why raw block can help

  • Skips the filesystem and its metadata work.
  • Gives engines that implement their own storage layout direct control.
  • Can reduce latency variation for very demanding databases.
  • Avoids double caching, where both the engine and the operating system keep the same data in memory.

Why most teams stay with files

  • Only applications designed for raw devices can use it.
  • Backups, inspection and repair tools become harder.
  • Operational knowledge is thinner in most teams.
  • The gain is often small compared with fixing the disk tier or the access pattern.
i
Tip

Treat raw block as a specialist tool for engines that explicitly document support for it. For nearly everything else, a well-chosen filesystem on a well-chosen disk is simpler and fast enough.

Direct I/O and caching

Even inside a filesystem, an application can ask to bypass the operating system’s page cache with a mode usually called direct I/O. Engines that keep their own cache use it to avoid caching the same data twice. This changes how memory is used by the node and affects how you read monitoring graphs, because the node will show little page cache for such a Pod while the process itself holds a large buffer pool.

11Cloud Disk Limits: Per-Volume, Per-Machine and Bursting

Performance in the cloud is a contract with several clauses. A bottleneck often hides in the one you did not read.

When you rent a network disk, the provider promises certain numbers. Those numbers come with rules, and the rules differ among providers and change over time, so always confirm them in the current documentation before you plan. The general shapes, however, repeat.

The three ceilings

Ceiling 1

The volume’s own limit

Each disk has a maximum IOPS and throughput. On some platforms this grows with size. On others you can buy performance independently of size.

Ceiling 2

The machine’s storage limit

Every virtual machine type can push only so much total storage traffic across all of its disks. Ten fast disks on a small machine are all squeezed through one narrow doorway.

Ceiling 3

The network share

On many platforms, storage traffic shares network paths with ordinary traffic, or has its own cap that depends on machine size.

The effective speed is the smallest of the three ceilings, not the largest.

Burst credits

Some disk types give a modest steady baseline and let you go faster for short periods by spending credits that build up when the disk is quiet. This is excellent for spiky workloads and treacherous for steady ones. A database that runs well for the first hour of a benchmark and then slows dramatically has probably used up its credits. Always run tests long enough to drain any burst allowance, and prefer disk types with a flat, documented baseline for production databases.

Size and performance linkage

On some platforms a bigger disk is a faster disk, because performance is tied to capacity. A team may therefore buy more space than it needs, just to gain speed. Other platforms decouple the two, so you pay for the exact IOPS and throughput you require. Understanding which model your provider uses saves money and prevents the confusing situation where doubling the disk doubles the speed.

Resizing and modifying volumes

Kubernetes supports growing a claim when the class allows expansion and the driver supports it. Many drivers can expand a volume while it is in use, and some can also change the performance settings in place, though capabilities vary. Check your driver’s feature list, and test an expansion in a non-production cluster. Shrinking is generally not supported.

Question to ask the documentationWhy it matters
What are the baseline and maximum IOPS and throughput of this disk type?Sets the first ceiling
Does performance depend on disk size?Determines whether to buy size or performance
Is there a burst allowance, and how long does it last?Explains tests that slow down after minutes
What is the storage bandwidth cap of my machine type?Sets the second ceiling
How many disks can one machine attach?Limits density per node
Can I change type or performance without downtime?Decides how painful a mistake is

12Ephemeral Storage, emptyDir and the Node Root Disk

Not all storage is persistent, and the temporary kind is where many surprises hide.

Every container needs somewhere to write that is not a persistent volume: a writable layer on top of its image, temporary directories, logs. This space is called ephemeral storage, and it lives on the node, usually on the same disk as the operating system and the container runtime. Everything on that disk competes for the same speed.

What shares the node disk

  • Unpacked container images and their layers.
  • The writable layer of every running container.
  • emptyDir volumes of every Pod, unless memory-backed.
  • Container logs written by the runtime.
  • Kubelet working data and system logs.

A single Pod that writes heavily to its temporary space can slow down image pulls, log writing and the other Pods on that node. Because this is all one pool of I/O, it is also a hidden source of noisy-neighbour problems.

Memory-backed emptyDir

An emptyDir can be told to use memory instead of disk. Speed is superb, but the space counts against the container’s memory limit, and a full memory volume can cause the Pod to be killed. Use it for small, hot scratch data, such as sockets or tiny caches, and set a size limit.

Giving the container runtime its own disk

On busy nodes, a good pattern is to put the container runtime’s data directory on a separate disk from the operating system, and sometimes to give heavy temporary space a third. Then an image pull storm does not delay the kubelet, and a runaway log does not fill the system disk. Many managed services and node images allow you to choose the size and type of the node’s root volume, and tuning that choice is worthwhile.

Requests, limits and eviction

Pods may declare how much ephemeral storage they request and the most they may use. The kubelet watches disk space and inodes on the node, and when free space falls below its thresholds, it starts evicting Pods to protect the machine. Notice that these controls count space, not speed. They keep the disk from filling up, but they do not stop one Pod from using all the I/O bandwidth.

!
Common mistake

Assuming that CPU and memory limits protect you from disk contention. Kubernetes has no built-in per-Pod limit for disk IOPS or bandwidth. Isolation must come from separate disks, dedicated nodes, or features of your storage system.

13Know Your Workload: Access Patterns

You cannot tune what you have not described. Start by asking how the application actually touches its data.

Two workloads can both be “a database” and ask for opposite things. Before touching a setting, characterise the traffic using a few questions. How large are the requests? Are they random or in order? What share is reads versus writes? Do writes wait for confirmation? How much parallelism does the application create? How much data is the hot set that gets used repeatedly, compared with the cold bulk?

WorkloadTypical patternWhat it usually needs most
Transactional databaseSmall random reads and writes, frequent syncs on a write-ahead logLow and steady latency, strong IOPS, fast sequential log writes
Analytics and reportingLarge sequential scansHigh throughput, big block sizes
Message queue or log brokerSequential appends, sequential reads of recent dataThroughput and steady write latency
Search indexMix of random reads and bursty background mergesGood read IOPS and room for merge bursts
Key-value engine with compactionSteady writes plus heavy background rewritingThroughput headroom, since background work competes with foreground requests
Web content from a shared volumeMany small file readsMetadata speed, caching, often better served by a CDN or object store
Machine-learning trainingBig streaming reads, checkpoint writes in burstsThroughput and parallel readers, local caching of datasets
CI build workersMany small temporary files, bursts of writesFast local scratch, quick cleanup

The write-ahead log

Many databases first write each change to a sequential journal called a write-ahead log and only later update the main data files. The log write must be confirmed as safe before the transaction is reported complete. That makes log latency the main limit on how many transactions per second a database can commit. The data files, by contrast, are written lazily in the background. This is why a favourite tuning move is to place the log on its own fast, low-latency volume.

Background work is part of the workload

Engines that organise data in sorted layers must periodically rewrite it, a process often called compaction or merging. It can use more I/O than the foreground requests. When a cluster looks fine and then stalls for a few minutes, the cause is frequently a background rewrite that saturated the disk. Include it when you size capacity.

Same product, different needs

A company ran two instances of the same relational database. One powered an online shop, with short transactions, and was latency-bound. The other fed a nightly report, scanning huge tables, and was throughput-bound. Putting both on the same disk class made the shop slow at night and the report slow by day. Giving each its own class, sized for its own pattern, cured both without buying anything larger.

14How to Benchmark Storage Properly

A measurement you can trust is worth more than any opinion. A misleading one is worse than none.

The most widely used tool for storage testing is fio, a flexible program that generates controlled read and write patterns and reports IOPS, throughput and latency percentiles. Other tools exist, including wrappers that run these tests inside a cluster against real volumes. Whichever you choose, the method matters more than the tool.

flowchart TD
  A["Describe the real workload: block size, mix, parallelism"] --> B["Create a test volume from the exact StorageClass"]
  B --> C["Run a Pod on a representative node with matching resources"]
  C --> D["Warm up and run long enough to drain any burst allowance"]
  D --> E["Record IOPS, throughput and latency percentiles"]
  E --> F{"Do results meet the goal?"}
  F -->|"Yes"| G["Write down the numbers as the baseline"]
  F -->|"No"| H["Change one thing and repeat"]
  H --> C
    
Figure 3. A repeatable benchmarking loop

Rules for honest results

  1. Test the same path your application uses. A benchmark on the node’s local disk says nothing about a network volume mounted in a Pod.
  2. Match the access pattern. Use the block size, read and write mix, and parallelism that you measured in Chapter 13. Defaults are rarely realistic.
  3. Defeat the cache on purpose, or measure it on purpose. A file that fits in the page cache will report absurdly fast reads. Use a test file much larger than memory, or choose direct I/O when testing the device itself.
  4. Run long enough. Burst credits, thin provisioning and background garbage collection show up after minutes, not seconds.
  5. Test on a quiet and on a busy node. Shared resources change results when neighbours are active.
  6. Change one thing at a time. Otherwise you cannot tell which change helped.
  7. Repeat the test. Run it several times and at different hours to see the spread, not just a lucky number.

A standard set of tests

TestPatternWhat it reveals
Random read, small blocks4 KB, many in flightMaximum read IOPS and latency under load
Random write, small blocks4 KB, many in flightMaximum write IOPS and write latency
Sequential read, large blocks1 MB, a few in flightPeak read throughput
Sequential write, large blocks1 MB, a few in flightPeak write throughput
Single-depth sync writeOne request at a time, waiting for eachRaw latency, the number a database log feels
Mixed read and writeFor example 70 percent readsHow the volume behaves under a realistic blend
i
Tip

Run the single-depth sync write test early. It takes seconds and tells you more about database suitability than any headline IOPS number.

15Reading the Results Without Fooling Yourself

Numbers do not speak for themselves. They need a careful reader.

Compare to the limits you know

Take the observed IOPS and throughput and compare them with the three ceilings from Chapter 11. If the result sits exactly at the volume’s published limit, you have found a ceiling and the cure is to raise it. If it sits far below every published limit, something else is in the way: queue depth, application behaviour, or a mount setting.

Watch the shape of latency

Healthy storage under rising load shows latency that stays flat, then climbs sharply once the device saturates. Plot latency against queue depth. The knee of that curve is the practical sweet spot. Running beyond it adds waiting and no extra work.

Common ways to be fooled

Trap

Testing a cold, empty disk

Some disks restored from snapshots load their blocks lazily from a remote store on first read. The first pass is slow, and later passes are fast. Know which phase you are measuring.

Trap

A test file that fits in memory

You are measuring the page cache, not the disk. Results look wonderful and mean nothing.

Trap

Too short a run

Burst credits mask the baseline. The honest figure appears only after they are spent.

Trap

One quiet hour

Shared systems behave differently at peak. A good result at 3 a.m. may not survive 3 p.m.

Writing down a baseline

When you have a trusted result, record it with the date, the class, the size, the node type, the kernel and driver versions and the exact test settings. This baseline becomes your reference. After any upgrade or change, rerun the same test. Storage regressions are common after driver or kernel updates, and without a baseline they are almost impossible to prove.

A result without its settings is just a rumour.

16Inside a CSI Driver and Its Effect on Speed

The driver sits between Kubernetes and the storage system. It does not touch your data path, but it controls everything around it.

A CSI driver is usually deployed as two cooperating parts. A controller part, running as ordinary Pods, handles cluster-wide actions such as creating disks, attaching them to machines, resizing and taking snapshots. A node part, running on every node, handles local actions such as formatting, mounting and reporting usage. Small helper containers called sidecars translate Kubernetes events into driver calls.

flowchart LR
  subgraph CP["Controller side"]
    P["External provisioner"] --> CD["Driver controller service"]
    AT["External attacher"] --> CD
    RS["External resizer"] --> CD
    SN["External snapshotter"] --> CD
  end
  CD --> ST["Storage system API"]
  subgraph ND["Each node"]
    KL["Kubelet"] --> NP["Driver node service"]
    NP --> MT["Format and mount on the node"]
  end
  ST -.-> MT
    
Figure 4. The two halves of a CSI driver and its helper sidecars

What this means for performance

  • Data does not flow through the driver. Reads and writes go from the node straight to the device or storage service. The driver’s speed does not set your IOPS.
  • The driver does set how quickly volumes appear. Slow provisioning, attaching or mounting delays Pod starts, rescheduling after failure and scaling events. Sidecars that are throttled by API rate limits make this worse in large clusters.
  • The node part sets what happens at mount time. Formatting options, permission changes and checks happen there, as the next chapter explains.
  • Capacity and attach limits are reported by the driver. The scheduler relies on them.
  • Metrics come from the driver and the kubelet. They are your window into attach time, mount time and volume usage.
i
Tip

Give the controller sidecars enough CPU and memory, and keep them on stable nodes. A starved or restarting controller means volumes stop being created or attached across the whole cluster, which looks like a storage speed problem but is really a control-plane problem.

Keeping drivers current

Storage drivers evolve quickly. Newer releases bring faster attach, better limits handling, volume modification support and bug fixes. Treat them like any critical component: read the release notes, upgrade in a staging cluster first, and rerun your baseline benchmarks afterwards.

17Slow Pod Starts: Attach, Mount and Permission Delays

Storage performance is not only about steady speed. It is also about how long a Pod waits before it can begin.

A Pod that uses a persistent volume cannot start its containers until the volume is ready. In a healthy setup this takes a few seconds to a minute. In an unhealthy one it can take many minutes, and when a database restarts during an incident, those minutes are painful. Several distinct causes are worth knowing.

1

Detach from the old node

A block volume that allows one node at a time must be released by its previous node first. If that node crashed, the system has to decide that it is really gone. Controllers wait for a safety period before forcing a detach, commonly several minutes by default, to avoid corrupting data that a half-alive node is still writing.

2

Attach to the new node

The cloud or array must connect the disk to the new machine. This usually takes seconds, but can take longer when API rate limits or large numbers of simultaneous attaches are involved.

3

Format on first use, or check on later use

A brand-new volume is formatted once. Formatting a very large volume with some options can take a while. Later mounts run a quick consistency check where needed.

4

Fix ownership and permissions

If the Pod asks for a group ownership setting, the system may walk through every file on the volume and change its group. On a volume with millions of files, this can take many minutes before the container starts.

5

Security relabelling

On systems that use SELinux, files may need their security labels recursively set at mount time. This can add a similar delay on large volumes, and newer releases provide ways to apply the label at mount time instead.

The permission change trap

The group ownership step is the most common hidden start-up delay. Kubernetes offers a policy setting for it, fsGroupChangePolicy. With the value that checks the root directory only, the walk is skipped when the top-level ownership already matches. After the first run, restarts become fast. Many large stateful workloads gain minutes of start time from this single setting.

i
Tip

When a Pod with a large volume takes a long time to become ready, look at its events. They record when the volume was attached and when the mount finished. A long gap after the mount usually points to ownership or labelling work.

Reducing the failover window

  • Use health checks and node monitoring that detect failure quickly.
  • Know your cluster’s timing settings for unreachable nodes and volume detach.
  • For databases with their own replicas, fail over at the application level rather than waiting for the old disk to move.
  • Keep volumes lean. Millions of files make every volume-wide operation slower.
  • Practice a node failure and record the time taken. The real figure often surprises teams.

18Snapshots, Clones and Restores

Backups are essential, but they have a speed footprint. Know it before the backup runs at noon.

A snapshot is a point-in-time picture of a volume. Many storage systems create snapshots almost instantly, using a technique that records only the changes after the picture was taken. A clone creates a new volume from an existing one or from a snapshot, which is a fast way to build test copies. Kubernetes exposes these through snapshot objects and through a data source field on new claims.

Everyday analogy

Imagine a notebook. A snapshot is like putting a bookmark at the current page and promising never to erase anything before it. New writing goes on fresh pages. This is quick, but later the notebook is messier, because old and new pages are interleaved and the librarian has to keep track of which is which.

How snapshots can affect speed

  • Copy-on-write cost. In some systems, the first write to a block after a snapshot requires copying the old content aside. A heavily written volume with frequent snapshots may see added write latency.
  • Many snapshots in a chain. Long chains can slow reads and make deletion expensive on certain platforms.
  • Snapshot creation is not the same as a backup. On many clouds, a snapshot is copied to cheaper storage in the background, and that background copy can use bandwidth.
  • Lazy loading after restore. A volume built from a snapshot may fetch blocks from the remote store on first access. The first read of each block is slow, and the workload may look broken until the volume is fully warmed. Some platforms offer ways to initialise the volume fully or in advance.

Application-consistent backups

A snapshot of a running database is only as good as the moment it was taken. Many databases need a short pause or a special flush for a snapshot to be consistent. Tools that coordinate this add a brief latency blip. Plan snapshots for quiet times, and test restores regularly, since a backup that cannot be restored is only a feeling of safety.

!
Do not skip

Time a full restore into a fresh volume and run your normal benchmarks on it. The speed of a just-restored volume can differ sharply from an ordinary one, and you need to know that before the day you depend on it.

19Noisy Neighbours and How to Isolate Storage Load

A shared node is a shared kitchen. One messy cook can slow everybody.

Since Kubernetes does not limit disk I/O per Pod by default, a single heavy Pod can consume most of a node’s disk bandwidth or most of the machine’s network storage bandwidth. Other Pods on the same node then see higher latency, even though their own volumes are separate disks. This is the classic noisy neighbour problem.

Option 1

Dedicated nodes

Use node labels and taints so that storage-heavy workloads run on their own machines, away from latency-sensitive services.

Option 2

Anti-affinity

Tell the scheduler that two heavy Pods must not share a node. They then never compete for the same machine-level cap.

Option 3

Separate disks

Give heavy work a volume that is not the node root disk, so logs and images stay unaffected.

Option 4

Throttling in the storage system

Some storage platforms and drivers can enforce per-volume or per-tenant performance limits. Where available, they provide true isolation.

Option 5

Kernel-level controls

The operating system can limit disk bandwidth per group of processes. Using these controls inside Kubernetes usually needs node-level configuration or a specialised agent, and results depend on kernel version and cgroup setup. Test carefully.

Option 6

Schedule heavy jobs for quiet hours

Backups, compaction and bulk loads can be timed so that they do not meet peak traffic.

Finding the culprit

The symptom is a latency rise across unrelated Pods on one node. On that node, look at disk utilisation, the queue length and the per-process I/O reports to see who is generating the load. Correlating the time of the slowdown with scheduled jobs often points straight at the cause.

The midnight log shipper

A team saw a nightly spike in database latency. Everything on the node looked normal until they found that a log-shipping agent started at midnight, reading yesterday’s logs from the shared root disk as fast as it could. Pointing the agent at a rate limit and moving heavy logs to a separate volume removed the spike with no change to the database at all.

20Operating System and Kernel Tuning on the Node

Some of the biggest levers sit just below the container, in the settings of the node’s operating system.

Most teams should change node-level settings carefully and only when measurements justify it, because these settings apply to everything on the machine. Managed Kubernetes services also restrict or reset some of them. Still, knowing what exists helps you read documentation and recommendations from database vendors.

Setting areaWhat it doesWhen it might matter
I/O schedulerOrders requests before sending them to the deviceFast flash devices usually need little or no reordering; spinning disks benefit from it
Queue depth and request limitsHow many requests the operating system keeps in flight per deviceRaising limits can help fast network disks that need parallelism
Read-aheadPrefetches the following data when it sees sequential readsHelps large scans; hurts random workloads on some devices
Dirty page limitsHow much unwritten data may sit in memory before the system forces a write-outLarge values cause big, sudden write stalls; small values smooth the writes
Swappiness and memory pressureHow eagerly the system swaps or drops cachesMemory pressure turns into extra disk traffic
Transparent huge pagesMemory page size managementSome databases recommend disabling it to avoid latency spikes
File descriptor and event limitsHow many files and events a process can have openHigh-concurrency servers hitting hidden ceilings
Device multipath and driver optionsHow the node reaches network storageRelevant for storage arrays and some network block systems

The page cache is your friend, and also your trap

The kernel keeps recently used file data in memory. A node with plenty of free memory therefore serves many reads without touching the disk at all. But the cache also counts toward a container’s memory accounting in many setups, and memory limits that are too tight force the cache to shrink, which turns into more disk reads. When a Pod’s I/O rises after you lower its memory limit, you have probably squeezed its cache.

How to apply changes safely

  1. Change one setting on one test node.
  2. Repeat the benchmark from Chapter 14 and compare it with the baseline.
  3. Roll it out through your node image or configuration management so it survives replacement.
  4. Record why the setting exists, because six months later nobody will remember.
!
Be careful

Copying a tuning list from the internet is risky. Recommendations are tied to specific kernels, devices and workloads. A setting that helped someone else’s flash array may harm your network disk. Measure first.

21Stateful Workloads: Databases, StatefulSets and Layout

Databases are the most demanding storage customers in a cluster. A few layout decisions give most of the gain.

A StatefulSet is the Kubernetes object designed for applications that need stable identity and their own storage. Each member gets its own claim, created from a template, and keeps it across restarts. This is the usual way to run databases, brokers and search nodes.

Separate volumes for separate jobs

Databases do different kinds of I/O, and giving each its own volume lets you tune each separately and prevents them from interfering.

VolumeWhat it holdsBest characteristics
DataMain table and index filesLarge, good random IOPS, solid throughput
Write-ahead logThe sequential journal of changesSmall but very low and steady write latency
Temporary spaceSort and join spill filesFast local scratch, loss acceptable
Backups and archivesCopies and shipped logsCheap, high-capacity, throughput-oriented

Placement rules

  • Spread members across nodes and zones with anti-affinity or topology spread rules.
  • Reserve nodes for heavy databases with taints, so general workloads do not crowd them.
  • Give the Pod guaranteed CPU and memory by setting equal requests and limits, so the database is not throttled or evicted during busy moments.
  • Mind the memory settings of the engine itself. A buffer pool that is too small forces extra disk reads. One that is too large starves the operating system and the container limit.

Operators

An operator is a controller that encodes the operational knowledge for a particular database: creating members, handling failover, taking backups, resizing volumes. Mature operators expose storage options directly, such as separate volume sizes for data and log, and know how to expand claims safely. Using one reduces the chance of a mistake during a stressful night.

Give the log its own lane, and the whole highway moves faster.

22Shared File Systems: NFS and Managed File Services

Sharing one volume across many Pods is convenient. It also changes the performance rules.

When many Pods on many nodes must read and write the same files, you need a file system that can be mounted in many places at once. Common examples include the Network File System protocol, managed file services from cloud providers, and distributed file systems. They offer the many-node access mode, but their strengths and weaknesses are different from a block disk.

Why metadata dominates

Every file operation involves two kinds of work: moving the data, and handling the metadata, which means names, sizes, permissions and timestamps. On a local disk, metadata is cheap. On a file share, each metadata action may be a round trip to the server. A job that lists a folder with ten thousand files, then opens each one, performs tens of thousands of network conversations before reading a single byte. This is why small-file workloads feel slow on shares even when throughput figures look high.

Common tuning levers

  • Read and write sizes. Larger transfers reduce overhead for big files.
  • Number of parallel connections. Modern clients can spread one mount across several connections, which helps throughput on fast links where the kernel and server support it.
  • Attribute and directory caching. Longer caching reduces round trips but lets clients see stale information for a while. Choose according to how much consistency the application needs.
  • Hard versus soft mounts. Hard mounts retry forever and protect data integrity. Soft mounts give up and return errors, which can corrupt applications that do not expect them.
  • Server-side tier or performance mode. Managed services often offer modes trading cost for throughput or latency. Choose by workload.
  • Server location. The file server in another zone adds latency and cost to every operation.

Where shares shine

  • Shared content and media read by many Pods.
  • Home directories and notebooks for teams.
  • Large files read in big sequential streams.
  • Legacy applications that expect a shared directory.

Where shares struggle

  • Databases with many small synchronous writes.
  • Build trees and package caches with huge numbers of tiny files.
  • Applications relying on strict file locking behaviour.
  • Workloads sensitive to tail latency.
i
Tip

If an application only needs shared data to be read, consider copying the data once to local scratch at start-up, or packaging it in object storage with a local cache. Local reads are many times quicker than repeated trips to a share.

23Distributed Storage Inside the Cluster

Running your own storage system on the cluster’s nodes gives flexibility and freedom. It also gives you its performance consequences.

Projects such as Rook with Ceph, Longhorn, OpenEBS variants and others turn the local disks of your nodes into a pooled, replicated storage service. Applications request volumes in the usual way, and the system places copies on several nodes. The benefit is that you keep durability and mobility on any infrastructure, including your own data centre.

Why writes cost more

To keep data safe, these systems write each block to several machines and wait for confirmation before saying the write is done. With three copies, a single application write becomes three disk writes and a set of network messages. This is called write amplification. Latency is set by the slowest of the required copies, plus network time.

sequenceDiagram
  autonumber
  participant App as Application Pod
  participant Pri as Primary storage node
  participant R1 as Replica node 1
  participant R2 as Replica node 2
  App->>Pri: Write one block
  Pri->>R1: Copy the block
  Pri->>R2: Copy the block
  R1-->>Pri: Stored
  R2-->>Pri: Stored
  Pri-->>App: Write complete
    
Figure 5. A replicated write waits for all required copies

What determines performance

1

The underlying devices

Flash devices give far lower latency than spinning disks. Using fast media for the journal or metadata of the system can dramatically improve write speed.

2

The network between storage nodes

Replication traffic is heavy. A fast, low-latency network, ideally dedicated or well-isolated from application traffic, is often the most valuable upgrade.

3

The replication or erasure coding choice

Keeping full copies is fast and costly in space. Erasure coding saves space but adds computing work and slower small writes. Choose per workload.

4

Data locality

Some systems can keep one copy on the same node as the Pod, so reads are local. This may improve read latency while writes still travel to the other copies.

5

Rebuild and rebalance work

When a node fails or is added, the system copies data around in the background. This heavy traffic can slow foreground requests unless limited. Tune recovery speed to balance safety and performance.

!
Think before stacking

Running a replicated database on top of replicated storage multiplies the copies. Three database replicas, each on storage that makes three copies, means nine copies of every write. If the database already replicates, ask whether the storage layer should do so too.

24Observability: What to Measure and Where

Tuning without measurement is guessing with extra steps. Put the gauges in place before the incident.

Storage health can be seen from four viewpoints, and a good dashboard has something from each. Together they let you say which stop on the path from Chapter 3 is struggling.

View 1

The application

Query times, commit times, request latency percentiles, queue lengths and error rates. The application is the final judge of whether storage is good enough.

View 2

The volume

The kubelet reports capacity, used space and inode usage for each mounted volume. Alerts on space and inodes prevent the dullest outages.

View 3

The node

Per-device read and write rates, queue length, utilisation and average wait time, usually gathered by a node exporter. These show saturation and contention.

View 4

The storage system and cloud

Provider metrics for each disk, such as consumed IOPS, throughput, queue length, burst balance and throttling events, and the health metrics of any in-cluster storage system.

The metrics that tell the most

MetricWhat it tells youWorry when
Device utilisationShare of time the device is busyStays near the maximum while latency climbs
Average queue lengthHow many requests are waitingGrows steadily; the device cannot keep up
Read and write latency percentilesHow long requests takep99 diverges from the median
Throughput versus the limitCloseness to the bandwidth ceilingFlat at exactly the published limit
Provider throttling or burst balanceWhether credits or caps are in playBalance trending to zero
Volume space and inodesDistance to fullAbove 80 percent, rising
Attach and mount durationsPod start delaysMinutes instead of seconds
Page cache hit behaviour and memory pressureWhether reads are served from memoryCache shrinking while reads climb
Pending or failed volume eventsProvisioning or attach errorsAny sustained growth

Good alerts, not loud alerts

  • Alert on user-visible symptoms first, such as commit latency, and on causes second.
  • Alert on volume space with enough lead time to expand safely.
  • Alert when a disk approaches its provisioned limits, before throttling begins.
  • Alert on unusual attach and mount durations.
i
Tip

Put application latency, node disk wait and provider throttling on one dashboard with a shared time axis. The cause of a slowdown usually becomes obvious when you can see all three rise together.

25Troubleshooting: From Symptom to Cause

A calm, ordered path beats guessing. Work from the outside in, and from the cheap checks to the expensive ones.

flowchart TD
  A["Application is slow"] --> B{"Is the slowness at start-up only?"}
  B -->|"Yes"| C["Check attach, mount, ownership change and relabelling times in Pod events"]
  B -->|"No"| D{"Is the node disk busy with a long queue?"}
  D -->|"No"| E["Look at CPU, memory pressure, locks and the application itself"]
  D -->|"Yes"| F{"Is throughput or IOPS flat at a published limit?"}
  F -->|"Yes"| G["Raise the volume limit or machine storage cap, or spread the load"]
  F -->|"No"| H{"Do other Pods on the node cause the load?"}
  H -->|"Yes"| I["Isolate with dedicated nodes, separate disks or rate limits"]
  H -->|"No"| J["Check queue depth, block size, filesystem options and the access pattern"]
    
Figure 6. A troubleshooting path for slow storage

Symptom table

SymptomLikely causeFirst checkTypical cure
Pod stuck pending with a volume complaintDisk in a different zone, or attach limit reachedPod and claim events, node attach countsUse wait-for-first-consumer binding; spread Pods
Pod takes many minutes to startRecursive ownership or label change, slow detachTime between attach and container startSet ownership change policy to check the root only; trim file counts
Fast at first, slow after several minutesBurst credits exhaustedProvider burst balance graphChoose a type with a flat baseline or provision more
Latency spikes every few minutesBackground compaction, snapshot, or write-out stallsTiming against engine and backup schedulesSeparate volumes, throttle background work, tune dirty limits
Throughput stuck at an exact round numberHitting a volume or machine capCompare with documented limitsRaise limits, larger machine type, add volumes
Writes fail though space is freeOut of inodesInode usage on the volumeReformat with more inodes, reduce tiny files
Everything on a node slows togetherNoisy neighbour or full root diskPer-process I/O and root disk usageIsolate, move logs, add disks
Shared volume slow for listing and buildsMetadata round tripsOperation counts versus data movedLocal scratch, caching, change tool or storage type
Large transfers hang but small ones workNetwork packet size mismatch on the storage pathNetwork settings between node and storageAlign packet size settings
Speed dropped after an upgradeDriver, kernel or option changeRerun the baseline testRoll back or adjust; report the regression
i
Tip

Keep a small debugging Pod image with disk and network test tools ready. Being able to run a quick test on the same node and the same kind of volume in two minutes turns a long argument into a short answer.

26Patterns and Anti-Patterns

Learn from the mistakes other teams have already paid for.

Patterns that work

  • Publish a small menu of well-benchmarked StorageClasses with clear names.
  • Use wait-for-first-consumer binding for zonal and local storage.
  • Give database logs their own low-latency volume.
  • Benchmark with the real access pattern, long enough to drain bursts.
  • Record baselines and repeat them after every upgrade.
  • Reserve nodes for heavy storage users.
  • Monitor application latency and provider throttling together.
  • Rehearse node failure and restore, and time them.

Anti-patterns

  • Buying the fastest disk everywhere without finding the real bottleneck.
  • Benchmarking a file that fits in memory.
  • Putting a database on a general-purpose shared file system.
  • Letting every Pod write heavily to the node root disk.
  • Stacking replication on replication without a reason.
  • Disabling safety features to win a benchmark.
  • Copying a kernel tuning list without measuring.
  • Ignoring inode and space alerts until the volume is full.
ADR-1Accepted
Decision

Every production database gets a separate, low-latency volume for its write-ahead log, with its own StorageClass and its own alerts.

Reason

Commit latency is bounded by log write latency. Isolating the log from data file and background traffic keeps that latency steady and makes problems easier to diagnose.

ADR-2Accepted
Decision

All StorageClasses that use zonal or local storage must use wait-for-first-consumer binding.

Reason

Creating a disk before the Pod is placed leads to zone mismatches and pending Pods. Waiting costs nothing and removes the failure class.

A pattern in practice

A platform team publishes three classes called standard, fast-db and throughput. Each page of their internal documentation lists the benchmark results, the cost per month for a typical size, and the workloads that suit it. New teams choose from the menu in minutes, and the platform team reviews any request that needs a custom class. In a year, the number of storage-related incidents drops, and the storage bill becomes explainable.

27Balancing Cost and Performance

Speed costs money, and the most expensive disk is the one nobody needs.

Every performance tier has a price, and prices differ widely. The skill is to buy performance where it is felt and nowhere else. A few principles keep spending sensible.

1

Measure before upgrading

If the volume is nowhere near its limits, a faster tier changes nothing. The cheapest improvement is often in the application or the layout.

2

Split by need

Give the small, latency-critical log a premium disk and the large, cold data a cheaper one. Paying premium prices for terabytes of cold data is a classic waste.

3

Buy performance, not just size

Where your provider lets you provision performance independently, avoid buying empty space solely to gain speed.

4

Cache the hot part

A modest amount of memory or fast local scratch in front of slower storage often beats an expensive disk. Reading data twice from slow storage is the real waste.

5

Count the hidden costs

Cross-zone traffic, snapshot storage, API calls to object stores, idle volumes left behind by deleted workloads, and over-provisioned replicas all add up.

6

Review regularly

Workloads change. A quarterly look at utilisation versus provisioned limits finds disks that are far too big or far too small.

Orphaned volumes

When a claim is deleted but the reclaim policy keeps the disk, the volume remains and the bill continues. Likewise, volumes from retired test environments linger. A monthly report of volumes that are not attached to any Pod pays for itself surprisingly quickly.

The fastest storage is not the one with the biggest number. It is the one that matches the workload and nothing more.

28A Worked Example With Numbers

Walk through one problem from complaint to cure, using plain arithmetic.

The situation

A company runs a relational database for an order system in a Kubernetes cluster. The database uses one network block volume for everything, with published limits of 3,000 IOPS and 125 megabytes per second. At peak, orders feel sluggish, and every evening a reporting job makes things worse. The machine type running the database has a storage bandwidth cap well above the disk limit, so the volume itself is the first ceiling.

Measure the demand

ItemObservationCalculation
Peak order writes2,500 small writes per second, 8 KB each2,500 × 8 KB = 20,000 KB, about 20 MB per second
Peak read IOPS from queries1,200 per second, 8 KB each1,200 × 8 KB, about 10 MB per second
Combined IOPS2,500 + 1,2003,700 per second, above the 3,000 limit
Evening report scanLarge sequential reads at full speedTries to read 120 MB per second
Combined throughput in the evening20 + 10 + 120150 MB per second, above the 125 limit

What the numbers say

  • At peak, the IOPS demand of 3,700 is above the 3,000 limit by about 23 percent. Requests queue up and latency grows. The slowness is real, and it matches a ceiling, not an application bug.
  • In the evening, throughput demand exceeds the bandwidth limit by 25 megabytes per second, so the report starves the orders, and the orders slow down the report.
  • The log traffic and the data traffic share the same queue, so a burst of data reads delays log writes, which directly delays commits.

Applying queue depth arithmetic

The team measures a single-depth sync write latency of 2 milliseconds on the volume. Using the rule from Chapter 2, one request at a time gives at most about 500 operations per second (1 divided by 0.002). The database uses group commit, batching several transactions per log write, so it handles the 2,500 order writes with fewer log writes. Even so, the log’s latency sets the commit time, and 2 milliseconds is acceptable only if it stays steady. Sharing a queue with the report breaks that steadiness.

The plan

1

Separate the log

Move the write-ahead log to its own small volume provisioned for low latency. Its demand is small in size, so cost stays low.

2

Raise the data volume limit

Provision about 6,000 IOPS and 250 megabytes per second on the data volume, which gives roughly 60 percent headroom over the 3,700 IOPS peak and covers the evening throughput of 150.

3

Move the report off the primary

Run the report against a read replica, so its scan never meets order traffic. The replica has its own volume, sized for throughput.

4

Check the machine cap

Confirm that the combined bandwidth of the log and data volumes (about 250 plus a small log figure) is below the machine type’s storage cap. If not, move to a larger machine type.

5

Rerun the benchmark and alerts

Repeat the mixed test, confirm p99 commit latency, and set alerts at 70 percent of the new limits.

i
Lesson

Nothing here required a heroic kernel tweak. Simple arithmetic found a ceiling, a clean layout removed interference, and a monitored limit protected the result.

29Real-World Scenarios

Typical situations where these ideas apply, described in general terms.

Retail

Black Friday database stalls

A shop’s order database slows during a sale because burst credits on its disk run out after the first hour of traffic. Switching to a type with a flat baseline and adding alerts for the credit balance solves it.

Media

Transcoding farm on a shared file system

Workers read large source files from a share and write results back. Throughput is fine, but thousands of tiny status files overwhelm metadata. Moving status to a small database and using local scratch for temporary files fixes it.

Fintech

Slow failover after a node crash

A payments database takes eight minutes to come back after a node loss. Time is lost waiting for the old disk to detach and then changing ownership on millions of files. Application-level failover to a standby and a root-only ownership check cut it to under a minute.

Data

Analytics and operational data on one disk

A team runs nightly scans on the same volume as an operational service. Moving scans to a replica with a throughput-oriented class ends the daily complaints.

AI

Training jobs waiting for data

GPUs sit idle because dataset reads are slow. Caching the dataset on local fast disks at the start of the job, and reading it in parallel, keeps the expensive accelerators busy.

SaaS

Search cluster rebuilds slow the site

When a search node is replaced, rebuilding data saturates the network and disks. Limiting recovery speed and adding a dedicated storage network keeps queries responsive.

i
Note

These are typical patterns, not reports about named companies. Check current provider limits and case studies before quoting figures in your own material.

30A Practical Tuning Checklist

A short, ordered list you can print and follow.

1

State the goal

Write one measurable sentence, such as a target p99 latency or a target restart time.

2

Describe the workload

Block size, read and write mix, parallelism, sync behaviour, hot data size and background work.

3

Know the ceilings

Volume limits, machine storage caps, network paths and burst rules from current provider documentation.

4

Choose the right kind of storage

Local, network block, shared file, distributed or object, based on durability, mobility and latency needs.

5

Set the class properly

Right disk type, wait-for-first-consumer binding, expansion allowed, sensible filesystem and mount options.

6

Lay out the volumes

Separate log, data and scratch. Dedicate nodes to heavy workloads. Spread replicas across zones.

7

Benchmark honestly

Same path, same pattern, long enough, repeated. Save the baseline with all settings.

8

Make start-up fast

Use the root-only ownership check, keep file counts sane, and rehearse failover.

9

Instrument and alert

Application latency, node disk wait, volume space and inodes, provider throttling and attach times.

10

Review on a schedule

Re-run baselines after upgrades, check utilisation against provisioned limits, and clean up orphaned volumes.

31Glossary of Terms

TermMeaning in plain words
IOPSInput and output operations per second; how many separate requests storage handles each second
ThroughputHow many bytes per second can flow to or from storage
LatencyThe time one storage request takes from start to finish
Tail latencyThe slow outliers, usually shown as p99 or p999
Block sizeThe size of each individual read or write request
Queue depthHow many requests are in flight at the same moment
Sequential and random accessReading in order versus jumping around
Sync writeA write that waits until data is safely stored before continuing
Page cacheMemory the operating system uses to keep recent file data
Write-ahead logA sequential journal that a database writes first, to survive crashes
PersistentVolumeClaimA request for storage made by an application
PersistentVolumeAn actual piece of storage in the cluster
StorageClassA named recipe describing how storage is created
CSIContainer Storage Interface; the standard plug-in model for storage drivers
Volume binding modeThe rule for when a disk is created and tied to a claim
Topology and zoneThe physical grouping of machines; a zonal disk is usable only inside its zone
Access modeHow many nodes may use a volume at once and how
Ephemeral storageTemporary space tied to a Pod’s life, on the node’s disk
emptyDirA scratch directory that exists for the Pod’s lifetime
Local persistent volumeA disk attached to one node and declared as a Kubernetes volume
Burst creditsExtra speed allowed for a short time, earned while the disk is quiet
InodeThe record a filesystem keeps for each file
fsGroupA Pod setting for group ownership of files on a volume
SnapshotA point-in-time picture of a volume
Write amplificationThe extra writes caused by copying data to several places
Noisy neighbourA workload whose heavy use slows down others sharing the same resource
StatefulSetA Kubernetes object for applications with stable identity and their own storage
OperatorA controller that automates running a particular application
MetadataInformation about files such as names, sizes and timestamps

32Frequently Asked Questions

Q1Which number matters most: IOPS, throughput or latency?

It depends on the workload. Transactional systems feel latency and IOPS. Scans, backups and streaming feel throughput. Describe your workload first, then pick the number that it is bound by.

Q2Is local storage always faster than network storage?

Usually it has lower latency, because there is no network. But a poorly chosen local disk can be slower than a strong provisioned network disk, and local storage has durability and mobility costs. Benchmark both against your workload.

Q3Can I change the StorageClass of an existing claim?

Not directly. The class of a bound claim is fixed. You normally create a new volume from the new class and migrate the data, or use a driver feature that modifies the existing volume where supported.

Q4Why does my benchmark show great numbers and my application is still slow?

The test probably did not match the application’s pattern. Block size, queue depth, sync behaviour and the read and write mix all change results. Or the real limit is elsewhere, in locks, CPU or the network.

Q5Does Kubernetes limit disk IOPS per Pod?

Not by default. Resource requests and limits cover CPU, memory and ephemeral storage space, not I/O speed. Isolation comes from separate disks, dedicated nodes or features in the storage system.

Q6What is the quickest way to cut the start time of a Pod with a huge volume?

Check whether a recursive ownership change is happening, and set the ownership change policy so it runs only when the root directory does not already match. Then look at attach and detach delays.

Q7Should I disable the filesystem journal or safety barriers to go faster?

No, not for data you care about. The gain is usually small compared with the risk of corruption after a crash or power loss.

Q8Why did my volume get slower after I restored it from a snapshot?

Some platforms load blocks lazily from a remote store on first access, so early reads are slow until the volume warms. Initialise the volume or read through it before relying on it.

Q9Is a bigger disk always a faster disk?

On some platforms performance is tied to size, and on others it is independent. Check your provider’s rules. Do not buy extra space just to buy speed if you can purchase performance directly.

Q10Should databases run on a shared file system?

Usually not. Databases need low, steady latency for small synchronous writes and reliable locking behaviour. Block volumes or local disks are normally a better fit, unless your vendor documents support for a specific file service.

Q11How do I know if I am being throttled by the cloud?

Compare observed IOPS and throughput with the documented limits and look at the provider’s metrics for throttling or burst balance. Flat lines at an exact round number are the classic sign.

Q12Is object storage a replacement for a persistent volume?

Not for applications that need a filesystem or low-latency random writes. It is excellent for backups, archives, logs and large datasets that are accessed through an API or loaded into local scratch.

Q13How often should I re-run storage benchmarks?

After any change to the driver, kernel, node image, machine type or storage class, and on a regular calendar, such as quarterly, to catch drift.

Q14Do managed Kubernetes services handle all this for me?

They manage the control plane and often supply default storage classes, but the workload design, volume layout, class choice and monitoring remain your responsibility.

33Summary and Key Takeaways

What to remember

  • Three numbers, one story. IOPS, throughput and latency describe different kinds of speed. Match the number that matters to your workload, and use percentiles instead of averages.
  • The path has many stops. The application, mount, filesystem, block layer, network and storage system each add delay or limits. The narrowest stop sets the speed.
  • Know the three ceilings. The volume, the machine’s storage cap and the network all limit the result. The smallest wins.
  • Choose the kind of storage first. Local, network block, shared file, distributed and object storage have very different personalities and trade-offs.
  • The class is the main dial. Use a small menu of benchmarked classes, with wait-for-first-consumer binding, and remember that existing volumes do not change when the class does.
  • Layout beats brute force. Separate the log, data and scratch, isolate heavy workloads and spread replicas.
  • Benchmark honestly. Use the real pattern, drain the burst allowance, repeat it, and keep a written baseline.
  • Start-up is performance too. Attach, detach, ownership and relabelling steps can cost minutes. Prepare for them and rehearse failover.
  • Watch from four viewpoints. Application, volume, node and provider metrics together reveal where the slow stop is.
  • Spend where it is felt. Measure first, split by need, and clean up what nobody uses.