Storage & Managed Data

Object, Block and File

Three storage contracts, not three products: a blob you address by key and replace whole, a raw device your operating system formats, and a filesystem several machines mount at once. Choosing the wrong contract is the expensive mistake — the vendor is a footnote.

The question this answers

Infrastructure question

Which storage contract does this data need — a whole-blob namespace, a raw device, or a filesystem several machines share?

Application requirement

One ordinary product writes three unrelated kinds of data: user-uploaded profile photos, a PostgreSQL data directory, and a folder of render assets that four workers read concurrently. No single storage service serves all three well, and the difference is not price.

What it provides

A vocabulary that survives providers: the unit you address, the operations you are given, how many machines may attach at once, and what one operation costs in latency.

Application RequirementInfrastructure RequirementComputeNetworkStorageIdentityDeploymentScalingReliabilityObservabilitySecurityCostTrade-offs

The contract is the unit you address and the operations you get

Every storage decision reduces to four questions, and none of them is "which service is cheapest". *What is the smallest thing I can address?* *Can I modify part of it, or must I replace the whole thing?* *How many machines can attach at once?* *What does one operation cost me in latency?* Answer those and the storage type falls out; skip them and you end up running a database on a network filesystem because it was the box already on the diagram.

Object storage addresses a whole object by a key over HTTP. There is no seek, no partial write in the general case, no directory that means anything to the system — the slashes in a key are characters. You PUT an object and you GET an object, and both are one network round trip with the tail latency of a network round trip.

Block storage addresses fixed-size blocks on a device. It is deliberately dumb: it knows nothing about files, and it is your operating system's filesystem driver that turns blocks into inodes, directories and byte ranges — this is exactly the layering the Operating Systems domain teaches under File Systems: From Path to Blocks. Because the device is dumb, it is fast and it supports random writes, which is why databases live on it.

File storage sits between them: a real filesystem, with POSIX semantics — locks, permissions, rename, partial writes — exported over a network protocol so more than one machine can mount it at the same time. You buy sharing and you pay for it in per-operation latency, because every stat is now a network call.

ObjectBlockFile
Addressed unitWhole object, by keyFixed-size block on a deviceFile and byte range, by path
Partial modificationReplace the object (some providers offer ranged writes; do not design around it)Yes — random writes are the pointYes, with POSIX semantics
Attached byAnyone with credentials, over HTTP, from anywhereUsually exactly one instance at a timeMany machines, concurrently
Latency shapeNetwork round trip per operation, high tailSub-millisecond to low-millisecond, tight tailNetwork round trip per *filesystem operation*
Capacity modelEffectively unbounded; you never provision sizeProvisioned; you resize it and you can fill itUsually elastic, but throughput is provisioned
Native fitUploads, backups, logs, static assets, data lakesDatabase files, VM root disks, write-ahead logsShared assets, legacy apps that need a mount
The same four questions, answered three ways

Where each contract attaches in the stack

Drawing the layers makes the trade-offs stop being arbitrary. Block storage attaches *below* the filesystem, which is why the guest OS sees a disk and why a database can control its own page layout and fsync behaviour. File storage attaches *at* the filesystem layer, replacing the local driver with a network client — so everything above it works unmodified, and everything above it now has network latency underneath it. Object storage attaches *above* the filesystem, as an API the application calls directly; there is nothing to mount and nothing to format.

That single picture predicts most of the surprises. A database on object storage is slow not because object storage is slow but because the database wants to modify 8 KB pages in place and the contract only offers whole-object replacement. A shared filesystem feels like a local disk until an ls of a directory with 200,000 entries takes eleven seconds, because every entry is a network operation that a local disk answered from the page cache.

Where each storage contract attaches, and how a failure at that layer presents
Application
provides Business objects: an upload, a row, a report
fails as The user sees a 500 or a missing image; the cause is one layer down.
Object API (HTTP)depth: This domain — see object-storage
provides Key → bytes + metadata, from any machine with credentials
fails as Elevated 5xx or throttling on a hot key prefix; the application must retry with backoff.
Filesystem (local or network)depth: Operating Systems — file systems
provides Paths, directories, locks, partial writes, POSIX semantics
fails as Disk full, inode exhaustion, or — on a network filesystem — a stalled mount that hangs every process touching it.
Block devicedepth: Operating Systems — devices and I/O
provides Fixed-size addressable blocks with random write
fails as I/O errors and rising queue depth; the application sees latency, not errors, until it sees both.
Replicated physical media
provides Durability the provider is contractually responsible for
fails as Almost never visibly — this is the layer the shared-responsibility split actually removes from you. See Shared Responsibility.

One system, three contracts, on purpose

Real systems use more than one, and the diagram should say which is which. The photo-sharing service below puts uploads in object storage because they are large, immutable and served to the public edge; puts the database on block storage because PostgreSQL needs random writes and a durable fsync; and gives the render workers a shared filesystem because the third-party rendering binary takes a directory path and cannot be changed.

That last one is the honest reason file storage exists in most architectures: not because it is the best contract, but because something in the stack demands a mount and rewriting it is not on the roadmap. That is a legitimate reason. It should just be written down as one, so nobody later assumes the shared filesystem was a design preference and adds three more workloads to it.

Three contracts in one system. The reason each was chosen is in the note.PROVIDER-NEUTRAL
Virtual network
Private subnetprivate
API serviceprivate
Render workers ×4private— third-party binary takes a directory path
PostgreSQLprivate
Block volumeprivate— random writes, durable fsync, one attachment
Shared filesystemprivate— four mounts, POSIX semantics, per-operation latency
Object storage bucketprivate— immutable blobs by key, served through the CDN
API servicePostgreSQL· SQL
PostgreSQLBlock volume· page writes + WAL
Render workers ×4Shared filesystem· mount
API serviceObject storage bucket· PUT / GET by key

Key points

  • Object, block and file are three access contracts, not three price tiers: the unit you address and the operations you get are what differ.
  • Block storage attaches below the filesystem, file storage replaces it, object storage sits above it as an API — that layering predicts every performance surprise.
  • One attachment, many attachments, or no attachment at all is usually the fastest way to narrow the choice.
  • A well-designed system normally uses at least two of the three, and should say in the diagram why each one was chosen.
  • File storage is often chosen because software demands a mount, not because it is the best contract. Write that down as the reason.

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.

How it works
  • Object: the application signs an HTTP request, the provider routes it by bucket and key, stores the bytes across replicated media and returns a version or entity tag.
  • Block: the hypervisor presents a virtual disk; the guest kernel's filesystem driver maps files to block ranges and issues reads and writes to the device queue.
  • File: the client mounts a network filesystem; every open, stat, read and lock becomes a protocol operation against the server, cached according to the protocol's consistency rules.
  • The provider replicates the underlying media in all three cases — the durability guarantee is theirs, the correctness of what you write is yours.
What you still own
  • Decide and record the contract per data class, not per application. "User uploads" and "the database" are different classes even inside one service.
  • Own capacity for block and throughput for file; object storage is the only one of the three you do not size in advance.
  • Own lifecycle: object storage grows forever unless someone writes a rule. See Storage Lifecycle: Hot, Warm, Archive, Delete.
  • Own the consistency assumptions your code makes — especially any code that lists a directory or a key prefix and assumes it is complete.
How it fails
  • A database on a network filesystem: correct, and then catastrophically slow or corrupt when the protocol's locking semantics do not match what the engine assumed.
  • A full block volume — the database stops accepting writes while the instance, the load balancer and the health check all stay green.
  • A stalled network filesystem mount: every process that touches the path blocks in uninterruptible I/O, and the box becomes unkillable rather than unhealthy.
  • Object storage used as a work queue: listing a prefix is not a queue read, and two workers happily pick up the same key.
How it scales
  • Object storage scales capacity without you; what runs out first is request rate against a hot key prefix, and the fix is key naming, not more storage.
  • Block storage scales by provisioning bigger or faster volumes — a vertical move with a ceiling, and IOPS usually runs out before capacity.
  • File storage scales capacity elastically but throughput is provisioned, and metadata operations are the dimension that saturates first.
  • The dimension that actually runs out at scale is rarely bytes. It is operations per second and the latency of each one.
Security
  • Object storage is reachable from anywhere with credentials — its trust boundary is identity, not network position, which is why a misconfigured bucket is a headline and a misconfigured volume is a ticket.
  • Block volumes inherit the instance's exposure: whoever can reach the instance can read the filesystem on it.
  • File storage is reachable by every machine that can mount it, so its blast radius is the whole mount group — the set of machines is the access-control decision.
  • Encryption at rest is a default on all three at most providers; the meaningful question is who holds the key and who can decrypt. See Key Management and Encryption at Rest.
Cost shape
  • Object: pay for stored gigabytes, requests, and egress — the request meter surprises workloads with many small objects.
  • Block: pay for provisioned capacity whether or not you use it, plus provisioned IOPS or throughput where offered. An idle volume attached to a stopped instance still bills.
  • File: pay for capacity plus provisioned throughput, and it is usually the most expensive per gigabyte of the three.
  • The cheapest contract is usually the one that matches the access pattern, because a mismatch is paid in request counts, not in gigabytes.
What to watch
  • Object: request rate, 4xx/5xx by operation, and throttling responses on a prefix.
  • Block: queue depth, average and p99 device latency, and free space — free space is the one that turns into an outage without warning.
  • File: metadata operations per second and client-side latency, split by mount.
  • The signal that lies: total bytes stored. It looks calm through every one of the failures above.
Simpler alternatives
  • The local disk that came with the instance. For a cache, a scratch directory or a build workspace, ephemeral local storage is faster, simpler and free — and losing it on restart is fine by definition.
  • The database you already run. A few thousand small records with transactional requirements belong in PostgreSQL, not in a bucket with a naming convention.
  • No storage at all: recompute it. A derived thumbnail or an aggregate that costs 40 ms to regenerate does not always need a durable home and a lifecycle policy.
  • One contract instead of three. A small system that puts uploads in object storage and everything else in a managed database is a complete, defensible design — see No Cargo-Cult Infrastructure.
What adopting this costs
  • Object storage buys unbounded, cheap, globally reachable capacity; it costs you transactions, joins, in-place updates and predictable listing.
  • Block storage buys speed and full filesystem semantics; it costs you a provisioned, single-attach resource that you must size, monitor and grow.
  • File storage buys sharing without changing the application; it costs per-operation latency, a shared blast radius, and the highest price per gigabyte.
  • Using all three is correct and adds three operational surfaces — backup, capacity, access control and cost each need an owner per contract.

What people believe, and what is true

Claim

Object storage is just a cheaper disk.

Reality

It is a different contract. No random writes, no filesystem, no directory semantics, and a network round trip per operation — none of which a disk imposes.

Claim

A shared filesystem is the simple answer when two machines need the same data.

Reality

It is the answer that requires no code change. It also makes both machines share a failure domain and a latency profile, and it is the most expensive per gigabyte.

Claim

Storage choice is a cost decision.

Reality

It is an access-pattern decision that has cost consequences. Systems that optimize the price per gigabyte first end up paying for it in request charges and rewrites.

Apply it