Vibe Engines
YouTube
System Design

Design a Distributed File System

Step 1 / 9

Learn system design by building a distributed file system like GFS or HDFS step by step.

The numbers to beat1 mastermetadata onlyin-RAMchunk mapdirectclient ↔ chunkserver data

The whole design, in writing

Learn system design by building a distributed file system like GFS or HDFS step by step. An interactive guide covering the single file-server ceiling, splitting huge files into large chunks across chunkservers, a single master holding the namespace and chunk map, keeping the master out of the data path, replication with rack-aware placement, the primary/pipelined write path, making the master survivable, and the unhappy paths (small files, stragglers, re-replication).

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

What is a distributed file system?

A data pipeline needs to store files that are terabytes each and read them at huge aggregate throughput across a fleet of worker machines. One file server can't hold them, can't feed a thousand readers at once, and loses everything when its disk dies.

Clientread / append
New in this step: Client.

A distributed file system (GFS, HDFS) presents one namespace over a whole cluster of commodity machines. It's tuned for a specific workload — huge files, big sequential reads and appends — and gets its scale from a clever split: a single master owns the metadata, while the actual bytes stream directly between clients and hundreds of chunkservers.

What the new pieces do

Clientclient
A data-processing job (think MapReduce) that reads and appends huge files in big sequential streams — not tiny random edits.

Step 1 · The ceiling

One file server, one workload

Start with a single big file server (an NFS box). Workers mount it and read. It's simple and correct. Where does it fall over for a data-crunching cluster?

Clientread / append
The system as it stands at this step.

A single file server feeding a cluster of workers. What caps it?

  1. Naming is irrelevant. The real caps are capacity (one box's disks), aggregate throughput (one box's NICs feeding a thousand readers), and durability (its disk dies).

  2. One server can't hold petabytes, can't feed a thousand workers at full bandwidth, and loses data when it fails. A cluster filesystem spreads all three across many machines.

  3. RAM doesn't add disk capacity, network bandwidth to many readers, or redundancy. The bottleneck is fundamentally "one machine," which more RAM can't fix.

One box caps three things at once: capacity (its disks), aggregate throughput (its network feeding many readers), and durability (its disk failing). A data cluster needs the file's bytes spread across many machines so a thousand workers can each read their slice at full speed, and no single failure loses data.

Why this piece earns its place

The call to spread bytes across a cluster only pays off because of an assumption this step makes quietly: the workload is huge files, read sequentially and appended to rather than overwritten. Spreading is not free — every read now needs a lookup before it can start, so the first byte arrives later than it would from a single mounted server. That cost vanishes inside a hundred-megabyte stream and is ruinous on a 4KB one. So the day this design meets an interactive workload — millions of small files, random reads, latency measured per operation — it is worse than the one box it replaced, and no tuning fixes it, because the ask-then-stream shape is the architecture rather than a setting. The honest move is to name that boundary here instead of defending it at the end: this is a throughput filesystem for a batch cluster, and a latency-sensitive workload deserves a different store rather than this one bent to fit.

Step 2 · Big blocks

Split files into large chunks

To spread a terabyte file across machines, you break it into pieces. How big should a piece be — kilobytes like a normal filesystem block, or something far larger?

Clientread / append
The system as it stands at this step.

How big should each piece of a file be, and why?

  1. Millions of tiny blocks explode the metadata the master must track and add per-block overhead. For huge sequential files, large chunks drastically cut metadata and maximize streaming throughput.

  2. A whole-file piece can't be spread across machines or read in parallel, defeating the point. You want pieces big enough to be efficient but small enough to distribute.

  3. Big chunks mean far fewer chunks per file, so the master's metadata is small enough to hold in memory, and each read is a long sequential stream — ideal for the workload. The trade is that small files waste a chunk, which the system accepts.

Split files into large chunks (GFS used 64 MB; HDFS blocks are often 128 MB). Big chunks mean a petabyte of data is only millions of chunks — few enough for the master to track in memory — and every read is a long sequential stream that saturates disk and network. The cost (a small file wastes a whole chunk) is a deliberate trade for this workload.

Why this piece earns its place

Large chunks concentrate load as surely as they shrink metadata, and that is the failure this answer creates. A small but popular file — a binary or a config every worker opens when a job starts — lands in a single chunk, so its handful of replicas absorb the whole cluster’s read burst in the same instant while thousands of other chunkservers sit idle. The fix is not a smaller chunk size; that would surrender the metadata win everywhere to solve a problem in a few files. It is to give that file more replicas than the default so the burst spreads across more machines, and to stagger job startup so a thousand workers don’t all ask in the same second. A client that is stuck waiting can also be pointed at another client that already pulled the bytes. The general lesson: chunk size is a placement decision as much as a storage one, because nothing smaller than a chunk can be spread at all.

Step 3 · A fleet to hold them

Chunkservers

Those chunks need to live somewhere that scales and survives failure. What holds them, and how do you make chunk loss survivable from the start?

ClientChunkserver 1Chunkserver 2Chunkserver 3
New in this step: Chunkserver 1, Chunkserver 2, Chunkserver 3. · swipe to pan the diagram

Where do chunks live so the store scales and survives failures?

  1. One copy per chunk means any server failure loses data, and high-end boxes waste money. Use commodity chunkservers and replicate each chunk across several of them.

  2. Chunkservers store chunks as ordinary files and stream them to clients; replicating each chunk (default 3×) across different servers/racks makes any single failure survivable and spreads read load.

  3. The master holds metadata only — the actual bytes are far too large for it and must live on the chunkserver fleet. Keeping data off the master is central to the design.

Run a fleet of commodity chunkservers that store chunks as ordinary local files and stream them to clients. Replicate each chunk across several chunkservers (default 3×) so any single failure is survivable and popular chunks can be read from multiple sources in parallel. Capacity and read bandwidth both scale by adding chunkservers.

Why this piece earns its place

Three is a knob, not a law, and you should be able to say how you would set it. One copy loses data on the first disk failure. Two survives a failure but leaves a single copy standing for however long the repair takes, so one more unlucky failure in that window — or a routine reboot of the machine holding it — loses the chunk. Three keeps a spare while the cluster heals. Going higher buys durability you cannot measure while costing a full extra copy of every byte stored and every byte written, and the write cost is the one people forget. It should also vary by data rather than being one global number: a scratch intermediate file a job can simply recompute doesn’t deserve the same protection as the input nobody can regenerate. Set it per file, and treat the default as the answer for data you would have to apologise for losing.

What the new pieces do

Chunkserver 1data
A commodity box storing large chunks as plain Linux files. Streams bytes directly to and from clients.
Chunkserver 2data
Holds replicas of chunks. Writes are pipelined replica-to-replica so bandwidth isn't wasted fanning out from one node.
Chunkserver 3data
The third replica (default replication is 3×). Placed on a different rack so one rack failure can't lose a chunk.

Step 4 · Keep the master out of the bytes

One master for metadata

Something must track which chunks make up each file and where each chunk's replicas live. But if every byte of every read flowed through that one component, it would instantly be the bottleneck. How do you get a single source of truth without it choking on data?

Clientread / appendMasternamespace + map
New in this step: Master.

A single master holds the metadata. How do you stop it becoming the data bottleneck?

  1. No single machine can serve a cluster's full read bandwidth — that's the ceiling you're escaping. The master must be removed from the data path, not scaled up.

  2. Multiple masters streaming data reintroduces coordination and still funnels bytes through metadata nodes. The elegant answer keeps ONE master and routes data around it entirely.

  3. The master answers "which chunkservers hold chunk N?" — a tiny control message — then the client talks to those chunkservers directly for the actual data. The master handles metadata only, so a single master serves a huge cluster. Clients cache locations to avoid re-asking.

Split control from data. The client asks the master a tiny question — "which chunkservers hold chunk N of this file?" — and the master answers from its in-memory map. Then the client streams the actual bytes directly to/from those chunkservers; the master never touches file data. One master can steer an enormous cluster because it only moves metadata.

Why this piece earns its place

Keeping the master out of the data path only holds if clients stop asking it, so they cache the locations they’re given — and a cache means a client’s view of the cluster goes stale exactly while the cluster is repairing itself. A replica moves, a chunkserver dies, and the client dials a machine that is gone or no longer holds that chunk. That has to fail fast and loudly rather than quietly return old bytes: the client retries another replica from its cached set, and only on a miss goes back to the master for a fresh answer. Two things keep the master’s side of this small. Cached locations carry an expiry, so a stale entry can’t live forever. And a client asks about several chunks in one request, so a job streaming a whole file pays one lookup, not one per chunk. The load the master really carries is proportional to file opens, not to bytes moved — that’s the number to quote when someone doubts one master is enough.

  • 1 mastermetadata only
  • in-RAMchunk map
  • directclient ↔ chunkserver data

What the new pieces do

Mastergateway
The single metadata authority: holds the file→chunk namespace and which chunkservers hold each chunk. Answers "where?", then gets out of the way.

Back of the envelope

control: client → master
tiny location lookup
data: client ↔ chunkservers
bytes never hit the master
clients cache locations
master load stays low

Step 5 · Don't lose a chunk

Replication & rack-aware placement

Chunkservers fail daily, and sometimes a whole rack or switch goes down at once. Three replicas are useless if all three sit in the same rack. How do you place copies so real, correlated failures can't take out a chunk?

ClientMasterChunkserver 1Chunkserver 2Chunkserver 3
The system as it stands at this step. · swipe to pan the diagram

You keep 3 replicas of each chunk. Where do you place them?

  1. Three replicas in one rack all die when that rack's switch or power fails — correlated failure. Placement must spread replicas across failure domains (racks) to be meaningful.

  2. The master places replicas on different racks so a rack outage loses at most one copy, balances by disk usage, and re-replicates when a copy is lost. Rack-aware placement is what turns 3× into real durability.

  3. Concentrating replicas on one server destroys the redundancy — that server's failure loses every copy. Replicas must be on distinct servers, ideally distinct racks.

The master does rack-aware placement: it puts a chunk's replicas on chunkservers in different racks so a rack/switch/power failure loses at most one copy. It balances placement by disk utilization, and when a replica is lost it schedules re-replication to restore the target count. Replication is only as good as the independence of where the copies sit.

Why this piece earns its place

Placing copies in different racks pays for durability with cross-rack bandwidth, and the bill arrives at the worst possible moment. Every write now crosses rack uplinks to reach its other replicas, and when a rack does fail, every chunk it held is under-replicated at once, so the master schedules a wave of copying across those same uplinks while surviving jobs are still trying to read. Healing at full speed can hurt more than the failure did. So repair is throttled: a cap on how many clone operations run at once, cluster-wide and per chunkserver, plus a bandwidth limit on each, so restoring copies never starves live traffic. The queue is ordered by risk rather than by arrival — a chunk down to one copy is cloned before a chunk down to two, and a chunk nobody reads can wait. Placement policy and repair policy are really one policy; designing either alone is how a rack failure becomes a cluster outage.

  • 3×default replication
  • rack-awareindependent copies
  • autore-replicate on loss

Step 6 · Writing at scale

The primary & pipelined replication

A write must land on all three replicas consistently, and you don't want to waste the writer's bandwidth sending the same bytes three times, nor let three replicas disagree on the order of concurrent appends.

Chunkserver 164MB chunksChunkserver 2replicaChunkserver 3replica
New in this step: Chunkserver 1 → Chunkserver 2, Chunkserver 2 → Chunkserver 3.

The master grants one replica a short lease as the primary; it picks the order of mutations so all replicas apply them identically. Data is pushed once and pipelined replica-to-replica (client → replica A → B → C) to use every link's full bandwidth instead of fanning out from the client. Because the workload is append-heavy, GFS offers an efficient record append that atomically adds to the end at a system-chosen offset.

Why this piece earns its place

A lease is a timeout, and a timeout can’t tell a dead primary from a slow one. A primary stalled in garbage collection, or sitting behind a partition, still believes it holds the lease while the master — watching that lease expire — is free to grant another. Now two replicas think they order writes. The lease alone does not close that gap; what closes it is a version number the master bumps on the chunk every time it grants a lease. A replica that missed mutations carries an older version, so the master won’t hand it to readers, won’t make it primary, and collects it as garbage. The stale writer’s traffic is refused because its version is wrong, not because anyone managed to detect that it was slow. Lease extensions then ride on heartbeats the master already exchanges, so the common case costs no extra round trip — and declining to extend is how the master stops mutations on a chunk it is about to move.

  • leaseone primary orders writes
  • pipelineA → B → C
  • appendatomic record append

Step 7 · Protect the one brain

Make the master survivable

You deliberately built a single master — elegant, but now it's the one thing whose loss stops all new lookups. How do you keep a single logical master without it being a fatal single point of failure?

MasterShadow MasterChunkserver 1Chunkserver 2Chunkserver 3Op Log + Checkpoint
New in this step: Shadow Master, Op Log + Checkpoint. · swipe to pan the diagram

Persist every metadata change to a replicated operation log and take periodic checkpoints, so the master's entire state can be recovered by replay. Run shadow masters that follow the log and serve read-only lookups, and promote a standby quickly on failure. Keep chunk locations out of the persistent state — the master rebuilds them from chunkserver heartbeats at startup, so the log stays small. Because the master is out of the data path, a brief failover doesn't stop in-flight transfers.

Why this piece earns its place

A promoted master is not ready the moment its log finishes replaying. It has the namespace, but it deliberately never persisted where any chunk lives, so for its first stretch of life it is only listening to chunkservers report what they hold. If it starts making placement decisions during that silence, it concludes the cluster has lost nearly everything and launches a re-replication storm that copies the cluster onto itself. So a new master stays read-only until enough of the fleet has checked in, and only then may it schedule repairs — the same reason HDFS holds a starting NameNode in safe mode. The knob here is checkpoint frequency, because replay time is simply the time since the last checkpoint: checkpoint rarely and failover stretches out into log replay, checkpoint constantly and you stall live mutations unless the checkpoint is built in the background from a copy of the state. Recovery time is a number you choose, not one you discover mid-outage.

What the new pieces do

Shadow Masterservice
A hot standby replaying the master's operation log. Serves read-only lookups and can be promoted fast if the master dies.
Op Log + Checkpointcontrol
The master's durable, replicated operation log plus periodic checkpoints — the source of truth it recovers from.

Step 8 · The sharp edges

Small files, stragglers & healing

Real clusters hit the design's soft spots: millions of tiny files bloat the master's memory, a slow "straggler" chunkserver drags a job, and failures constantly erode replication.

Chunkserver 3replicaRe-replicationrestore copies
New in this step: Re-replication.

Discourage small files (they waste chunks and inflate metadata) — pack them or use a layer on top. Combat stragglers by reading a chunk from the fastest of its replicas and by speculative task execution upstream. Let the master continuously re-replicate under-replicated chunks and rebalance disk usage as servers join and leave. Verify chunks with checksums to catch corruption, and rely on the append/at-least-once model rather than promising fully random-write consistency.

Why this piece earns its place

The append path is where this design hands work back to the application, and you should say so plainly. An atomic record append guarantees a record lands at least once on every replica, not exactly once: when one replica fails a mutation the client retries, and the replicas that succeeded on the first attempt keep their earlier copy. Replicas can also carry padding where a retry skipped past a region. So reading a file back is not simply reading bytes — records have to be self-describing. Each carries a checksum so a reader can walk past padding and garbage to the next real record, and a unique id so duplicates can be dropped. Readers filter; they never assume. The bargain is deliberate: making appends exactly-once would need a coordination round on every write, which is precisely what the lease exists to avoid. The system stays simple and fast by publishing a weaker guarantee and letting one shared client library absorb it.

What the new pieces do

Re-replicationrepair
Master-scheduled work that rebuilds under-replicated chunks onto healthy chunkservers after a failure.

You did it

You just designed a distributed file system.

ClientMasterShadow MasterChunkserver 1Chunkserver 2Chunkserver 3Op Log + CheckpointRe-replication
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • One file server caps capacity, throughput and durability — spread files across a cluster.
  • Split huge files into large chunks (64–128MB) so metadata stays tiny and reads stream.
  • Store chunks on commodity chunkservers, each chunk replicated (3×) and disposable.
  • A single master serves only metadata; clients stream bytes directly to/from chunkservers.
  • Rack-aware placement makes 3 replicas survive correlated (rack) failures.
  • A leased primary orders writes; data is pipelined replica-to-replica; appends are atomic.
  • Make the master recoverable (op log + checkpoint + shadows), and continuously re-replicate.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Why keep the master out of the data path, and how does that scale?

    If every byte of every read and write flowed through a single master, that master's network and CPU would cap the whole cluster's throughput — you'd have merely moved the single-server bottleneck. GFS/HDFS avoid this by making the master a pure metadata service: a client asks it only "which chunkservers hold chunk N?", a tiny control message answered from an in-memory map, and then the client streams the actual data directly to or from those chunkservers. Because the master handles only small, infrequent location lookups (and clients cache those locations to avoid re-asking), a single master can steer a cluster serving enormous aggregate data bandwidth. The control plane (client↔master) and data plane (client↔chunkserver) scale independently — one brain, many muscles.

  2. Why are chunks so large (64–128 MB) compared to a normal 4KB filesystem block?

    Two reasons, both tied to the target workload of huge files and sequential access. First, metadata: the master keeps the entire chunk map in memory for speed, so the number of chunks must stay manageable — large chunks mean a petabyte is only millions of chunks instead of billions, keeping the master's memory footprint small. Second, throughput: a big chunk means each read is a long, contiguous, sequential stream that saturates disk and network with minimal seeking and minimal per-operation overhead, and it lets a client do lots of work against one chunk while holding a single cached location and a persistent connection. The downside is internal fragmentation — a tiny file still occupies (logically) a chunk and bloats metadata per byte — which is exactly why these systems discourage small files.

  3. How does a write stay consistent across three replicas without hammering the master?

    The master grants one of the replicas a time-limited lease making it the primary for that chunk. The primary then chooses a single serial order for all concurrent mutations and tells the other replicas to apply them in that same order, so every replica ends up byte-identical without the master being consulted on each write. Separately, to avoid wasting the writer's bandwidth sending the data three times, the bytes are pushed once and pipelined down a chain of replicas (client → nearest replica → next → next), so each network link is used at full speed and total write bandwidth isn't throttled by one node's uplink. The append-heavy workload is served by an atomic record-append operation that appends at a system-chosen offset, giving useful at-least-once append semantics that concurrent writers can rely on.

  4. The single master seems like a single point of failure — is it?

    It's a single point of coordination, but the design makes it recoverable rather than immortal. All namespace-changing operations are written to a replicated operation log and periodically compacted into checkpoints, so the master's entire persistent state can be reconstructed by replaying the log — losing the master process doesn't lose the metadata. Volatile data like which chunkserver currently holds which chunk is deliberately kept out of the log and instead rebuilt from chunkserver heartbeats at startup, which keeps the log small and recovery fast. Shadow masters follow the log and can serve stale read-only lookups, and a standby can be promoted quickly on failure. And because the master is out of the data path, a brief failover doesn't interrupt reads and writes already streaming between clients and chunkservers — only new metadata lookups pause. HDFS later hardened this into an automatic active/standby NameNode pair sharing an edit log.

  5. Object storage vs a distributed file system — how do they differ?

    They solve overlapping problems with different interfaces and workloads. A distributed file system (GFS/HDFS) exposes a mutable, POSIX-ish namespace of files you can append to and read as byte streams, optimized for huge files and high-throughput sequential access by data-processing frameworks running in the same cluster — it favors throughput over latency and assumes append-heavy access. Object storage (S3) exposes a flat bucket/key namespace of immutable blobs over a simple HTTP API, optimized for durability, massive scale, and access over the internet by arbitrary clients, with no notion of appending in place (you replace an object). Architecturally both separate metadata from data and rely on replication/erasure coding across commodity nodes, but the DFS keeps a filesystem abstraction with in-cluster streaming, while object storage keeps a web-scale key-value blob abstraction; modern data stacks increasingly put object storage underneath and layer table/file semantics on top.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. The key trick that lets one master serve a huge cluster is…

    Clients ask the master only for chunk locations, then stream bytes directly to/from chunkservers.

  2. Chunks are large (64–128MB) mainly to…

    Few chunks per file → in-memory metadata; big chunks → high sequential throughput.

  3. Replicas of a chunk are placed…

    Rack-aware placement makes 3 copies survive correlated rack/switch/power failures.

  4. During a write, consistency across replicas comes from…

    The primary serializes mutations; data is pipelined replica-to-replica for bandwidth.

  5. The single master avoids being a fatal SPOF by…

    Its state is durable and fast to recover; volatile chunk locations come from chunkserver heartbeats.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Store huge files: hold terabyte-scale files across a cluster of commodity machines.
  • Read & append: stream big sequential reads and appends at high aggregate throughput.
  • Locate chunks: ask the master which chunkservers hold a file’s chunks, then stream bytes directly.
  • Survive failure: replicate each chunk (3×) across racks and re-replicate lost copies.
  • Consistent writes: a leased primary orders mutations; appends are atomic.

The qualities that shape everything

Each one names the mechanism that buys it.

Metadata small enough to hold in memory
Files split into 64-128 MB chunks, so a petabyte is only millions of chunks — few enough for the master to keep the chunk map in RAM — and each read is a long sequential stream.
Survive constant commodity-hardware failure
Chunks live on a fleet of commodity chunkservers, each chunk replicated 3× so any single failure is survivable and popular chunks read from several sources in parallel.
One master that never bottlenecks the data
The master answers only tiny location lookups from its in-memory map; clients then stream bytes directly to/from chunkservers, so control and data planes scale independently.
3 copies that survive correlated failures
The master places replicas in different racks so a rack/switch/power failure loses at most one copy, and re-replicates to restore the target count.
Consistent writes without a master hit per write
The master leases one replica as primary to order mutations, and data is pipelined replica-to-replica so no single node’s uplink bottlenecks replication.
The single master isn’t a fatal SPOF
Every metadata change goes to a replicated operation log plus checkpoints, shadow masters follow it, and a standby is promoted fast — chunk locations rebuilt from heartbeats.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Spread the file’s bytes across many machines over a single big file server

One box caps capacity (its disks), aggregate throughput (its NICs feeding a thousand readers) and durability (its disk dies) all at once. A cluster spreads all three so each worker reads its slice at full speed and no single failure loses data.

Large chunks (64-128 MB) over tiny 4KB blocks like a local filesystem

Millions of tiny blocks explode the metadata the master must track and add per-block overhead. Big chunks keep the chunk map in memory and make each read a long sequential stream — accepting that a small file wastes a whole chunk.

Many commodity chunkservers, each chunk replicated over one dedicated high-end server per chunk

A single copy loses data on any failure, and high-end boxes waste money. Commodity chunkservers with 3× replication make any single failure survivable and spread read load — durability from replication, not expensive hardware.

Master serves only location lookups over a huge master machine that serves all the data

No single machine can serve a cluster’s full read bandwidth — that’s the exact ceiling you’re escaping. Keeping the master out of the data path lets one brain steer many muscles: control and data planes scale apart.

Rack-aware placement across failure domains over placing replicas wherever there’s free space

Three replicas in one rack all die when its switch or power fails — correlated failure makes 3× meaningless. Spreading copies across racks so a rack outage loses at most one is what turns replication into real durability.

The answer, out loud

What a strong answer to “Design a Distributed File System” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Scope it before I draw anything

    Before I draw anything, let me pin down what we’re building, because file system is a wide word. I’m assuming a storage layer for a data-processing cluster: files that are terabytes each, a thousand or so worker machines reading them, and access that is overwhelmingly big sequential reads and appends rather than little random edits. That last assumption is the one I want agreed up front, because almost every decision after it is bought with it. I’d also assume commodity hardware, which means machines failing is ordinary traffic here, not an incident to be handled.

  2. 3–8 min

    Why one file server can’t do it

    The naive version is one big file server everyone mounts, and it’s worth saying out loud why that fails, because it fails three ways at once. It can’t hold the data — capacity is whatever disks fit in one chassis. It can’t feed the readers — a thousand workers pulling at once all share one machine’s network. And it can’t survive, because when that machine’s disk dies the data is gone with it. Those are three separate ceilings, and the same move lifts all three: put the file’s bytes on many machines.

    Built in step 1: One file server, one workload
  3. 8–14 min

    Break each file into big chunks

    So I split every file into fixed-size chunks and scatter them. The interesting question is the size, and I’d go far larger than a normal filesystem block — think sixty-four to a hundred and twenty-eight megabytes rather than four kilobytes. Two reasons. One, whatever component tracks where chunks live has far fewer of them to track: at that size a petabyte is millions of chunks, not billions. Two, a read becomes one long contiguous stream instead of a million little lookups, which is exactly what this workload wants. The cost is that a tiny file still burns a chunk, and I’ll come back to that.

    Built in step 2: Split files into large chunks
  4. 14–20 min

    A fleet of cheap boxes, three copies

    The chunks live on a fleet of chunkservers — ordinary machines storing each chunk as a plain local file. Capacity and read bandwidth both grow by adding boxes, which is the property I wanted. And since I already said hardware fails constantly, every chunk is replicated, three copies by default. That buys two things at once: any single machine can die without losing data, and a popular chunk can be read from three places in parallel. Durability comes from the replication, not from buying more reliable hardware, and that’s deliberate — making failure survivable is far cheaper than making it rare.

    Built in step 3: Chunkservers
  5. 20–27 min

    One master, and it never touches the bytes

    Now something has to know which chunks make up a file and where each copy sits. I’d use a single master for that, which usually gets a raised eyebrow, so here’s the defence. The master answers one tiny question: which chunkservers hold chunk N. Then the client goes and talks to those chunkservers directly, and the actual bytes never pass through the master at all. If they did, I’d have rebuilt the single-server bottleneck I just escaped. Because it only moves metadata, and can keep the whole chunk map in memory, one master can steer an enormous cluster. One brain, many muscles.

    Built in step 4: One master for metadata
  6. 27–33 min

    Where the three copies actually go

    Three copies only help if they fail independently, so placement is the master’s job rather than the client’s. If all three land in one rack, one switch or one power feed takes out every copy and the replication factor was theatre. So copies go in different racks, the master also balances by how full the disks are, and when a copy disappears it schedules new ones to bring the count back up. I’d frame it this way: the number of replicas is the easy part, the independence of the replicas is the durability.

    Built in step 5: Replication & rack-aware placement
  7. 33–38 min

    Writes: one orderer, one pipeline

    Writes need two things. Every replica has to end up with the same bytes in the same order, and I don’t want the writer sending the same data three times. So the master hands one replica a short lease making it the primary, and that primary picks the order every replica applies. The data itself is pushed once down a chain — client to the nearest replica, then on to the next — so each link is carrying bytes instead of one uplink carrying three streams. And because the workload is append-heavy, appends get a dedicated atomic operation where the system picks the offset.

    Built in step 6: The primary & pipelined replication
  8. 38–43 min

    Protecting the one brain

    Back to the master, because I said single and I owe you an answer. I don’t make it never fail, I make it fast to rebuild. Every namespace change goes into a replicated operation log, with periodic checkpoints so a replay stays short, and shadows follow that log and can serve read-only lookups. Chunk locations deliberately aren’t in the log at all — the chunkservers tell a new master what they hold. And what makes a brief failover tolerable is the decision from earlier: the master isn’t in the data path, so transfers already streaming don’t even notice.

    Built in step 7: Make the master survivable
  9. 43–45 min

    The trade-off, and what I’d do next

    So, the honest summary: this thing is fast because it refused to be general. Huge files, sequential reads, appends — and in exchange it’s bad at millions of small files, which eat the master’s memory, and bad at random overwrites, which it never promised. Every choice bought throughput with something: big chunks bought it with wasted space, one master with a component you have to protect, replication with three times the disk. With more time I’d go after the small-file case by packing them behind a layer on top, and I’d want to work out what this looks like across two data centres, which is a question the design deliberately doesn’t answer.

What this teaches

Learn system design by building a distributed file system like GFS or HDFS step by step. An interactive guide covering the single file-server ceiling, splitting huge files into large chunks across chunkservers, a single master holding the namespace and chunk map, keeping the master out of the data path, replication with rack-aware placement, the primary/pipelined write path, making the master survivable, and the unhappy paths (small files, stragglers, re-replication).

Key takeaways

  • One file server caps capacity, throughput and durability — spread files across a cluster.
  • Split huge files into large chunks (64–128MB) so metadata stays tiny and reads stream.
  • Store chunks on commodity chunkservers, each chunk replicated (3×) and disposable.
  • A single master serves only metadata; clients stream bytes directly to/from chunkservers.
  • Rack-aware placement makes 3 replicas survive correlated (rack) failures.
  • A leased primary orders writes; data is pipelined replica-to-replica; appends are atomic.
  • Make the master recoverable (op log + checkpoint + shadows), and continuously re-replicate.

Concepts covered

  • What is a distributed file system?
  • One file server, one workload
  • Split files into large chunks
  • Chunkservers
  • One master for metadata
  • Replication & rack-aware placement
  • The primary & pipelined replication
  • Make the master survivable
  • Small files, stragglers & healing
built to stream petabytes past a master that never touches the bytes — make the calls, drop a chunkserver, run the gauntlet.
Finished this one? 0 / 65 System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More System Designs