01

Requirements & scope

Problem Statement & Requirements

Design a distributed object storage system that stores and retrieves arbitrary binary objects (files) of any size with high durability, availability, and scalability — similar to Amazon S3, Google Cloud Storage, or Azure Blob Storage.

Functional Requirements

  • PUT object — upload objects from bytes to 5 TB in size
  • GET object — download objects by key; support range reads (byte-range fetches)
  • DELETE object — remove an object permanently
  • LIST objects — list objects in a bucket with prefix filtering and pagination
  • Multipart upload — upload large objects in parallel parts, then assemble
  • Buckets — logical namespace for objects (globally unique name)
  • Metadata — per-object user-defined metadata (key-value headers) and system metadata (size, content-type, etag, created_at)
  • Versioning — optional per-bucket; retain all historical versions of an object
  • Presigned URLs — generate time-limited, signed URLs for unauthenticated upload/download
  • Lifecycle policies — auto-transition objects between storage classes or auto-delete after N days
  • Storage classes — Standard (hot), Infrequent Access, Archive (cold)

Non-Functional Requirements

  • Durability — 99.999999999% (11 nines) — lose < 1 object per 10 billion stored per year
  • Availability — 99.99% (< 53 min downtime/year)
  • Scalability — store exabytes of data, trillions of objects
  • Throughput — handle millions of requests/second globally
  • Consistency — strong read-after-write consistency (S3 achieved this in 2020)
  • No size limit — individual objects up to 5 TB; buckets unlimited

Out of Scope

  • POSIX file system semantics (no rename, no directories, no locks)
  • Real-time streaming (this is blob storage, not a streaming platform)
  • Block storage (EBS) or file storage (EFS/NFS)
02

Scale estimations

Scale Estimations

Traffic

MetricValue
PUT requests / second500K RPS
GET requests / second5M RPS (10:1 read-write ratio)
DELETE requests / second50K RPS
LIST requests / second200K RPS
Peak GET / second15M RPS
Total objects stored100 trillion
Total buckets500 million

Storage

MetricValue
Average object size1 MB (heavily skewed: many small, few huge)
Median object size64 KB
Max object size5 TB
Total data stored100 trillion × 1 MB avg = ~100 EB (exabytes)
New data / day500K/s × 1 MB × 86,400 = ~43 PB/day
Replication factor3 (across AZs)
Raw storage needed100 EB × 3 = ~300 EB
Metadata per object~500 bytes (key, size, etag, timestamps, ACL, version)
Total metadata100T × 500 B = ~50 PB of metadata

Bandwidth

MetricValue
GET bandwidth (5M × 1 MB avg)~5 TB/s
PUT bandwidth (500K × 1 MB avg)~500 GB/s
Cross-AZ replication~1 TB/s
Total egress~6.5 TB/s

Hardware Estimate

ComponentSpec
Storage nodes100,000+ (with 100 TB usable each)
Disk per node12× 20 TB HDD (capacity optimized) + 2× 2 TB NVMe (metadata/journal)
RAM per node64-128 GB (metadata caching, I/O buffers)
Network per node25 Gbps NIC
Metadata nodes5,000+ (SSD-backed, high-IOPS)
03

Layered architecture

Phase 1 of 4 · Client & edge

Start with the entry points

First, define who uses the system and where requests enter. This is the boundary of the design, not yet a complete request path.

Client SDK
Support multipart transfers so large uploads can resume after network failures.
API gateway
Validate identity and bucket policy before granting access to object data.
Architecture after phase 1. Includes Client SDK, API gateway. Client SDK connects to API gateway.
Phase 1 — Start with Client SDK, API gateway.SVGSVGEditable diagramEditable diagram

This first layer defines the system boundary. The next chapters add application logic and data dependencies. After examining the request paths and tradeoffs, we will add operational safeguards and bring the complete architecture together.

04

API & contracts

Phase 2 of 4 · Application & compute

Add the application logic

Keep the entry points and add the services that do the work. Give each service a clear responsibility before defining its contracts. Storage and background dependencies come next.

Metadata service
Resolve object names, versions, checksums, and the placement of their chunks.
Data service
Move bytes without sending the entire payload through the metadata service.
Architecture after phase 2. Includes Client SDK, API gateway, Metadata service, Data service. Client SDK connects to API gateway. API gateway connects to Metadata service. API gateway connects to Data service.
Phase 2 — Added Metadata service, Data service. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

API Design

PUT Object

EXAMPLE
PUT /{bucket}/{key} HTTP/1.1
Host: s3.region.example.com
Content-Type: image/jpeg
Content-Length: 3145728
x-amz-meta-camera: "iPhone 15"          # user metadata
x-amz-storage-class: STANDARD
Authorization: AWS4-HMAC-SHA256 Credential=.../s3/aws4_request, ...

<3 MB binary body>

Response (200 OK):
ETag: "d41d8cd98f00b204e9800998ecf8427e"
x-amz-version-id: "v3nR2ql9sa8GhZ1"     # if versioning enabled

GET Object

EXAMPLE
GET /{bucket}/{key} HTTP/1.1
Host: s3.region.example.com
Range: bytes=0-1048575                    # optional: first 1 MB only
If-None-Match: "d41d8cd98f00b204e9800998ecf8427e"   # conditional

Response (200 OK / 206 Partial / 304 Not Modified):
Content-Type: image/jpeg
Content-Length: 3145728
ETag: "d41d8cd98f00b204e9800998ecf8427e"
Last-Modified: Thu, 03 Apr 2026 10:00:00 GMT
x-amz-meta-camera: "iPhone 15"

<binary body>

Multipart Upload (Large Objects)

EXAMPLE
Step 1: Initiate
POST /{bucket}/{key}?uploads HTTP/1.1

Response: { "upload_id": "upl-abc123" }

Step 2: Upload Parts (parallel)
PUT /{bucket}/{key}?partNumber=1&uploadId=upl-abc123
<part 1 body: 100 MB>  → ETag: "etag1"

PUT /{bucket}/{key}?partNumber=2&uploadId=upl-abc123
<part 2 body: 100 MB>  → ETag: "etag2"

... (up to 10,000 parts, 5 MB - 5 GB each)

Step 3: Complete
POST /{bucket}/{key}?uploadId=upl-abc123
{
  "parts": [
    {"part_number": 1, "etag": "etag1"},
    {"part_number": 2, "etag": "etag2"}
  ]
}

Response (200 OK):
ETag: "composite-etag-abc"

LIST Objects

EXAMPLE
GET /{bucket}?prefix=photos/2026/&delimiter=/&max-keys=1000&continuation-token=...

Response (200 OK):
{
  "name": "my-bucket",
  "prefix": "photos/2026/",
  "delimiter": "/",
  "max_keys": 1000,
  "is_truncated": true,
  "continuation_token": "next-page-token",
  "common_prefixes": ["photos/2026/01/", "photos/2026/02/"],   # "folders"
  "contents": [
    {
      "key": "photos/2026/cover.jpg",
      "size": 3145728,
      "etag": "d41d8cd98f00...",
      "last_modified": "2026-04-03T10:00:00Z",
      "storage_class": "STANDARD"
    }
  ]
}

Presigned URL

EXAMPLE
# Server-side generation (SDK)
url = s3.generate_presigned_url(
    method="GET",
    bucket="my-bucket",
    key="photos/cat.jpg",
    expires_in=3600   # 1 hour
)
# → https://s3.region.example.com/my-bucket/photos/cat.jpg
#     ?X-Amz-Expires=3600
#     &X-Amz-Signature=a1b2c3...
#     &X-Amz-Credential=...

# Client uses URL directly — no SDK or credentials needed
GET https://s3.region.example.com/my-bucket/photos/cat.jpg?X-Amz-Expires=...
→ 200 OK + binary data (if signature valid and not expired)
05

Data model

Phase 3 of 4 · Data & async work

Connect data and background work

The services now need their dependencies: durable state, fast lookups, or work that continues outside a request. Separate these responsibilities rather than treating every component as a database.

Metadata store
Store object metadata with consistency appropriate for listing and overwrite semantics.
Storage nodes
Spread data and parity across independent failure domains; validate chunks with checksums.
Repair workers
Detect corruption and rebuild lost fragments before additional failures exhaust redundancy.
Architecture after phase 3. Includes Client SDK, API gateway, Metadata service, Data service, Metadata store, Storage nodes, Repair workers. Client SDK connects to API gateway. API gateway connects to Metadata service. API gateway connects to Data service. Metadata service connects to Metadata store. Data service connects to Storage nodes. Storage nodes connects to Repair workers.
Phase 3 — Added Metadata store, Storage nodes, Repair workers. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Data Model

Object Metadata Schema

Object Metadata Schema
Object Metadata SchemaSVGSVGEditable diagramEditable diagram

Data Chunk Layout

Data Chunk Layout
Data Chunk LayoutSVGSVGEditable diagramEditable diagram

Bucket Metadata

Bucket Metadata
Bucket MetadataSVGSVGEditable diagramEditable diagram
06

Core design decisions

Core Design Decisions

Decision 1: Data Durability — How to Achieve 11 Nines

This is THE most important decision. Losing even one customer object is unacceptable.

Option A: Replication (3 copies across AZs)
Option A: Replication (3 copies across AZs)
Option A: Replication (3 copies across AZs)SVGSVGEditable diagramEditable diagram
ProsCons
Simple to implement3× storage cost
Fast reads (any replica can serve)200% overhead for durability
Fast writes (parallel replication)Expensive at exabyte scale
Simple repair (copy from surviving replica)
Option B: Erasure Coding (Reed-Solomon)
Option B: Erasure Coding (Reed-Solomon)
Option B: Erasure Coding (Reed-Solomon)SVGSVGEditable diagramEditable diagram
ProsCons
1.4× overhead (vs 3× for replication)Higher CPU for encode/decode
Higher durability (more redundancy per byte)Slower reads (must read from 10 nodes minimum)
Massive cost savings at scaleRepair requires reading 10 chunks to reconstruct 1
Mathematically provable durabilityComplex implementation
Recommendation: Hybrid Approach
Recommendation: Hybrid Approach
Recommendation: Hybrid ApproachSVGSVGEditable diagramEditable diagram

Decision 2: Metadata Store — The Brain of the System

Decision 2: Metadata Store — The Brain of the System
Decision 2: Metadata Store — The Brain of the SystemSVGSVGEditable diagramEditable diagram

Decision 3: Consistency Model

Decision 3: Consistency Model
Decision 3: Consistency ModelSVGSVGEditable diagramEditable diagram
07

Request flows

Detailed Flow Diagrams

PUT Object Flow

PUT Object Flow
PUT Object FlowWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

GET Object Flow

GET Object Flow
GET Object FlowWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Multipart Upload Flow

Multipart Upload Flow
Multipart Upload FlowWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram
08

Performance & caching

Garbage Collection & Compaction

The Problem

The Problem
The ProblemSVGSVGEditable diagramEditable diagram

GC Architecture

GC Architecture
GC ArchitectureSVGSVGEditable diagramEditable diagram

Aggregate File Compaction

Aggregate File Compaction
Aggregate File CompactionSVGSVGEditable diagramEditable diagram
09

Advanced design

Storage Node Internals

Disk Layout

Disk Layout
Disk LayoutSVGSVGEditable diagramEditable diagram

Data Integrity — Checksums Everywhere

Data Integrity — Checksums Everywhere
Data Integrity — Checksums EverywhereSVGSVGEditable diagramEditable diagram

Storage Classes & Lifecycle

Storage Classes & Lifecycle
Storage Classes & LifecycleSVGSVGEditable diagramEditable diagram
10

Edge cases

Handling Edge Cases

Concurrent PUT to Same Key

EXAMPLE
Problem: Two clients PUT the same key simultaneously

  Client A: PUT bucket/key val=A → writes to nodes, commits metadata
  Client B: PUT bucket/key val=B → writes to nodes, commits metadata

Resolution:
  • Metadata store uses optimistic concurrency (version vector or CAS)
  • "Last writer wins" based on metadata commit timestamp
  • Both data payloads are written; GC later cleans up the loser's chunks
  • With versioning enabled: both versions are preserved

  The client that receives 200 OK last is the "winner"
  All subsequent GETs return the winner's value
  Guaranteed by strong consistency of metadata store

Partial Failure During PUT

EXAMPLE
Problem: Data written to 2 of 3 nodes, then gateway crashes

Scenario 1: Metadata NOT yet committed
  → Object doesn't exist (metadata is source of truth)
  → Orphaned chunks on 2 nodes → GC cleans up in 72h
  → Client gets timeout → retries → new PUT succeeds

Scenario 2: Metadata committed (quorum write succeeded)
  → Object exists with 2 of 3 replicas
  → Repair service detects under-replicated chunks
  → Copies from surviving replica to new 3rd node
  → Full durability restored within minutes

Key insight: Metadata commit is the "point of no return"
  Before commit: object doesn't exist, chunks are garbage
  After commit: object exists, repair ensures full replication

Hot Bucket / Hot Key

EXAMPLE
Problem: One bucket gets 1M+ requests/sec (e.g., viral content, popular API)

Metadata hotspot:
  All requests for bucket X hit the same metadata partition
  → Add request-level caching for metadata (short TTL, 1-5s)
  → Partition metadata by hash(bucket + key) not just bucket

Data hotspot:
  One object read 100K times/sec
  → CDN / caching layer in front of object storage
  → Read from any of 3 replicas (load balance)
  → For extreme cases: create temporary read-only replicas

S3 solved this with automatic partition splitting:
  Bucket with increasing key prefix "2026/04/03/photo-001"
  → Auto-splits partition at "2026/04/03/photo-500"
  → Each partition served by different nodes

LIST Performance at Scale

EXAMPLE
Problem: Bucket with 10 billion objects, LIST with prefix scan

  Naive approach: scan all 10B keys → timeout

  Solution: Metadata index designed for prefix scans
  
  Key layout in metadata store:
    bucket=my-photos key=photos/2026/01/01/img001.jpg
    bucket=my-photos key=photos/2026/01/01/img002.jpg
    bucket=my-photos key=photos/2026/01/02/img001.jpg
    ...

  LIST ?prefix=photos/2026/01/01/
  → Range scan from "photos/2026/01/01/" to "photos/2026/01/01/\xff"
  → Returns first 1000 keys + continuation token
  → Continuation token = last key seen (for next page)

  With ordered KV store: range scan is O(log N + K) where K = results
  → Fast regardless of total bucket size

Cross-Region Replication

Cross-Region Replication
Cross-Region ReplicationSVGSVGEditable diagramEditable diagram
11

Tradeoffs

Tradeoffs & Design Decisions Summary

DecisionOption AOption BChosenWhy
Durability3× replicationErasure coding RS(10,4)HybridReplication for hot (fast access); EC for warm/cold (cost savings)
ConsistencyEventualStrong read-after-writeStrongEliminates entire class of application bugs; 1-2 ms cost is acceptable
Metadata storeSharded MySQLOrdered KV (FoundationDB)Ordered KVPrefix scans for LIST; strong consistency via Raft; horizontal scaling
Small object storageIndividual filesAggregate filesAggregateAvoids inode/allocation waste; 4× better disk utilization for small objects
Large object chunkingVariable sizeFixed 64 MB chunksFixedSimpler placement, predictable I/O, easier parallelism
Write quorumAll 3 replicas2 of 3 (majority)2 of 3Tolerates 1 slow/failed node; repair brings 3rd up async
Object versioningAlways onOpt-in per bucketOpt-inSaves storage for buckets that don't need history
Multipart assemblyPhysical copy/concatMetadata-only (logical)Metadata-onlyZero copy overhead; parts stay in place; metadata records order
GC strategyImmediate deleteMark-sweep with grace periodMark-sweep (72h)Grace period prevents race condition data loss during in-flight PUTs
EncryptionClient-side onlyServer-side (SSE)Both optionsSSE (default, transparent); client-side for maximum security
12

Reliability & fault tolerance

Phase 4 of 4 · Operations & resilience

Add operational safeguards

With the request paths and tradeoffs established, add the controls that help detect trouble and keep the system operating. The final diagram retains every layer introduced so far.

Lifecycle control
Move older objects across storage classes and safely remove unreferenced chunks.
Architecture after phase 4. Includes Client SDK, API gateway, Metadata service, Data service, Metadata store, Storage nodes, Repair workers, Lifecycle control. Client SDK connects to API gateway. API gateway connects to Metadata service. API gateway connects to Data service. Metadata service connects to Metadata store. Data service connects to Storage nodes. Storage nodes connects to Repair workers. Metadata store connects to Lifecycle control.
Phase 4 — Added Lifecycle control. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Reliability & Fault Tolerance

Single Points of Failure & Mitigations

Single Points of Failure & Mitigations
Single Points of Failure & MitigationsSVGSVGEditable diagramEditable diagram

Durability Math (11 Nines in Detail)

Durability Math (11 Nines in Detail)
Durability Math (11 Nines in Detail)SVGSVGEditable diagramEditable diagram

Graceful Degradation

EXAMPLE
Level 1 (single disk failure):
  → Chunks on that disk read from other 2 replicas. Zero impact.
  → Repair creates new 3rd replica within 1-4 hours.

Level 2 (single node failure):
  → All chunks on that node served from replicas.
  → Repair redistributes replacement replicas across cluster.
  → Brief increase in cross-AZ traffic.

Level 3 (full AZ outage):
  → 1/3 of primary replicas unavailable.
  → Remaining 2 AZs serve all reads (2 of 3 replicas survive).
  → Writes: quorum (2 of 3) still possible across remaining AZs.
  → Metadata: Raft continues with majority of nodes.

Level 4 (metadata store degradation):
  → PUTs and DELETEs may fail (need metadata write).
  → GETs for recently accessed objects served from gateway cache.
  → LIST requests fail until metadata recovers.
  → Data is safe on storage nodes regardless.

Level 5 (region outage):
  → Failover to DR region (if cross-region replication enabled).
  → RPO: seconds to minutes (replication lag).
  → RTO: minutes (DNS update + metadata sync verification).
13

Production architecture

High-Level Architecture

High-Level Architecture
High-Level ArchitectureSVGSVGEditable diagramEditable diagram

Component Breakdown

Component Breakdown
Component BreakdownSVGSVGEditable diagramEditable diagram

Full System Architecture (Production-Grade)

Full System Architecture (Production-Grade)
Full System Architecture (Production-Grade)Wide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram
14

Further exploration

Compare storing a million small objects with storing a few enormous ones. The first workload stresses metadata and per-request overhead; the second stresses transfer throughput, multipart recovery, and repair bandwidth. Use those differences to explain why one redundancy and placement policy may not suit every storage tier.

15

Interview playbook

Interview Tips

  1. Start with the separation of metadata and data — This is the foundational insight. Metadata (where is this object?) is small, needs strong consistency, and lives on SSD. Data (the actual bytes) is large, needs durability, and lives on HDD. They have completely different requirements.

  2. Explain 11 nines of durability concretely — Don't just say "replicate 3 times." Walk through the math: disk failure rate, repair window, probability of correlated failure. Show that fast repair (MTTR) matters more than replica count.

  3. Erasure coding vs replication is the #1 tradeoff — At exabyte scale, 3× replication costs 2× more than erasure coding. Explain RS(10,4): 40% overhead instead of 200%, tolerates any 4 chunk failures. Use replication for hot data (speed), EC for cold (cost).

  4. Multipart upload shows depth — Explain that parts are NOT physically concatenated. Metadata records the ordered list of part locations. GET streams parts in sequence. Zero-copy assembly. This is a non-obvious insight that impresses interviewers.

  5. Address the small object problem — Millions of 1 KB files on disk = inode exhaustion + wasted allocation blocks. Solution: pack into aggregate files (256 MB). Index maps (object_id → file + offset + length). This shows practical systems knowledge.

  6. GC with grace period prevents data loss — Explain the race condition: in-flight PUT writes chunks before metadata commits. If GC deletes "unreferenced" chunks during this window, data is lost. 72-hour grace period eliminates this risk.

  7. Strong consistency is achievable — S3 switched to strong read-after-write in 2020. Explain how: quorum reads/writes on metadata store, linearizable operations via Raft, and why this eliminates an entire class of application bugs.

  8. LIST performance at scale — Naive LIST on a billion-object bucket is a disaster. Explain ordered key-value store with range scans: O(log N + K) where K is result count. Prefix filtering is just a range scan with bounds.

  9. Mention storage classes and lifecycle — Shows you understand the economics. Standard (3× replication, fast) → IA (EC, cheaper) → Glacier (EC, cheapest, slow retrieval). Lifecycle engine transitions automatically.

  10. End with operational concerns — Background scrubbing (detect bit rot before it's needed), repair prioritization (most-at-risk chunks first), capacity planning (predict when to add nodes), and the importance of cross-AZ and cross-region placement for correlated failure resistance.