Cloud Storage Systems

Cloud Storage Systems

Definition: The three fundamental storage abstractions in cloud architecture — Object Storage, Block Storage, and File Storage — each with distinct access patterns, durability guarantees, and cost profiles. AWS S3, launched in 2006 as one of AWS’s first services, effectively created the object storage category and popularized “storage as an HTTP API” rather than a filesystem; block and file storage are older concepts (SAN and NAS respectively) that cloud providers virtualized and offered as on-demand, API-provisioned services in the years that followed.

How It Works

  • Object Storage (AWS S3, GCS, Azure Blob, Cloudflare R2): a flat key-value store accessed over HTTP/REST rather than a filesystem API; each object is immutable (you replace it, not patch it in place), carries its own metadata, and scales to effectively unlimited capacity across storage classes tuned for access frequency (S3 Standard, Infrequent Access, Glacier for cold archival). Ideal for unstructured data like images, backups, and log archives.
  • Block Storage (AWS EBS, GCP Persistent Disk): raw, low-latency disk volumes attached to a single VM over a virtual SAN, formatted with a normal filesystem (ext4, NTFS); supports random-access reads/writes, making it the right choice for database data files and boot volumes. Performance is typically provisioned in IOPS and throughput tiers independent of raw capacity.
  • File Storage (AWS EFS, Azure Files): a managed network file system (NFS/SMB) that can be mounted concurrently by many VMs at once, giving POSIX-like shared file access that block storage (single-attach) can’t provide.
  • Durability vs. availability are distinct guarantees: S3 advertises 11 nines of durability (near-zero chance of losing the underlying bytes via redundant storage across facilities) but only around 4 nines of availability (chance a request succeeds at any given moment) — a common source of confusion when reasoning about SLAs.
  • Consistency model matters for correctness: S3 offers strong read-after-write consistency for new objects; some object stores are only eventually consistent, which can surprise code that writes then immediately reads.
  • Multipart upload lets large objects (commonly required above roughly 100MB, mandatory above 5GB on S3) be split into parts uploaded in parallel and reassembled server-side, both speeding up large transfers and allowing a failed part to be retried without re-uploading the whole object.

Why It Matters

  • Selecting the right storage abstraction balances cost, throughput, latency, and access pattern — using the wrong one is a common source of both performance problems and unnecessary spend
  • Object storage’s near-infinite scale and low per-GB cost make it the default backbone for data lakes, static asset hosting, and backup retention across virtually every cloud architecture
  • Decoupling storage from compute — any of the three can be provisioned, resized, or replaced independently of the VMs or containers using it — is foundational to how cloud elasticity works at all
  • Presigned URLs and direct-to-object-storage uploads let applications offload large file transfers away from their own servers entirely, avoiding a bottleneck that would otherwise sit in the application’s own request path

Under the Hood: How Object Storage Reaches Eleven Nines of Durability

An advertised durability figure like “99.999999999%” (eleven nines) isn’t marketing — it’s a direct consequence of how object storage systems physically store data, and it’s worth understanding why it’s achievable at that scale. When an object is written, the storage system doesn’t just save one copy; it splits the object using erasure coding (similar in spirit to RAID, but computed across many more fragments) into data shards plus parity shards, then distributes those shards across multiple physical drives, racks, and (for the highest durability tiers) separate physical facilities or availability zones. The system can reconstruct the full object even if several shards are lost simultaneously — as long as enough of the remaining shards survive to satisfy the erasure-coding threshold — meaning it tolerates individual disk failures, whole-rack failures, and even a facility outage without losing the underlying bytes. Durability and availability are then genuinely separate numbers: durability asks “will these bytes still exist somewhere, eventually,” which erasure coding across facilities protects extremely well, while availability asks “can I retrieve them right now,” which depends on the request path (network, load balancers, the API layer) actually being reachable at this exact moment. A facility can be briefly unreachable due to a network partition — hurting availability — while the underlying data remains perfectly intact and recoverable the moment connectivity returns, which is exactly why the two guarantees carry different numbers of nines.

Comparison: Object Storage vs Block Storage vs File Storage

Object StorageBlock StorageFile Storage
Access patternHTTP/REST API, whole-object read/writeRaw disk, random-access reads/writesNetwork filesystem (NFS/SMB), POSIX-like
Attach modelUnlimited concurrent clients over HTTPSingle VM (or a small cluster with specialized setups)Many VMs concurrently
Best forUnstructured data, backups, static assets, data lakesDatabase files, boot volumes, low-latency random I/OShared config, home directories, multi-writer document stores
Typical costLowest per-GBHigher, provisioned IOPS add costModerate to high, priced for concurrent access
MutabilityImmutable — replace the whole objectIn-place random writesIn-place writes, POSIX file semantics

Common Pitfalls

  • Using expensive, single-attach Block Storage for static user-uploaded media instead of cheap, natively-scalable Object Storage — this both costs more and doesn’t scale past one instance
  • Forgetting that object storage is not a general-purpose filesystem: no partial in-place writes, no native file locking, and (for many providers) eventual consistency on overwrites/deletes of existing keys, which breaks assumptions ported over from local disk code
  • Leaving storage buckets/containers with public read access misconfigured, one of the most common real-world causes of large-scale data leaks (unencrypted S3 buckets exposing customer PII)
  • Not setting lifecycle policies to transition old objects to cheaper cold-storage tiers, quietly paying Standard-tier prices for rarely-accessed archival data for years
  • Ignoring egress costs — moving data out of cloud object storage (to another cloud, or to users at scale) is often priced far higher than storage itself, and can dominate a bill that looked cheap based on storage cost alone
  • Skipping versioning on buckets holding important data, so an accidental overwrite or delete (application bug, compromised credential) has no recovery path

Code Example

import boto3

s3 = boto3.client("s3")

# Upload an object with server-side encryption and a storage class
s3.put_object(
    Bucket="user-uploads",
    Key="avatars/user-42.jpg",
    Body=image_bytes,
    ContentType="image/jpeg",
    ServerSideEncryption="AES256",
    StorageClass="STANDARD",
)

# Generate a presigned URL so the client can upload directly to S3,
# bypassing the application server entirely for the file transfer itself
upload_url = s3.generate_presigned_url(
    "put_object",
    Params={"Bucket": "user-uploads", "Key": "avatars/user-42.jpg"},
    ExpiresIn=300,  # 5 minutes to use the URL
)

Code Example: Lifecycle Policy to Cut Cold-Storage Cost

{
  "Rules": [
    {
      "ID": "archive-old-logs",
      "Status": "Enabled",
      "Filter": { "Prefix": "logs/" },
      "Transitions": [
        { "Days": 30, "StorageClass": "STANDARD_IA" },
        { "Days": 90, "StorageClass": "GLACIER" }
      ],
      "Expiration": { "Days": 365 }
    }
  ]
}

Applied once to a bucket, this automatically moves 30-day-old logs to Infrequent Access, 90-day-old logs to Glacier, and deletes anything past a year — no manual intervention or scheduled job required.

Best Practices

  • Match the storage type to the access pattern: object storage for unstructured/immutable data, block storage for a single VM’s low-latency random I/O, file storage only when true concurrent POSIX access is required
  • Use presigned URLs for large uploads/downloads instead of proxying file bytes through your own application server
  • Set lifecycle policies on every bucket holding data with a known retention curve, moving cold data to cheaper tiers automatically rather than manually
  • Enable versioning and block public access by default on any bucket holding non-public data, and audit bucket policies regularly rather than only at creation time
  • Encrypt at rest by default and budget for egress costs explicitly when architecting any cross-cloud or high-download-volume workload

FAQ

Can I use S3 as a database? Not as a primary transactional store — it has no query language, no indexes beyond the key, and (for some operations) eventual consistency, but it’s commonly used alongside a real database as a data lake or for storing large blobs referenced by a database row.

Should a database’s data files sit on block or object storage? Block storage — databases need low-latency random reads/writes to a single attached volume, which is exactly what block storage provides and object storage’s HTTP-API, whole-object model can’t deliver.

What does “eventual consistency” mean for object storage in practice? It means that after an overwrite or delete of an existing key, a read immediately afterward might still return the old version for a short window on some providers — S3 has offered strong read-after-write consistency for all operations since December 2020, but it’s still worth checking a given provider’s documented guarantee before relying on it.

History

  • AWS S3 launched in 2006, one of AWS’s first public services, and effectively created “object storage” as its own cloud category built around a simple HTTP API rather than filesystem semantics
  • AWS EBS followed in 2008, giving EC2 instances persistent block storage that survived instance termination, since the original EC2 instance store was ephemeral
  • Managed file storage arrived later — EFS launched in 2015 — filling the gap for workloads that genuinely needed concurrent POSIX-style access across many instances at once
  • S3-compatible APIs became a de facto standard beyond AWS itself, with MinIO, Cloudflare R2, and Backblaze B2 all implementing the same request signing and object API, letting tooling built for S3 largely work unmodified against them

Example

An app stores user profile pictures in S3 object storage served through a CDN, keeps its Postgres database’s data files on an EBS block volume for low-latency random I/O, and mounts an EFS file share so multiple worker VMs can read/write a shared set of uploaded documents concurrently — three different storage types in one architecture, each chosen for the access pattern it’s actually good at rather than defaulting to one for everything.

Dig deeper