The question this answers
What kind of storage does a database need, and why can it not be a bucket or a network share?
PostgreSQL must modify 8 KB pages in place, append to a write-ahead log, and be certain that a committed transaction survives a power loss. It needs a device it can fsync, not an API it can PUT to.
A durable, low-latency virtual disk with random read and write at block granularity, attached to one instance, surviving instance restart and replacement.
Blocks, filesystem, pages: three layers that must agree
A block volume is deliberately ignorant. It exposes fixed-size blocks — 512 bytes or 4 KB — and offers read, write and flush. It has no idea what a file is. The guest kernel's filesystem driver builds files, directories and inodes on top; the Operating Systems domain teaches that mapping under File Systems: From Path to Blocks and Inodes.
A database then builds a third layer on top of the second. PostgreSQL organizes its heap into 8 KB pages, reads them into a buffer pool, modifies them in memory, and writes them back — see Pages: The Unit of Everything and The Buffer Pool for the internals. Durability comes from the write-ahead log: before a commit is acknowledged, the log record is written *and flushed to stable media*, which is what fsync is for (Write-Ahead Logging).
That is why the physical characteristics of the volume become database latency. fsync on a device with 8 ms of write latency puts a floor under your commit latency that no amount of query tuning removes. Provisioned IOPS is not a storage nicety; it is directly the ceiling on transactions per second for a write-heavy workload. Block storage is the layer where an infrastructure choice turns into a Database Internals fact.
It is also the reason a bucket cannot substitute. Object storage offers whole-object replacement and no flush semantics. A storage engine that must modify one 8 KB page inside a 40 GB table has nothing to work with.
fsync contractAttachment, lifecycle and the two kinds of "local disk"
A block volume normally attaches to exactly one instance at a time. That single-attach property is not an arbitrary limit — a filesystem assumes it is the only writer, and two instances mounting the same volume read-write without a cluster filesystem corrupt it quickly and quietly. Some providers offer multi-attach; it requires a filesystem designed for it, and it is not the answer to "two servers need the same files". That is File Storage.
The volume has its own lifecycle, separate from the instance. Detach it, the data stays. Terminate the instance, the volume can survive if you configured it to. Snapshot it, and you have a point-in-time copy in object storage that can seed a new volume in another zone. That separation is what makes stateful workloads possible on replaceable compute — and it is exactly what a Kubernetes persistent volume claim wires up on your behalf (Stateful Workloads: Databases Are Not Stateless APIs).
The trap is the *other* local disk. Many instance types come with directly-attached storage that is fast, cheap and ephemeral: it disappears when the instance stops. Excellent for scratch space, temporary files and caches. Catastrophic for a database, and the mistake is easy because both appear as /dev/… inside the guest and neither announces which kind it is.
| Network-attached volume | Instance-local disk | Object storage | |
|---|---|---|---|
| Survives instance stop | Yes | No — the data is gone | Yes |
| Survives instance termination | If configured to persist | No | Yes |
| Latency | Low, but crosses a storage network | Lowest — physically attached | Network round trip per operation |
| Random writes | Yes | Yes | No |
| Attachment | One instance (multi-attach is a special case) | One instance, always | Any authorized identity |
| Right for | Databases, WAL, persistent volumes, root disks | Scratch, caches, build workspaces, spill files | Uploads, backups, snapshots, artifacts |
It is the one storage type that fills up
Object storage never runs out. A shared filesystem usually grows elastically. A block volume is provisioned at a size, and it will reach that size — and the way it reaches it is almost always something nobody was watching: WAL segments that stopped being recycled because a replication slot fell behind, an autovacuum that cannot keep up with bloat, a log file with no rotation, or a table that simply grew.
The failure is nastier than the arithmetic suggests. The instance stays up. CPU is normal. The load balancer's health check hits /health, which does not write anything, and reports the target healthy. Meanwhile the database has stopped accepting writes and every user-facing write path is returning 500s. This is the archetypal case of the infrastructure signal and the application signal disagreeing, and the reason free-space alerting on a database volume is not optional.
Growing a volume online is possible on most providers, and it is a two-step operation: expand the volume, then expand the filesystem inside the guest. Teams forget the second step and conclude that resizing did not work. Shrinking is usually not possible at all, which makes over-provisioning a one-way cost commitment — see Right-Sizing Without Causing an Outage.
Bars are relative weights, not currency. Real rates depend on provider, region, commitment and volume.
Key points
- Block storage exposes fixed-size blocks with random write and a flush primitive; the filesystem and the database engine are two layers built on top of it.
- Device latency becomes commit latency, because
fsyncon the write-ahead log is on the transaction path. - One volume, one instance. Two writers without a cluster filesystem corrupt the data quietly.
- The volume's lifecycle is separate from the instance's — that separation is what makes stateful workloads on replaceable compute possible.
- It is the only storage contract that can fill up, and it fills up while every health check stays green.
The loop, answered
Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.
- • The provider attaches a virtual disk to the instance; the hypervisor presents it as a block device to the guest kernel.
- • The guest filesystem maps files and directories onto block ranges and issues reads, writes and flushes to the device queue.
- • The database writes its WAL record and calls
fsyncbefore acknowledging a commit; the volume must confirm the data is on stable media. - • The provider replicates the underlying blocks across devices — and, depending on the offering, across a zone — so a device failure is not a data loss.
- • Snapshots copy changed blocks into object storage, from which a new volume can be created, including in another zone.
- • Own free space, and alert on it well before it matters. This is the highest-value alert on any database host.
- • Own the performance tier: IOPS and throughput are provisioned choices that directly bound your transaction rate.
- • Own filesystem expansion after a volume resize — the second half of an operation people think is one step.
- • Own snapshot schedule, retention and — critically — restore testing. See Restore Testing.
- • Own the delete-on-termination setting, in both directions: wrong one way you lose data, wrong the other you accumulate paid-for orphans.
- • Volume full: the database refuses writes, the instance stays up, and the health check keeps reporting healthy.
- • Inode exhaustion from millions of tiny files — "no space left on device" with free gigabytes showing.
- • IOPS throttling once a burst credit balance is exhausted: latency multiplies with no change in query plans or traffic.
- • A zone failure taking the volume with it, because a network-attached volume normally lives in one zone and cannot be attached from another.
- • Two instances mounting the same volume read-write and corrupting the filesystem — often silently, discovered days later.
- • Detach on an unclean shutdown leaving the filesystem needing a check, which turns a 40-second restart into a much longer one.
- • Vertical only: bigger, faster volumes. There is a ceiling per volume and it is lower than people expect.
- • IOPS runs out before capacity for transactional workloads; throughput runs out first for analytical scans.
- • Beyond one volume you are in database territory — read replicas, partitioning, sharding — not storage territory. See Managed Databases.
- • Growing is usually online and one-directional; you can almost never shrink, so over-provisioning is a lasting commitment.
- • The volume inherits the instance's trust boundary: anyone with root on the instance reads everything on the filesystem.
- • Encryption at rest is provider-managed and near-free; the meaningful control is who may create, attach and snapshot volumes.
- • A snapshot is a full copy of the data with its own access policy. A snapshot shared publicly is a data breach with no server involved — see Public Exposure, Read With Context and Backups Are Sensitive Data Copies.
- • Detached volumes and old snapshots are unmonitored copies of production data. Include them in the data inventory or they will be the thing nobody remembered.
- • Provisioned capacity bills whether or not it is used — headroom is real money, which is the tension in Right-Sizing Without Causing an Outage.
- • The performance tier is frequently the larger line item on a database volume.
- • Snapshots bill incrementally per changed block, so a high-churn database with aggressive retention costs far more than its volume.
- • Orphaned volumes from terminated instances are pure waste and are invisible unless somebody looks. See Idle Capacity: Headroom or Waste?.
- • Free space and free inodes, with an alert threshold that leaves time to act rather than time to page.
- • Device queue depth and p99 write latency — the leading indicators of both throttling and a degrading storage path.
- • Burst-credit balance where the tier has one; it depletes silently and the cliff is abrupt.
- • Snapshot success *and* the last successful restore test. A snapshot job that has been failing for six weeks reports nothing.
- • The signal that lies: instance CPU and the application health check. Both stay perfect through a full-disk outage.
- • Instance-local ephemeral disk, when the data is genuinely disposable — caches, scratch, spill files. Faster and cheaper, and losing it on restart is the accepted contract.
- • A managed database, which hides the volume entirely and takes the capacity and snapshot work with it. Usually the right first answer — see Managed Databases.
- • Object storage for anything append-only and large: logs, backups, media. Do not keep them on the database volume.
- • No persistent storage at all: a stateless service with its state in a managed database needs nothing beyond its root disk, and that is the simplest possible answer.
- • Buys the semantics a storage engine requires; costs you a provisioned, single-attach, zone-bound resource you must size and monitor.
- • Buys low, predictable latency; costs money proportional to the performance tier rather than to usage.
- • Buys data that survives instance replacement; costs you a lifecycle to manage — attach, detach, snapshot, delete — that nothing manages for you.
- • Buys snapshots as cheap point-in-time copies; costs you a retention policy and an untested-restore risk if you never exercise them.
What people believe, and what is true
A cloud disk is a disk.
Most are network-attached. They degrade as latency rather than as errors, and they are bound to one availability zone.
The local NVMe on this instance type is the fastest option, so use it for the database.
It is ephemeral. Stopping the instance destroys it. It is excellent scratch space and a data-loss incident waiting for a maintenance event.
We resized the volume but nothing changed.
Resizing the volume does not resize the filesystem on it. That is a second command inside the guest.