Skip to main content

Block vs File vs Object Storage

Three Ways to Say “Store This”

Say you have a gigabyte of data — a database, a folder of documents, a video. Before you can store it in the cloud, you have to answer a question most people never notice they're answering: which shape of storage do you want it in? There are three, and they are not interchangeable. The wrong one doesn't just cost more — it makes some of the things you want to do impossible.

The three shapes form a ladder, each rung built on the one below it:

  • Block — the bottom rung, closest to a physical disk: a raw array of fixed-size blocks you address by number. No names, no files — you build structure on top.
  • File — the middle rung: a tree of names and directories with the familiar open / read / write / seek, that many machines can mount and share.
  • Object — the top rung: a flat key → an immutable blob, reached over HTTP, that scales to trillions of items and costs the least per gigabyte.

Climbing the ladder trades one thing for another, every rung: up buys you scale, sharing, and a lower price; down buys you latency, in-place editing, and control. (This is the layer above the disk physics from The Memory Hierarchy: Disk, RAM, CPU Cache and Sequential vs Random Access — those decide how fast a byte moves; this decides the shape you access it through.) Let's climb it from the bottom, on a real machine.

The storage abstraction ladder, three rungs, with an axis on the left: climb up for scale, sharing, and lower cost; climb down for latency, control, and in-place edits. The bottom rung, in amber, is BLOCK: a raw array of fixed-size numbered blocks, drawn as a row of numbered cells addressed by number, mapped to AWS EBS, single-attach with the lowest latency. The middle rung, in blue, is FILE: a POSIX tree of names and directories with in-place writes, drawn as a folder branching to files, mapped to AWS EFS, mounted so many hosts share it at once. The top rung, in violet, is OBJECT: a flat key mapping to an immutable blob accessed over HTTP, drawn as a bucket of keyed blobs, mapped to AWS S3, with infinite scale, the cheapest per gigabyte, and no partial write. Each rung is built on the one below, trading control and latency for scale and sharing as you climb.

Block: a Raw Array of Numbered Blocks

The bottom rung is what a disk actually is to the operating system: a long, flat array of fixed-size blocks — 512 bytes or, today, 4 KB each — with no names and no structure. You don't ask for "the file cat.jpg"; you ask for "block number 91,204." Let's make one for real (a loopback device stands in for a cloud volume):

$ blockdev --getsize64 /dev/loop   → 67108864 bytes   (67 MB of addressable space)
$ blockdev --getbsz  /dev/loop   → 4096 bytes per block   (you address by BLOCK NUMBER, not a name)
$ od -An -c /dev/loop | head -1  → \0 \0 \0 …   (raw block 0 — no files, no names, just bytes)

That's the whole abstraction: addressable bytes, in numbered chunks, and nothing else. There is no concept of a file here. To get files, you (or your operating system) build a filesystem on top — or a database writes its pages straight to the raw blocks, which is exactly why databases love block storage: total control over the layout, and the lowest latency of the three, because it's a device attached directly to one machine.

And that's the catch built into the shape: a block volume is single-attach — it belongs to one host at a time, like a physical drive bolted into one server. You can't hand the same volume to five hundred machines. In the cloud this rung is AWS EBS (GCP Persistent Disk, Azure Managed Disk) — the thing you attach to one VM to hold its boot disk and its database files.

File: Names, a Tree, and Sharing

Put a filesystem on those raw blocks and you climb to the middle rung — the one you already know from your laptop. Now there are names, directories, and the whole POSIX vocabulary: open, read, write, seek, permissions, locks. Watch a block device become a file abstraction:

$ mkfs.ext4 /dev/loop && mount /mnt/v     → now a TREE of names with POSIX read/write
$ echo "hello earth" > /mnt/v/note.txt          → cat note.txt: hello earth
$ printf PLANET | dd of=note.txt bs=1 seek=6 conv=notrunc    (write 6 bytes IN PLACE at offset 6)
$ cat note.txt                                   → hello PLANET   ← 6 bytes changed, size still 12 B

Two things there matter for the rest of the lesson. First, in-place editing: I changed six bytes in the middle of the file without rewriting the rest — seek to the offset, write, done. Second, and this is the whole reason file storage exists as a separate thing: a filesystem can be exported over a network protocol (NFS, SMB) so that many machines mount the same tree at once and read and write it concurrently. Block storage can't do that; a filesystem is built for it.

In the cloud this rung is AWS EFS (Amazon FSx, GCP Filestore, Azure Files): a managed, shared POSIX filesystem you mount on as many servers as you like. It's the answer when a fleet of app servers needs to see the same files — shared uploads, a content directory, an ML dataset mounted read-only by a hundred training nodes.

Object: a Flat Key → an Immutable Blob

The top rung throws away the tree entirely. Object storage is a flat namespace: you PUT a blob under a key and later GET it by that key — over HTTP, not a mount. Each object carries its data plus rich custom metadata, and there is no filesystem underneath that you can see. Here it is for real (MinIO speaks the S3 API):

$ PUT media/2024/07/cat.jpg   → size=10,485,760 B  type=image/jpeg  metadata={album, camera}
$ GET over HTTP               → http://…/media/2024/07/cat.jpg   (fetched by URL, NOT mounted)
$ LIST                        → key = '2024/07/cat.jpg'   ← ONE flat string; the '/' is just a char

Notice the key. It looks like a path — 2024/07/cat.jpg — but there are no folders. It is one flat string; the / is an ordinary character, and the tidy "folder" view in the S3 console is a cosmetic illusion the UI paints by splitting on slashes. There are no directories to create, no rename (that's a copy-then-delete), no locks.

And the defining constraint — the one that decides half your architecture — is that objects are immutable. There is no "write byte 500." To change anything, you replace the whole object with a new PUT:

$ change 1 byte in the middle → there is no such API. To edit, re-PUT the WHOLE object:
    re-uploaded all 10,485,760 B to change 1 byte.   (a file would have written 1 byte in place.)
$ update-byte-range API?      → False   (objects are IMMUTABLE)

You give up in-place editing and low latency; in return you get something the rungs below cannot touch — effectively infinite scale (trillions of keys, no volume ceiling), the cheapest price per gigabyte, eleven nines of durability, and an address that is already a URL. This rung is AWS S3 (GCP Cloud Storage, Azure Blob), and it is where the internet keeps its photos, videos, backups, logs, and data lakes.

What’s Cheap, Possible, or Forbidden

Here's the idea worth carrying out of this whole lesson, and it's bigger than three definitions: the abstraction you pick decides, before you write a line of code, what operations are cheap, which are expensive, and which are simply impossible. The disk underneath barely matters; the shape is everything.

You already saw the sharpest example: editing a few bytes is 6 bytes written in place on block and file, but a whole-object re-upload on object storage — 10 MB to change one, because objects are immutable. That single fact rules object storage out for anything that mutates in place, and rules it in for anything write-once. Below, throw operations at all three and watch each answer — cheap, costly, or impossible. Try to store a hundred billion items on a block volume; try to run a database on object storage; try to share a block volume with 500 servers. The failures are the lesson.

Pick an operation — edit a few bytes, share with 500 servers, store 100 billion items, run a database, serve to the world over HTTP — and watch block, file, and object each answer cheap, costly, or impossible, with the mechanism and an effort bar. The signature moment: editing a few bytes is 6 bytes in place on block and file, but a whole re-upload of the object — because objects are immutable. The abstraction, not the disk underneath, decides what's even possible.

Three Prices, and How to Choose

Three shapes, three price tags — and the ranking is not the one people assume. Per gigabyte-month, object is the cheapest (0.023onS3),blockisinthemiddle( 0.023 on S3), **block is in the middle** (~0.08 on EBS), and file is the most expensive ($0.30 on EFS) — because with file storage you're paying for a whole managed shared filesystem, not just space. (The headline price hides fine print: object adds per-request fees, and block adds provisioned IOPS — but the per-GB order holds.)

So how do you actually choose? Two rules cover almost every case:

  1. Pick the lowest rung the workload genuinely needs. Each step up trades away latency and in-place control for scale and sharing you may not need — and "cheapest per gigabyte" is a trap if the shape can't do the job.
  2. Or pick by the one operation you need to be cheap — because the abstraction decides that.

In practice that lands as: block for a machine's boot disk and its database data files — one owner, low latency, a real filesystem. File when a fleet of servers must share the same tree and expect POSIX. Object for everything write-once-read-many at scale — backups, user uploads, images and video, static website assets, logs, data lakes — especially anything you'll serve straight over HTTP behind a CDN.

The cloud mapping and the three prices for block, file, and object storage, ordered cheapest to priciest per gigabyte with a bar proportional to price. On top, in violet, OBJECT mapped to AWS S3 at about $0.023 per gigabyte-month — the cheapest, plus per-request fees — reached for backups, media, static assets, and data lakes; its price bar is a sliver. In the middle, in amber, BLOCK mapped to AWS EBS at about $0.08 per gigabyte-month plus provisioned IOPS, reached for boot volumes and database data files; a medium bar. At the bottom, in blue, FILE mapped to AWS EFS at about $0.30 per gigabyte-month — the priciest, because you are paying for a managed shared filesystem — reached for a shared filesystem many servers mount at once; a full-width bar. The bottom line: cheapest per gigabyte is not always right — pick the lowest abstraction the workload needs.

What to Remember

  • Three abstractions, one ladder. Block = raw numbered blocks, single-attach, lowest latency (EBS). File = a shared POSIX tree, mounted by many hosts (EFS). Object = a flat key → an immutable blob over HTTP, infinite scale, cheapest (S3). Each rung is built on the one below.
  • ⭐⭐ The abstraction decides what's cheap, possible, or forbidden — before any code. Editing a few bytes is in place on block/file but a whole-object re-upload on object; a block volume can't be shared with 500 hosts; a database can't live on object storage.
  • Object storage is not a filesystem over HTTP. Flat keys (the / is just a character; folders are a console illusion), immutable objects (re-PUT to change anything), rich metadata, no seek/rename/lock.
  • Cheapest per GB isn't always right. Object is cheapest, file the priciest, block in between — but you pick the lowest rung the workload needs, or the one that makes your key operation cheap.
  • The map: boot volumes & database files → block/EBS; a shared filesystem for many servers → file/EFS; backups, media, static assets, data lakes → object/S3.

Object storage is the one that runs the modern internet at scale — which raises the obvious question: how do you actually build a thing that stores trillions of immutable blobs, cheaply, with eleven nines of durability, addressed by a flat key? That's the next lesson — Design an S3: Blob Storage as a System — where you take this abstraction apart and assemble the metadata plane and the data plane that make it work.