How to Decide Between Object Storage and Block Storage for Media

For most media on a web host, object storage is the right default: images, video, audio and backup archives are written once, read many times, and never need to be mounted as a filesystem. Block storage is the right choice when the application expects a local disk, such as a database volume, a video transcode scratch area, or a CMS upload directory that code opens with ordinary file calls. Choosing between object storage and block storage for media comes down to one question: does your software need a filesystem, or can it speak HTTP?

Both types sit behind the same network, but they expose completely different interfaces. Block storage gives you a raw device over a protocol such as iSCSI or NVMe over TCP. You partition it, format it with a filesystem, and mount it. The kernel sees a disk. Object storage gives you an HTTP API. You issue requests with GET, PUT, DELETE and HEAD against a bucket and a key. There is no device, no mount point and no inode. That single difference drives almost every other tradeoff.

How each one behaves under the hood

With block storage, every read and write is a block operation at a fixed offset on the device. The filesystem layer above it decides where bytes land. When you upload a photo, the web process writes it to a path like /var/www/uploads/2024/cat.jpg, and the filesystem allocates blocks. Later, a request for that image becomes a series of block reads, usually served from the page cache if the file was recently touched. Latency is low and predictable, and the storage looks exactly like the disk on your laptop.

Object storage works differently. A PUT sends the whole object, or a part of it in a multipart upload, to a service that stores it as an immutable blob under a key. A GET returns the bytes plus response headers. You can inspect those headers with a plain command:

curl -sI https://storage.example.net/media/clip.mp4
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 48211904
ETag: "9f2c1a7d4e8b6f03"
Last-Modified: Thu, 14 Mar 2024 09:12:44 GMT

The ETag is the object's identity for caching and conditional requests. Send If-None-Match with that value and you get a 304 Not Modified when nothing changed, which saves bandwidth on repeat views. For block storage there is no equivalent header, because the filesystem handles caching and the web server handles conditional requests from its own metadata.

Consistency, durability and the mental model

Block volumes are usually attached to one instance at a time. If you need the same data on two machines, you either share the volume with a cluster filesystem, which is fiddly, or you copy it, which drifts. Object stores are built for many readers and writers at once. Multiple servers can read the same key concurrently without coordination, and object writes are atomic: a reader sees the old object or the new one, never a half-written file. That property is why media pipelines push finished assets to object storage and let the CDN pull from there.

Durability also differs in kind. A block volume is as durable as the volume itself and whatever snapshot policy you configure. Lose the volume and you lose the data unless you have snapshots or replication. Object stores typically replicate objects across devices or locations internally, so a single disk failure is not your problem. You still need versioning and lifecycle rules, but the baseline is higher. Neither type is a backup on its own. If a process deletes a key or overwrites a block, the deletion is real.

Object storage vs block storage for media: the practical split

Point your user-facing media at object storage. Images, video, audio and downloadable files are read far more often than they are written, they benefit from HTTP caching headers, and they scale horizontally without you resizing a volume. A typical upload path looks like this from the application's side:

aws s3 cp ./render/final.mp4 s3://media-bucket/video/final.mp4 \
  --content-type video/mp4 \
  --cache-control "public, max-age=31536000, immutable"

The --cache-control flag matters more than the upload itself. Setting a long max-age with immutable tells browsers and CDNs they never need to revalidate that key, which is correct when the key includes a content hash or version. If your keys are stable paths that get overwritten, use a shorter max-age or an ETag based revalidation instead, or you will serve stale media.

Keep block storage for the things that cannot tolerate HTTP semantics. Databases, session stores, search indexes, and any application that calls fopen, stat or mmap on a path need a real filesystem. Video transcoding is a good example: ffmpeg writes temporary segment files and reads them back in sequence, and a local block volume handles that with far less friction than an object API. The same goes for a CMS that expects wp-content/uploads to exist as a directory. You can put a FUSE gateway or a sync agent in front of object storage to fake a filesystem, but you inherit latency and consistency surprises, so do it only when you must.

Backups are the ambiguous case. A database dump is a single large file written once and read rarely, which suits object storage well. A live database data directory is not a backup and should not be copied to object storage while running. Snapshot the block volume, then export the snapshot to object storage if you want offsite copies. That gives you a consistent point-in-time image plus durable retention.

When each choice becomes wrong or expensive

Block storage becomes the wrong choice when your media volume grows faster than your budget or your patience for resizing. Every resize means detaching, expanding and sometimes extending the filesystem, and some filesystems only grow online. You also pay for provisioned capacity whether or not it is full, and you pay for IOPS tiers if your provider sells them separately. A media library that doubles every few months will keep you in that loop. The moment you find yourself mounting a volume just to serve static files over HTTP, you have built an object store badly.

Object storage becomes the wrong choice when your code needs random writes to a file, file locking, or directory listings that stay consistent under heavy churn. Renaming a key is a copy plus a delete, not a metadata operation. Appending to an object means rewriting it. Small objects are also inefficient: each one carries request overhead, and listing a bucket with millions of tiny keys is slow. If your pipeline generates thousands of thumbnails per minute, batch them into archives or keep the working set on block storage and publish only the final set.

Cost traps cut both ways. Object storage bills for storage, requests and egress, so a hot media file served directly to users can cost more in transfer than in storage. Put a CDN in front and set cache headers so most requests never reach the origin. Block storage bills for provisioned size and sometimes for snapshots, so stale snapshots quietly accumulate. Check both ledgers before you assume one is cheaper. The answer depends on your access pattern, not on the storage type's reputation.

What to do next

Map your data by access pattern before you migrate anything. List the paths your application opens as files and confirm they need a filesystem. Everything else, especially media served to browsers, should move behind an HTTP API with explicit cache headers. Then run one small experiment: copy a representative media set to object storage, serve it through a CDN, and compare request logs from both paths over a normal week. Let the logs and your own cost breakdown decide, and keep the block volume for the workloads that genuinely need a disk.

Related articles

Subscribe to our newsletter

Get the latest hosting tips, performance insights, and industry news.