AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content
SD notes

System Design · Note 19 of 22

Design a File Storage Service (Dropbox)

Intermediate · 8 min read · updated 14 November 2026

Quick answer

How do you design a file storage service like Dropbox?

Split each file into chunks of a few megabytes and name every chunk by its content hash. Store the chunks in a blob store and each file's ordered list of hashes, with a version number, in a metadata database. A client uploads only chunks the server lacks, commits the new list, and a notification service tells the user's other devices to pull the changed chunks.

On this page
  1. Requirements
  2. Capacity estimate
  3. API
  4. Data model
  5. High-level design
  6. Chunking and dedup
  7. Metadata vs blob store
  8. Delta sync and notifications
  9. Conflicts
  10. Bottlenecks and trade-offs
  11. Interview questions
  12. Common questions

Store files in the cloud and keep them in sync on every device: the design turns on chunks, hashes and a clean split between bytes and metadata.

Requirements

  • Functional: upload, download and delete files; sync changes to every device of an account; share files and folders; keep earlier versions.
  • Non-functional: never lose data (high durability); large files and flaky networks must work; sync in seconds; consistent view of each file's current version.
  • Read and write both matter: unlike a URL shortener, uploads are large and frequent.

Capacity estimate

Assumptions, not facts — say them out loud and let the interviewer change them.

AssumptionValueDerivedValue
Registered users50 millionStored data (avg 2 GB)100 PB
Daily active users10 millionUploads per second20M ÷ 86,400 ≈ 230/s
Uploads per user a day2New data a day (1 MB each)20 TB
Chunk size4 MBChunk rows to index100 PB ÷ 4 MB = 25 billion

API

HTTP
Copy
POST /files/upload-session          {"path": "/trip.mov", "hashes": [...]}  → 200 {"missing": ["9ae3", ...]}
PUT  /chunks/9ae3                   (raw bytes)                             → 201
POST /files/42/commit               {"baseVersion": 3, "hashes": [...]}     → 200 {"version": 4} | 409
GET  /changes?cursor=c81            (long poll)                             → 200 {"changed": [42], "cursor": "c82"}

Data model

TableKey columnsLives in
filesfile_id, owner_id, path, current_versionMetadata DB
versionsfile_id, version, chunk_hashes (ordered), size, created_atMetadata DB
chunkshash, size, ref_countMetadata DB
chunk byteshash → bytesBlob store (S3-like)

High-level design

  • Clients watch a sync folder, chunk and hash changed files, and talk to two paths.
  • Block service in front of the blob store: chunk uploads and downloads, often through a CDN for reads.
  • Metadata service in front of the metadata DB: files, versions, sharing, the commit.
  • Notification service: devices hold a long poll or WebSocket; change events reach it through a queue.

Chunking and dedup

  • Cut files into chunks (a few MB); name each by its content hash (SHA-256).
  • Ask the server which hashes it lacks; upload only those.
  • Identical chunks — inside a file, across versions, across users — are stored once.
  • Uploads become resumable and parallel: one chunk is the unit of retry.
Chunking and dedup: hash every chunk, upload only the new ones

The client cuts the file into fixed-size chunks — 4 characters here, about 4 MB in a real service — and fingerprints each one before sending anything.

0:01 / 0:25Step 1 of 8
Transcript · 8 steps

Metadata vs blob store

  • Bytes go to a blob store: cheap, durable (replicated or erasure-coded), no queries.
  • Metadata goes to a transactional DB: small rows, indexes, one atomic commit per version.
  • Shard metadata by owner or namespace id so one user's folder lives together — see database sharding.
  • A commit names only hashes already uploaded, so readers never see a half-written file.

Delta sync and notifications

  • On an edit, re-chunk, compare hash lists and upload only the delta.
  • Fixed-size chunks shift after an insertion; content-defined chunking (rolling hash) keeps the rest stable.
  • The commit publishes a change; other devices are pushed a notice, then pull the chunk list and missing chunks.
Delta sync: re-chunk, compare hash lists, upload only what changed

The metadata database holds file 42 as version 1: an ordered list of 6 chunk hashes. The bytes themselves live in the block store, keyed by hash.

0:01 / 0:16Step 1 of 5
Transcript · 5 steps
Sync: upload chunks, commit metadata, notify, then pull the delta

Two devices, one account. Follow how an edit on the laptop reaches the phone: three services, six messages, and no full-file transfer anywhere.

0:01 / 0:21Step 1 of 7
Transcript · 7 steps

Conflicts

  • Every commit carries its base version; the server applies it only if base = current (compare-and-set).
  • A refused device saves a conflicted copy and downloads the winner; the user merges.
  • Automatic merging works only for formats the service understands; for arbitrary files, keep both.
Conflicts: accept a commit only on the current version

Both devices hold version 3 of report.docx. Each commit tells the server which version the edit was made on, its base version.

0:01 / 0:16Step 1 of 5
Transcript · 5 steps

Bottlenecks and trade-offs

  • Chunk size: small chunks dedup better and retry cheaper but mean more metadata rows and requests.
  • Hot metadata shards: a huge shared folder concentrates writes; split large namespaces.
  • Garbage collection: delete a chunk only when no version references it (ref_count, or a mark-and-sweep).
  • Cross-user dedup saves space but can reveal that someone already stored a file; per-account dedup avoids that.

Interview questions

  • Why not store whole files? Edits would re-upload everything, failed uploads restart, and duplicates are stored again.
  • Why keep metadata out of the blob store? It needs transactions, indexes and listing; blob stores give durability, not queries.
  • How does a device learn about changes? A long poll or WebSocket to a notification service, then a pull of the delta.
  • Two devices edit offline — what happens? The first commit wins; the second fails its version check and becomes a conflicted copy.

Next: Design a Ride-Hailing App (Uber) · Before this: Design a Notification System

Common questions

Why split files into chunks?

Chunks make large files practical. An interrupted upload resumes from the last chunk, an edit re-sends only the chunks it touched, and identical chunks across files and users are stored once. Each chunk is small enough to retry cheaply and to upload in parallel.

What is content-addressable storage?

Each chunk is stored under the hash of its own bytes, usually SHA-256. The same content always gets the same name, so storing it twice is harmless and duplicates are found by a lookup. A file then becomes an ordered list of hashes.

Where are file contents and metadata stored?

Contents go to a blob store such as Amazon S3, which is cheap, durable and scales without limit but cannot query. Metadata — users, folders, file names, versions and chunk lists — goes to a database that supports transactions and indexes, such as a sharded relational database.

How does a file sync across devices?

After a device commits a new version, the metadata service publishes a change event. Other devices hold a long poll or WebSocket open to a notification service, hear about the change, fetch the new chunk list and download only the chunks they lack.

How are edit conflicts handled?

Every commit carries the version it was based on, and the server accepts it only if that is still the current version. A device whose commit is refused keeps its edit as a conflicted copy beside the new version, so nothing is lost and the user merges them.

What is the difference between fixed-size and content-defined chunking?

Fixed-size chunking cuts every N bytes, so inserting one byte near the start shifts every later chunk and they all look new. Content-defined chunking picks cut points from the data itself with a rolling hash, so an insertion changes only the chunks around it.

Design Dropbox: File Storage System Design

Design a file storage and sync service like Dropbox: chunking, content hashing and dedup, metadata vs blob storage, delta sync, notifications and conflicts.

  • Subject: System Design
  • Level: Intermediate
  • Reading time: 8 min
  • Updated: 2026-11-14

How do you design a file storage service like Dropbox?

Split each file into chunks of a few megabytes and name every chunk by its content hash. Store the chunks in a blob store and each file's ordered list of hashes, with a version number, in a metadata database. A client uploads only chunks the server lacks, commits the new list, and a notification service tells the user's other devices to pull the changed chunks.

Store files in the cloud and keep them in sync on every device: the design turns on chunks, hashes and a clean split between bytes and metadata.

Requirements

  • Functional: upload, download and delete files; sync changes to every device of an account; share files and folders; keep earlier versions.
  • Non-functional: never lose data (high durability); large files and flaky networks must work; sync in seconds; consistent view of each file's current version.
  • Read and write both matter: unlike a URL shortener, uploads are large and frequent.

Capacity estimate

Assumptions, not facts — say them out loud and let the interviewer change them.

AssumptionValueDerivedValue
Registered users50 millionStored data (avg 2 GB)100 PB
Daily active users10 millionUploads per second20M ÷ 86,400 ≈ 230/s
Uploads per user a day2New data a day (1 MB each)20 TB
Chunk size4 MBChunk rows to index100 PB ÷ 4 MB = 25 billion

API

POST /files/upload-session          {"path": "/trip.mov", "hashes": [...]}  → 200 {"missing": ["9ae3", ...]}
PUT  /chunks/9ae3                   (raw bytes)                             → 201
POST /files/42/commit               {"baseVersion": 3, "hashes": [...]}     → 200 {"version": 4} | 409
GET  /changes?cursor=c81            (long poll)                             → 200 {"changed": [42], "cursor": "c82"}

Data model

TableKey columnsLives in
filesfile_id, owner_id, path, current_versionMetadata DB
versionsfile_id, version, chunk_hashes (ordered), size, created_atMetadata DB
chunkshash, size, ref_countMetadata DB
chunk byteshash → bytesBlob store (S3-like)

High-level design

  • Clients watch a sync folder, chunk and hash changed files, and talk to two paths.
  • Block service in front of the blob store: chunk uploads and downloads, often through a CDN for reads.
  • Metadata service in front of the metadata DB: files, versions, sharing, the commit.
  • Notification service: devices hold a long poll or WebSocket; change events reach it through a queue.

Chunking and dedup

  • Cut files into chunks (a few MB); name each by its content hash (SHA-256).
  • Ask the server which hashes it lacks; upload only those.
  • Identical chunks — inside a file, across versions, across users — are stored once.
  • Uploads become resumable and parallel: one chunk is the unit of retry.
trip.mov, in fixed-size chunksblock storeKq7zf6c6sendR2mx9ae3sendKq7zf6c6skippW4e0e47sendR2mx9ae3skipJ8sdc6d6sendf6c69ae30e47c6d66 chunks · 4 sent · 2 skipped
Chunking and dedup: hash every chunk, upload only the new ones.
  1. The client cuts the file into fixed-size chunks — 4 characters here, about 4 MB in a real service — and fingerprints each one before sending anything.
  2. Chunk 1 hashes to f6c6. The block store has never seen it, so the client uploads the chunk, stored under its hash.
  3. Chunk 2 hashes to 9ae3, a hash the store lacks, so this chunk is uploaded too. The store now holds 2 distinct chunks.
  4. Chunk 3 hashes to f6c6, which the block store already holds. Nothing is uploaded: the file's chunk list simply names f6c6 again.
  5. Chunk 4 hashes to 0e47, a hash the store lacks, so this chunk is uploaded too. The store now holds 3 distinct chunks.
  6. Chunk 5 hashes to 9ae3, which the block store already holds. Nothing is uploaded: the file's chunk list simply names 9ae3 again.
  7. Chunk 6 hashes to c6d6, a hash the store lacks, so this chunk is uploaded too. The store now holds 4 distinct chunks.
  8. 4 of 6 chunks crossed the network. The same check works across files and users: identical content is stored once. Here the hash is a toy 16-bit one; real systems use SHA-256.

Metadata vs blob store

  • Bytes go to a blob store: cheap, durable (replicated or erasure-coded), no queries.
  • Metadata goes to a transactional DB: small rows, indexes, one atomic commit per version.
  • Shard metadata by owner or namespace id so one user's folder lives together — see database sharding.
  • A commit names only hashes already uploaded, so readers never see a half-written file.

Delta sync and notifications

  • On an edit, re-chunk, compare hash lists and upload only the delta.
  • Fixed-size chunks shift after an insertion; content-defined chunking (rolling hash) keeps the rest stable.
  • The commit publishes a change; other devices are pushed a notice, then pull the chunk list and missing chunks.
v1 on serverf6c69ae3f6c60e479ae3c6d6v2 on laptopf6c6have9ae3havef6c6have0755new9ae3havec6d6have68e3newBlock store+2 chunksMetadata DBfile 42 → v2, 7 hashesLaptop
Delta sync: re-chunk, compare hash lists, upload only what changed.
  1. The metadata database holds file 42 as version 1: an ordered list of 6 chunk hashes. The bytes themselves live in the block store, keyed by hash.
  2. The user edits the file. The laptop re-chunks it and hashes again: 7 chunks now, because the edit changed some bytes and added more at the end.
  3. It compares the new list with the old one. 5 of the 7 hashes are already on the server; only 0755 and 68e3 are new.
  4. The laptop uploads just those 2 chunks — the delta — instead of the whole file. Sending them again would be harmless: a chunk keyed by its hash is stored once.
  5. Last, it commits version 2's hash list to the metadata database in one write. Until that commit, other devices still see version 1, never a half-uploaded file.
LaptopBlocksMetadataNotifyPhonePUT 0755, 68e3commit v2 (7 hashes)file 42 changedv2 readyGET file 42GET 0755, 68e3
Sync: upload chunks, commit metadata, notify, then pull the delta.
  1. Two devices, one account. Follow how an edit on the laptop reaches the phone: three services, six messages, and no full-file transfer anywhere.
  2. The laptop uploads the 2 new chunks to the block store first, so every hash the next commit names already exists.
  3. Then it commits version 2's chunk list to the metadata service, the one source of truth for which version is current.
  4. The metadata service publishes a change event. A queue between them keeps the commit fast even when many devices must hear about it.
  5. The phone holds an open connection (a long poll or WebSocket) to the notification service, so it hears about the change at once instead of polling every few seconds.
  6. The phone asks for version 2's chunk list and compares it with the hashes it already has on disk, exactly as the laptop did.
  7. It downloads only 0755 and 68e3, the chunks it lacks, and rebuilds the file locally. Both devices now hold version 2.

Conflicts

  • Every commit carries its base version; the server applies it only if base = current (compare-and-set).
  • A refused device saves a conflicted copy and downloads the winner; the user merges.
  • Automatic merging works only for formats the service understands; for arbitrary files, keep both.
Metadatareport.docx · v4Laptophas v4Phonehas v4 + copyreport (Phone's conflicted copy)report.docx v4
Conflicts: accept a commit only on the current version.
  1. Both devices hold version 3 of report.docx. Each commit tells the server which version the edit was made on, its base version.
  2. The laptop and the phone both edit the file while offline. Neither knows about the other's change, and both edits are on base version 3.
  3. The laptop reconnects first. Its base, 3, is the current version, so the server accepts the commit and the file becomes version 4.
  4. The phone commits on base 3 too, but the current version is now 4. The compare-and-set fails, so the server refuses rather than silently overwrite the laptop's edit.
  5. The phone saves its edit as a conflicted copy and downloads version 4. No work is lost; the user, who knows what both edits meant, merges them.

Bottlenecks and trade-offs

  • Chunk size: small chunks dedup better and retry cheaper but mean more metadata rows and requests.
  • Hot metadata shards: a huge shared folder concentrates writes; split large namespaces.
  • Garbage collection: delete a chunk only when no version references it (ref_count, or a mark-and-sweep).
  • Cross-user dedup saves space but can reveal that someone already stored a file; per-account dedup avoids that.

Interview questions

  • Why not store whole files? Edits would re-upload everything, failed uploads restart, and duplicates are stored again.
  • Why keep metadata out of the blob store? It needs transactions, indexes and listing; blob stores give durability, not queries.
  • How does a device learn about changes? A long poll or WebSocket to a notification service, then a pull of the delta.
  • Two devices edit offline — what happens? The first commit wins; the second fails its version check and becomes a conflicted copy.

Next: Design a Ride-Hailing App (Uber) · Before this: Design a Notification System

Common questions

Why split files into chunks?

Chunks make large files practical. An interrupted upload resumes from the last chunk, an edit re-sends only the chunks it touched, and identical chunks across files and users are stored once. Each chunk is small enough to retry cheaply and to upload in parallel.

What is content-addressable storage?

Each chunk is stored under the hash of its own bytes, usually SHA-256. The same content always gets the same name, so storing it twice is harmless and duplicates are found by a lookup. A file then becomes an ordered list of hashes.

Where are file contents and metadata stored?

Contents go to a blob store such as Amazon S3, which is cheap, durable and scales without limit but cannot query. Metadata — users, folders, file names, versions and chunk lists — goes to a database that supports transactions and indexes, such as a sharded relational database.

How does a file sync across devices?

After a device commits a new version, the metadata service publishes a change event. Other devices hold a long poll or WebSocket open to a notification service, hear about the change, fetch the new chunk list and download only the chunks they lack.

How are edit conflicts handled?

Every commit carries the version it was based on, and the server accepts it only if that is still the current version. A device whose commit is refused keeps its edit as a conflicted copy beside the new version, so nothing is lost and the user merges them.

What is the difference between fixed-size and content-defined chunking?

Fixed-size chunking cuts every N bytes, so inserting one byte near the start shifts every later chunk and they all look new. Content-defined chunking picks cut points from the data itself with a rolling hash, so an insertion changes only the chunks around it.

← Design a Notification System · Design a Ride-Hailing App (Uber) →