A two-way file sync engine: every file in a user's "Dropbox" folder should appear identically across all their devices within seconds of any change. The hard parts:
a content-addressable block store that deduplicates chunks across users for massive storage savings;
a metadata service that tells clients exactly which blocks to fetch/push per file version;
and a sync loop that reconciles local file-system changes with remote state while handling conflicts, offline edits, and flaky networks.
Dropbox stores exabytes of user data; clients sync ~billions of file events per day.
Two-way sync — changes on any device propagate to all others
Work offline: edit files with no network; sync when reconnected
Delta sync — only upload/download changed blocks of a file
File sharing: per-file or per-folder read/write ACLs
Version history (restore previous version of a file)
Cross-platform clients: macOS, Windows, Linux, iOS, Android, web
Cross-user deduplication (if 1000 users have the same PDF, store it once)
Non-Functional
Sync latency < 5 seconds for typical edits on good network
Handle individual files up to 100 GB; folders with 1M+ files
No data loss — durability trumps everything else
Minimal upload bandwidth via block-level diff + dedup
Scale to 700M users, ~50 PB of unique data, ~exabytes stored (pre-dedup)
End-to-end integrity — every block verified by hash
03
Scale Estimation
Users
~700M
~17.5M paying; ~2 devices each average
Block size
4 MB
sweet spot: small enough for fine-grained diff, large enough to amortize network overhead
Data stored (unique)
~50 PB
cross-user dedup cuts logical ~500 PB to ~50 PB physical
File events / sec
~500K
aggregate saves / moves / deletes across all clients
Block uploads / sec
~50K
~200 GB/sec; post-dedup write rate is much lower
Dedup hit rate
~30%
cross-user dedup typical; higher for office docs + common software
04
API Design
Three logical APIs: metadata (file tree + versions), blocks (raw byte I/O), and notify (push changes to clients). Clients talk HTTPS for metadata + blocks and hold a long-poll / WebSocket for notify.
POST/metadata/commit
Client uploads new file metadata: {path, block_list: [sha256_hashes], mtime, parent_rev}. Server checks parent_rev for concurrent edits; returns {rev, conflict?: {…}}. Metadata is lightweight (few KB).
POST/blocks/check
Client sends list of SHA-256 hashes; server returns which ones are missing. Dedup happens here: blocks server already has from any user don't need uploading.
PUT/blocks/{hash}
Upload a block. Server verifies the hash of received bytes == URL hash. If mismatch → reject + log. After write, return 201.
GET/blocks/{hash}
Download a block by hash. Served from CDN for hot blocks; from cold tier otherwise. Auth required — client must own a file referencing this hash.
GET/metadata/delta?cursor=X
Poll for changes since cursor X. Server returns a stream of {path, rev, action: added|changed|removed} + a new cursor. Efficient way for clients to catch up after being offline.
GET/notify/subscribelong-poll
Long-poll / WebSocket. Server holds the connection; pushes "changes available" pings. Client then calls /metadata/delta to fetch. Keeps clients live without constant polling.
GET/versions?path=/foo.pdf
List all historical versions of a file. Each version references its own block list; old blocks retained per retention policy.
05
Architecture
Three tiers: block storage (content-addressable; SHA-256 keyed), metadata service (sharded OLTP), and the client sync engine (watches filesystem + talks to server). Notify service is a thin pub/sub on top. Dropbox famously moved off AWS S3 onto their own block storage system ("Magic Pocket") in 2016 for cost.
Sync Engine + Server TiersSVG
Request Flow — Step Through
FS watcher · local save detected→Chunker · 4 MB blocks + SHA-256→Block check · POST /blocks/check→Block upload · PUT only missing→Metadata CAS · parent_rev check→Notify svc · push to devices→Peer sync · delta + fetch blocks
Click Next Step to walk through the request flow.
06
Deep Dive — Sync Loop + Block Dedup
When a user saves a 100 MB photo, the sync loop does NOT upload 100 MB. Here's the exact sequence:
FS watcher fires. OS-level file-system event (inotify on Linux, FSEvents on macOS, ReadDirectoryChanges on Windows) tells the client "foo.jpg changed."
Client chunks the file. Split into 4 MB blocks. Compute SHA-256 of each block. Result: 25 hashes for our 100 MB photo.
Query /blocks/check with 25 hashes. Server looks them up in the block-metadata DB (refcount > 0 = already stored somewhere). Returns which are missing. For a small edit to an existing photo, only the changed blocks are missing — often just 1–2.
Upload only missing blocks. Concurrent PUT /blocks/{hash}. Server verifies bytes-hash = URL-hash, writes into Magic Pocket (durably replicated).
Commit metadata.POST /metadata/commit with new block list, parent_rev. Server CAS on parent_rev — if another device changed meanwhile, returns a conflict for the client to resolve.
Notify other devices. Metadata write emits event; Notify service pushes "something changed" to subscribed clients. They call /metadata/delta to pull new metadata and (if they have the file open) fetch needed blocks.
Cross-user dedup. The content-addressable store keys blocks by their SHA-256 hash. If user A uploads a block with hash X, and later user B's client asks "do you have X?", the answer is yes — no upload needed. Storage cost drops ~30% from cross-user dedup on typical mixed workloads (much higher for shared software installers, documents templates, stock photos).
Upload Sequence with Block DedupMermaid
sequenceDiagram
participant C as Client
participant BL as Block svc
participant ST as Block storage
participant M as Metadata svc
participant N as Notify
C->>C: chunk file → [h1..h25]
C->>BL: POST /blocks/check [h1..h25]
BL-->>C: missing: [h7, h19]
par concurrent uploads
C->>BL: PUT /blocks/h7
BL->>ST: write (replicated)
ST-->>BL: ok
and
C->>BL: PUT /blocks/h19
BL->>ST: write (replicated)
ST-->>BL: ok
end
C->>M: POST /metadata/commit {blocks, parent_rev}
alt parent_rev matches
M-->>C: rev=42
M->>N: FileChanged event
N-->>C: push to other devices
else stale parent_rev
M-->>C: 409 conflict + server state
Note over C: run conflict resolver
end
Conflict resolution. Two devices edit the same file offline, both come online and commit. Dropbox's choice: both versions are preserved. The second commit creates foo.txt and foo (user's conflicted copy).txt. Simple, predictable, survives years of production use. They explicitly chose not to do OT / CRDT merging — file systems don't have semantic awareness of the content.
Delta sync for big files. For large files (> 100 MB) changed in small ways (e.g., a 2 GB video with re-edited metadata), block-level chunking naturally gives delta — only the block(s) containing the change are uploaded. For rolling changes (inserts in the middle of a file shift all subsequent block boundaries), Dropbox uses rolling hash chunking (Rabin fingerprint) so chunk boundaries follow content, not fixed offsets. A rename or middle-insert only shifts 1–2 blocks, not the whole rest of the file.
Interview answer
"Client chunks files into 4 MB blocks with SHA-256 hashes. Before upload, queries the block service for missing hashes — cross-user dedup means we only upload the actually-new blocks. Metadata commit is CAS-on-rev to detect concurrent edits. Conflicts save both versions (never merge). Notify service pushes metadata changes to other devices via long-poll/WebSocket. For large files with inserts, rolling-hash chunking keeps most boundaries stable. Storage is content-addressed by hash across all users; ~30% typical dedup savings."
07
Tradeoffs & Design Choices
Fixed-size vs rolling-hash chunks. Fixed-size (4 MB) is simple and fast. Rolling hash (Rabin fingerprint) gives better delta ratios on files with mid-content inserts but costs more CPU + breaks cross-user dedup slightly (same logical content can hash differently). Dropbox uses hybrid — fixed by default, rolling for media.
Block store separate from metadata store. Metadata (tree structure, versions, ACLs) is transactional — OLTP on MySQL. Blocks are immutable byte bags — object storage on Magic Pocket. Conflating them would put transactional constraints on byte-addressed storage = slow.
Save both on conflict vs merge. Save-both is the right call for generic files. Merging requires understanding file format (text, docx, images) — out of scope for a sync engine. Google Docs has per-character merge because it owns the editor too.
Magic Pocket vs S3. Dropbox moved off S3 in 2016 because at their volume, running their own storage (with custom erasure coding) was cheaper. The "build vs buy" crossover is specific to storage-heavy businesses.
Long-poll vs WebSocket for notify. Both work. Long-poll simpler, survives more firewalls, wastes a little compute at the server. WebSocket holds state but more efficient. Dropbox uses long-poll historically; modern clients shifted toward WS.
08
Failure Modes
💥
Partial block upload
Client uploads 2.5 MB of a 4 MB block then loses connection. Server has corrupted half-block.
→ Mitigation: server verifies bytes-hash on commit; mismatches are rejected and discarded. Upload is idempotent by hash — client simply retries; clean data eventually lands.
🔄
Infinite sync loop
Client detects local change → syncs up → server notifies → client detects remote change → writes locally → FS watcher fires → loop.
→ Mitigation: sync engine tracks "server-originated" writes and suppresses the resulting FS event. Each file write is tagged with origin (local vs remote).
🕳️
Dedup poisoning
Attacker registers a block hash without uploading matching bytes, then later someone else uploads different content hashing to same value (collision).
→ Mitigation: SHA-256 collisions are cryptographically negligible. But — always verify bytes-hash on write; never "trust" a hash-already-exists shortcut without a one-time byte verification at ingest.
🔒
Privilege escalation via block access
User A knows block hash X (shared from social engineering); tries GET /blocks/X.
→ Mitigation: block access checked against user's owned metadata — you can only fetch a block if your metadata references it. Hashes are not authorizers, metadata is.
🐢
1M-file folder on first sync
New user installs Dropbox and has a 1M-file project folder. Syncing each file sequentially takes hours; metadata service overloaded.
→ Mitigation: batch metadata commits (1k files per API call); parallel block check for thousands of blocks at once; bulk-initial-sync protocol skips the normal per-file commit overhead.
🗑️
Accidental delete of shared folder
User drags "Team Shared" folder to trash. Change propagates to all 20 teammates' devices. Data appears lost.
→ Mitigation: versioning + 30-day undo; "restore folder" flow; enterprise plans warn before deleting shared content. Actual bytes are retained per retention policy even after deletes appear complete.
09
Interview Tips
Lead with chunking + content-addressable storage. This is THE answer to "how do you make this efficient?" Most candidates miss this and just "upload the file to S3."
Separate metadata from blocks. Metadata is transactional (needs DB); blocks are immutable byte bags (object store). Don't conflate them.
Conflict resolution is "save both." Not OT/CRDT. Name this explicitly — it shows you understand the scope of a sync engine vs a collab editor.
Delta sync via chunk hashes. 99% of candidates think "rsync-style diff" — the correct Dropbox answer is "chunked CAS, query missing hashes, upload only those."
Offline-first is the sync loop's existence. If you don't have a client-side SQLite holding pending ops + a reconcile step, your design assumes online-always.
Fixed 4 MB chunks, SHA-256 hashes, S3 for blocks, MySQL for metadata. Dropbox's actual architecture for years.
2
Sharded metadata + notify service
MySQL sharded by user_id. Long-poll notify pushes change events to clients — no polling. Carries to ~100M users.
3
Magic Pocket — exit S3
Build custom block storage with Reed-Solomon erasure coding; reduce storage cost at scale. ~2016 for Dropbox. Cross-user dedup built in.
4
Nucleus sync engine rewrite (Rust)
Client sync engine rewritten in Rust (from Python) in 2019–2020. Correctness issues from the decade-old engine solved by redesigning around a proven algorithm. Classic "second-system" that shipped.
5
Smart Sync + collaboration
"Online-only" files don't occupy local disk until opened. Dropbox Paper + Spaces add collab layers on top of the sync engine. The sync engine stays file-oriented; collab is separate.