Amazon Elastic Block Store (EBS) is the persistent disk behind most EC2 instances: a volume you attach to a server, format with a filesystem, and treat exactly like a physical drive — except it is not physical, it is not local, and it outlives the instance it is attached to. That last property is the whole point. When an EC2 instance stops or fails, the data on its instance store disappears; the data on its EBS volume does not. EBS is how a stateful server on ephemeral compute keeps its state. But the design choice that makes EBS durable — putting the disk on the network rather than on the host — also shapes its performance, its cost, and its failure modes in ways that surprise people who think of it as ‘just a hard drive.’ This piece walks the whole model: block versus instance store, the network-attached mental model, the volume-type family and why gp3 is the modern default, the IOPS/throughput/latency knobs, snapshots and encryption, live resizing, the performance traps, the single-AZ durability boundary, and — with an interactive lab — how gp3 provisioning turns into a monthly bill.
Block storage, and why EBS is not instance store
Storage on AWS comes in three broad shapes, and confusing them is the root of most EBS mistakes. Object storage (S3) hands you a whole object by key over HTTP — great for files, useless as a boot disk. File storage (EFS, FSx) gives you a shared filesystem many machines mount at once. Block storage — EBS — gives you raw, fixed-size blocks that the operating system sees as a disk. You put a filesystem (ext4, xfs, NTFS) on top, and applications read and write it byte-addressably, just like a local SSD. Databases, boot volumes, and anything that does random small reads and writes want block storage.
Every EC2 instance can also have instance store — physical NVMe or SSD disks directly attached to the host machine the instance runs on. Instance store is blisteringly fast because it is local, but it is ephemeral: stop or terminate the instance, or have the underlying hardware fail, and the data is gone forever. It is scratch space — a cache, a spill directory, a scratch partition for a shuffle-heavy job. EBS is the opposite: it is persistent and decoupled from any one host. You can detach an EBS volume from a dying instance and re-attach it to a healthy one with the data intact. That durability is the reason EBS exists, and, as the next section explains, it is bought with a network hop.
The key mental model: EBS is network-attached, not local
The single most important thing to internalize about EBS is that the disk is not in the box. When your instance writes to an EBS volume, that write does not land on a drive bolted to the same server — it travels over a purpose-built, high-throughput storage network to a separate fleet of storage servers, where it is written and replicated before the write is acknowledged. The volume looks like /dev/nvme1n1 to the kernel, but behind that device node is a network round trip.
This is the mental model everything else hangs off of. It explains why EBS is durable: because the data lives on a replicated storage fleet, an instance can die without taking the disk with it. It explains the latency floor: a local NVMe drive answers in tens of microseconds, while an EBS read has to cross the network, so single-digit-milliseconds is the realistic neighborhood — excellent for a networked disk, but orders of magnitude slower than truly local flash. And it explains the bandwidth ceiling: EBS traffic shares the instance’s network path, so a volume’s throughput can be capped not by the volume itself but by how much EBS bandwidth the instance type is allowed. When someone says ‘my io2 volume is provisioned for 500 MB/s but I only see 300,’ the answer is almost always that the instance, not the volume, is the bottleneck. Hold this picture — disk on the network, behind an instance-sized pipe — and the rest of EBS stops being surprising.
The volume-type family
EBS is not one product but a family of volume types, split into two camps: SSD-backed volumes optimized for IOPS (random, transactional work) and HDD-backed volumes optimized for throughput (large, sequential scans). Picking the right member is the first cost-and-performance decision you make.
| Type | Media | Optimized for | Typical use |
|---|---|---|---|
| gp3 | SSD | General purpose, IOPS | Default for almost everything — boot, apps, most DBs |
| gp2 | SSD | General purpose (legacy) | Older default; size-coupled performance |
| io1 | SSD | High, sustained IOPS | Older provisioned-IOPS workloads |
| io2 Block Express | SSD | Highest IOPS, durability | Mission-critical databases, SAN replacement |
| st1 | HDD | Throughput, sequential | Big-data, log processing, warm data |
| sc1 | HDD | Cold, infrequent | Cheapest; rarely accessed archives on a block device |
The two HDD types, st1 and sc1, are the odd ones out: they are cheap per gigabyte and can stream large sequential reads well, but they are terrible at random small I/O and should never hold a boot volume or a transactional database. For the SSD side, the modern advice collapses almost entirely to a single word: gp3. The older gp2 and io1 types still exist for compatibility and for a few edge cases, but new volumes rarely have a reason to choose them, for reasons the next two sections make concrete.
gp3, the modern default: performance decoupled from size
To appreciate why gp3 is the default, you have to understand what was wrong with gp2. On gp2, performance was welded to capacity: you got 3 IOPS per gigabyte, so the only way to get more IOPS was to make the volume bigger. Need 9,000 IOPS on gp2? You had to provision a 3,000 GB volume — three terabytes — whether or not you needed the space, and pay for all of it. Performance and size were a single dial, and that dial forced you to buy storage you did not want in order to get the speed you did.
gp3 breaks that coupling. A gp3 volume starts with a free baseline of 3,000 IOPS and 125 MB/s regardless of size — even a tiny 10 GB volume gets the full baseline — and you provision IOPS (up to 16,000) and throughput (up to 1,000 MB/s) independently of the number of gigabytes. Three separate dials: size, IOPS, throughput. The practical wins are large. First, cost: gp3 storage is roughly 20% cheaper per gigabyte than gp2, and a huge number of workloads live entirely inside the free baseline, so their performance line item is zero. Second, precision: you dial in exactly the IOPS and throughput you need without over-buying capacity, or over-buy capacity without paying for performance you will not use. The blanket recommendation to migrate gp2 volumes to gp3 is correct precisely because you almost always come out cheaper, faster, or both. The lab later lets you feel this decoupling directly.
io2 Block Express, durability, and Multi-Attach
Above the general-purpose tier sits io2, and its high-end form io2 Block Express, for workloads where gp3’s ceiling or durability is not enough. Three things distinguish it. First, raw performance: Block Express pushes into the hundreds of thousands of IOPS and gigabytes-per-second of throughput on a single volume, with sub-millisecond latency — the tier you reach for when replacing an on-prem SAN or running a demanding relational database that has outgrown gp3’s 16,000-IOPS cap.
Second, durability. A standard gp3 or gp2 volume is quoted at 99.8%–99.9% annual durability; io2 is engineered for 99.999% — two extra nines — which for a fleet of mission-critical database volumes is a meaningful reduction in expected annual data-loss events. Third, Multi-Attach: an io2 volume can be attached to multiple EC2 instances in the same Availability Zone at once. This is not a shortcut to a shared filesystem — the volume presents as a raw block device to every attached instance simultaneously, and without a cluster-aware filesystem or application that coordinates writes (think clustered databases using their own fencing and locking), concurrent writers will corrupt the data. Multi-Attach is a primitive for building high-availability clustered software, not a way to make one disk look like network file storage. If you want a shared filesystem, you want EFS, not Multi-Attach.
Three knobs: IOPS, throughput, and latency
People say a volume is ‘fast’ as if speed were one number. It is three, and they measure different things. IOPS (I/O operations per second) is how many distinct read or write operations the volume can service per second — the metric that matters for random, small I/O like a database doing thousands of little index lookups. Throughput (MB/s) is how many bytes per second move — the metric that matters for large, sequential I/O like streaming a log file or a big table scan. Latency is how long a single operation takes to come back — the metric that matters when a user is waiting on one query.
These are related but not interchangeable, and the relationship is roughly throughput ≈ IOPS × I/O size. A volume doing 4 KB operations needs a lot of IOPS to reach high throughput; a volume doing 1 MB operations reaches high throughput with very few IOPS but can saturate the throughput ceiling first. That is exactly why gp3 lets you provision IOPS and throughput separately: a transactional database is usually IOPS-bound and wants the IOPS dial turned up, while an analytics scan is throughput-bound and wants the MB/s dial turned up. Latency, meanwhile, is largely set by the network-attached nature of EBS — you can raise IOPS and throughput ceilings, but you cannot dial a networked disk down to local-NVMe latency. Diagnosing an EBS performance problem starts with asking which of these three is actually the constraint; tuning the wrong knob wastes money and fixes nothing.
Snapshots: incremental, S3-backed, lazily restored
An EBS snapshot is a point-in-time backup of a volume, stored in Amazon S3 (in AWS-managed buckets you do not see). Two properties make snapshots both cheap and occasionally confusing. First, they are incremental. Your first snapshot copies every used block; every subsequent snapshot of the same volume copies only the blocks that changed since the previous one. A daily snapshot of a 500 GB volume where 2 GB changes per day does not store 500 GB every day — it stores roughly 2 GB of deltas, all sharing the unchanged blocks. This makes frequent snapshots inexpensive, and it means deleting one snapshot in a chain does not break the others; AWS retains whatever blocks the remaining snapshots still need.
Second, restores are lazy. When you create a volume from a snapshot, the volume is available almost immediately — but the blocks are fetched from S3 on first access, in the background. That means the very first time your application reads a block that has not yet been pulled down, it pays a one-time latency penalty while that block is hydrated from S3. On a freshly restored database volume this shows up as sluggish initial performance that mysteriously improves as the working set warms up. The fixes are to pre-warm the volume by reading all blocks up front, or to use Fast Snapshot Restore, which pre-initializes the volume so it delivers full performance from the first read — a real cost, worth it when a restore has to be instantly fast, such as in a disaster-recovery cutover.
Encryption: KMS-backed, at-rest, and transparent
EBS encryption is one of the cleaner security stories on AWS because it is almost entirely transparent. When you enable encryption on a volume, EBS encrypts data at rest on the storage fleet, encrypts data in transit between the instance and the volume, and encrypts every snapshot taken from it — all using keys managed by AWS KMS. There is no performance penalty you will notice and no application change: the encryption and decryption happen below the block device, so the operating system and your software see plain, readable blocks and never touch the keys.
The behavior that trips people up is inheritance. Encryption propagates along the whole lineage: a snapshot of an encrypted volume is encrypted; a volume restored from an encrypted snapshot is encrypted; a volume copied from an encrypted snapshot stays encrypted. You cannot directly toggle encryption on an existing unencrypted volume — the migration path is to snapshot it, copy the snapshot with encryption enabled (choosing a KMS key), and create a new volume from that encrypted copy. Conversely, once a volume is encrypted you cannot make it plaintext again. Because you can set encryption by default at the account-and-region level, the right move for most organizations is to turn that on so every new volume and snapshot is encrypted automatically, and never have to remember to check the box. KMS then gives you the audit trail and access control over who can decrypt, which is where the real security value sits.
Elastic Volumes: resize and retype without downtime
One of EBS’s most operationally valuable features is that you can change a volume while it is live and attached to a running instance. With Elastic Volumes you can increase a volume’s size, change its type (gp2 → gp3, gp3 → io2), and adjust provisioned IOPS or throughput — all without detaching the volume, stopping the instance, or taking downtime. The modification happens in the background while the volume keeps serving I/O.
The one thing to remember is that the story has two layers. Growing the EBS volume enlarges the block device, but the filesystem sitting on top does not automatically know it has more room. After the volume finishes resizing you still have to grow the filesystem inside the guest — for example:
# after the EBS volume has been enlarged
sudo growpart /dev/nvme0n1 1 # extend the partition
sudo resize2fs /dev/nvme0n1p1 # ext4: grow the filesystem
# or, for xfs:
sudo xfs_growfs /Two more practical notes. First, you can only grow an EBS volume, never shrink it — to get smaller you create a new, smaller volume and copy data over. Second, after a modification there is a cooldown (about six hours) before you can modify the same volume again. In day-to-day terms, though, Elastic Volumes turns ‘we provisioned the disk too small’ from a maintenance-window outage into a live, no-downtime adjustment — a large part of why over-provisioning ‘just in case’ is the wrong instinct on EBS.
Performance gotchas that bite in production
EBS behaves predictably once you know its traps, and every one of them traces back to earlier sections. The recurring offenders:
| Gotcha | What actually happens |
|---|---|
| Cold snapshot restore | Blocks hydrate from S3 on first touch; initial reads are slow until warmed or Fast Snapshot Restore is used |
| gp2 burst buckets | Small gp2 volumes run on burst credits; sustained load drains the bucket and performance collapses to a low baseline |
| Not EBS-optimized | On older/smaller instances EBS traffic competes with normal network traffic unless EBS-optimized (dedicated bandwidth) is on |
| Instance bandwidth cap | The volume can do more than the instance type is allowed; the instance’s EBS bandwidth ceiling is the real limit |
| Wrong knob tuned | Raising IOPS on a throughput-bound job (or vice versa) spends money and changes nothing |
The gp2 burst trap deserves a word because it catches people migrating from it: a small gp2 volume has a low baseline and relies on a credit bucket to sustain bursts, so a load test looks great for a few minutes and then falls off a cliff when the credits run out. gp3 has no burst-credit cliff — its provisioned performance is flat and sustained — which is yet another reason to migrate. And the instance bandwidth cap is the one worth tattooing on the wall: because EBS is network-attached, a volume’s real-world throughput is min(volume limit, instance EBS limit). Provisioning a 1,000 MB/s gp3 volume on an instance whose EBS pipe tops out at 400 MB/s wastes the extra throughput you are paying for. Match the instance to the volume, not just the volume to the workload.
Durability and the Availability Zone boundary
EBS is durable, but its durability has a hard geographic boundary that is the most important thing to get right for resilience. An EBS volume lives in one Availability Zone. Within that AZ the data is automatically replicated across multiple storage servers, so a single drive or server failure does not lose your data — that is the internal redundancy behind the 99.8% (gp3) to 99.999% (io2) durability numbers. But that replication does not cross AZ lines. An EBS volume is not multi-AZ, and it cannot be attached to an instance in a different AZ.
The consequence: if an entire Availability Zone has an outage, the volumes in it — and any instances depending on them — are unavailable for the duration, no matter how many nines of intra-AZ durability the volume has. Durability (will I lose the data?) and availability (can I reach it right now?) are different questions, and a single-AZ volume answers the first far better than the second. The way you cross the AZ boundary is snapshots: snapshots live in S3, which is regional and replicated across AZs, so the pattern for surviving an AZ failure is to snapshot regularly and be ready to restore a new volume from that snapshot in a healthy AZ. For cross-region disaster recovery you copy snapshots to another region. Any resilience design on EBS ultimately rests on snapshots, because the volume itself will never leave its zone on its own.
See it: the gp3 provisioning lab
The gp3 pricing model rewards understanding one boundary: the free baseline. Every gp3 volume includes 3,000 IOPS and 125 MB/s at no charge beyond the per-gigabyte storage cost. Provision above either line and you pay per unit — and you pay whether or not the workload ever touches that capacity. The simulator below lets you drag volume size, provisioned IOPS, and provisioned throughput and watch, in real time, which portion is included and which is billed extra, along with an approximate monthly bill.
The lesson the sliders make obvious: for a large fraction of real workloads the baseline is already enough, and the entire performance line item is zero — you pay only for the gigabytes. The moment you drag past 3,000 IOPS or 125 MB/s the meter starts running, and it keeps running at idle, because EBS bills provisioned capacity, not consumed capacity. That single fact — that you are renting a guaranteed ceiling, not paying for what you use — is the difference between a right-sized volume and a bill full of headroom nobody asked for.
The cost model: you pay for provisioned, not used
EBS billing has a shape that, once you see it, changes how you provision. There are three components, and the crucial one is the second. Storage is billed per gigabyte-month of provisioned size — roughly $0.08/GB-month for gp3 — and here ‘provisioned’ means the size you asked for, not the bytes you actually wrote. A 500 GB volume that is 5% full still bills for 500 GB. Performance is billed only above the gp3 free baseline: the first 3,000 IOPS and 125 MB/s are included, and you pay per provisioned IOPS (about $0.005 each per month) and per provisioned MB/s (about $0.04 each per month) only for what you dial in beyond those lines. Snapshot storage is billed separately, per gigabyte-month of changed blocks kept in S3.
The theme running through all three is you pay for provisioned capacity, not consumed capacity. Idle IOPS still bill. Empty gigabytes still bill. Provisioned throughput you never saturate still bills. This is the opposite of S3, where you pay for bytes stored, and it is why the most common EBS waste is not expensive volume types — it is over-provisioned size and performance bought as ‘headroom’ and then left idle. Because Elastic Volumes lets you grow and retune live, the disciplined pattern is to provision lean and scale up when the metrics actually demand it, rather than pre-buying a ceiling. The lab above turns exactly these numbers into a live bill so the cost of headroom is visible before you commit to it.
A decision framework
Most EBS choices reduce to a short series of honest questions, and the answers point cleanly at a volume type and a provisioning posture:
| Ask… | Then… |
|---|---|
| Does the data need to survive the instance? | Yes → EBS. No, pure scratch → instance store (faster, ephemeral) |
| Random small I/O or large sequential scans? | Random → SSD (gp3/io2). Sequential & cost-sensitive → st1/sc1 |
| Is the workload inside 3,000 IOPS / 125 MB/s? | Yes → gp3 at baseline; performance is free |
| Need more than 16,000 IOPS or five-nines durability? | Yes → io2 Block Express. No → gp3 |
| Need one disk shared by a clustered app in an AZ? | Yes → io2 Multi-Attach (cluster-aware only). Shared files → EFS |
| How do I survive an AZ or region failure? | Snapshots to S3; copy cross-region for DR |
Read top to bottom, the framework almost always lands on the same unglamorous answer: an encrypted gp3 volume, provisioned lean, backed by regular snapshots, reserving io2 for the genuinely demanding or mission-critical cases and the HDD types for cheap sequential bulk. EBS rewards treating the disk not as a fixed physical object but as a set of independent dials — size, IOPS, throughput, type — that you can turn live as the workload teaches you what it actually needs. Provision for what you know today, watch the metrics, and grow into the ceiling instead of paying for it up front.