Filter concepts by levelShowing all levels.

System Design · Section 4

Capacity Estimation

Level
beginner
Read
20 min
Concepts
5

The numbers a design needs before any component is chosen: requests per second, storage growth, bandwidth, memory and queue volume, computed with three core formulas, refined with a read/write split, cache hit ratio and replication factor, and finally divided down into an actual, benchmarked infrastructure count rather than a guessed server number.

This section

What is true here

  1. Five categories — RPS, storage growth, bandwidth, memory, queue volume — cover what a capacity estimate needs.
  2. Three formulas: average RPS = requests/day ÷ 86,400; peak RPS = average × peak factor; storage = records × size × retention multiplier.
  3. Database-facing load is reads/sec × (1 − cache hit ratio); storage growth compounds by the replication factor.
  4. A powers-of-ten shortcut (÷100,000 instead of ÷86,400) gives a fast, ~15.7%-high estimate for quick mental math.
  5. An estimate only matters once it is divided by a benchmarked per-unit capacity into an actual infrastructure count.

What you will be able to do

  • Name all five categories a capacity estimate needs to cover
  • Compute average RPS, peak RPS and storage from a daily request total
  • Split total RPS into database-facing reads and writes using a cache hit ratio
  • Use the powers-of-ten shortcut for a fast mental estimate
  • Translate a peak RPS or storage figure into a server or node count

The formulas

What to estimate, and the three core formulas behind average RPS, peak RPS and storage.

What to estimate before designing

corebeginner

Before choosing any component, a design needs five numbers: requests per second, storage growth, bandwidth, memory, and queue volume — each one rules out or rules in different infrastructure choices.

Think of it as

A component diagram without numbers is a guess. Requests per second sizes the compute and load balancers; storage growth sizes the database and its backups; bandwidth sizes the network and CDN; memory sizes caching; queue volume sizes the message broker. Skipping any one of them leaves that part of the design unsized.

text
Five numbers, before any component is chosen:
  requests/sec   -> compute + load balancing
  storage growth -> database + backups
  bandwidth      -> network + CDN
  memory         -> caching
  queue volume   -> message broker + workers

What we're doing: Take one feature request and produce all five numbers before naming a single piece of infrastructure.

estimation-categories.txttext
Feature: "Users can upload and view short video clips."

  Requests/sec:   2,000 views/sec peak, 50 uploads/sec peak
  Storage growth: 500 GB/day of new video
  Bandwidth:      3 Gbps peak egress serving video
  Memory:         cache trending clips' first 5 seconds
  Queue volume:   50/sec transcoding jobs
3
Views and uploads are estimated separately — a 40:1 read/write split changes what gets optimized first.
4
Storage growth per day, not total — this is what tells you when the database needs partitioning or cold storage.
5
Bandwidth is the number a CDN or egress-cost conversation actually needs.
6
Memory here is scoped to what caching can realistically help with, not "cache everything."
7
Queue volume matches the upload rate 1:1, because every upload triggers exactly one transcoding job.

Why this works: Naming a component before these five numbers exist is designing from familiarity, not from the actual load. Each number rules specific choices in or out — a load balancer choice depends on requests/sec, a storage choice depends on growth, and so on.

Estimating only requests per second and skipping the other four

Wrong

text
"We'll get about 2,000 requests per second at peak,
so we need a few app servers behind a load balancer."
(storage, bandwidth, memory and queue volume never estimated)

Better

text
"2,000 req/s peak, 500 GB/day storage growth,
3 Gbps peak bandwidth, cache sized for the top 5%
of content, and a queue sized for 50 jobs/sec."

What you see: The app tier is correctly sized and holds up fine, while the database fills its disk within weeks and the transcoding queue backs up for hours during a traffic spike — because neither was ever estimated.

Why: Requests per second is the most visible number, so it is the one most often estimated alone. The other four fail silently at first and become incidents later, once real traffic exercises them.

Five numbers before any component is chosen

Requests/sec

sizes compute, load balancers

Storage growth

sizes database, backups

Bandwidth

sizes network, CDN

Memory

sizes caching

Queue volume

sizes broker, workers

  • Requests/sec — sizes compute, load balancers
  • Storage growth — sizes database, backups
  • Bandwidth — sizes network, CDN
  • Memory — sizes caching
  • Queue volume — sizes broker, workers

The five estimation categories

The five estimation categories
CategoryWhat it sizesIgnoring it means
Requests per secondcompute, load balancers, connection poolsthe API tier falls over under real load
Storage growthdatabase capacity, backup cost, retentiona disk fills up months after launch
Bandwidthnetwork links, CDN spend, egress costa cloud bill spikes with no design change
Memorycache size, what avoids a database hitthe cache is too small to matter
Queue volumemessage broker throughput, worker counta queue backs up faster than it drains

Together

text
A photo-sharing app, sized across all five:

  Requests/sec:   3,500 peak reads, 400 peak writes
  Storage growth: 40 GB/day of new photos
  Bandwidth:      1.2 Gbps peak egress serving photos
  Memory:         cache the 5% of photos that get 80% of views
  Queue volume:   400/sec thumbnail-generation jobs

Remember: Five numbers before any component: requests/sec, storage growth, bandwidth, memory, queue volume — each sizes a different part of the design.

See also: core formulas · infra translation

The core capacity formulas

corebeginner

Three formulas cover most capacity estimates: average RPS is requests per day divided by 86,400 seconds; peak RPS multiplies that by a peak factor; storage multiplies record count by average size by a retention multiplier.

Think of it as

A day has 86,400 seconds — that single constant converts any daily total into an average rate. Peak load is never the average; a peak factor (commonly 2x-5x, higher for spiky traffic) scales it up. Storage is never just "records times size" either — a retention multiplier accounts for indexes, replicas and headroom on top of the raw data.

text
average RPS = requests_per_day / 86400
peak RPS    = average RPS * peak_factor
storage     = records * avg_record_size * retention_multiplier

What we're doing: Apply all three formulas to one feature, in order, and show the arithmetic behind each result.

core-formulas.txttext
Feature: a URL shortener expecting 50,000,000
redirects/day, a peak factor of 4x, storing
20,000,000 short-URL records at 300 bytes each,
with a 1.5x retention multiplier (indexes + replica).

  Average RPS = 50,000,000 / 86,400        ≈ 579
  Peak RPS    = 579 × 4                    ≈ 2,315
  Storage     = 20,000,000 × 300B × 1.5    ≈ 8.4 GB
6
Daily total divided by 86,400 seconds converts a day-level number into a per-second average.
7
The average alone would under-provision the system — a 4x peak factor is what the design actually has to survive.
8
Record count times average size gives raw storage; the 1.5x multiplier adds room for the index the lookup needs and a replica copy.

Why this works: These three formulas turn a product sentence ("50 million redirects a day") into the numbers a design actually has to hold up under — a server count, a database size — without which every downstream choice is a guess.

Designing for the average, not the peak

Wrong

text
"Average RPS is 579, so we'll provision for
about 600 requests per second."

Better

text
"Average RPS is 579, but peak factor is 4x
during business hours, so provision for
2,315 requests per second."

What you see: The system runs fine most of the day and falls over every morning at 9am, because it was sized for the average load and the actual peak is four times higher.

Why: A system that only meets its average load will fail at exactly the moment traffic matters most — the peak. Average RPS is an input to the peak formula, not a substitute for it.

100M requests/day, peak factor 3x

Average RPS

100,000,000 ÷ 86,400 ≈ 1,157 RPS

Peak RPS

1,157 × 3 ≈ 3,472 RPS

Storage (raw)

10M records × 2 KB = 20 GB

Storage (with retention)

20 GB × 1.3 ≈ 24.8 GB

  1. Average RPS — 100,000,000 ÷ 86,400 ≈ 1,157 RPS
  2. Peak RPS — 1,157 × 3 ≈ 3,472 RPS
  3. Storage (raw) — 10M records × 2 KB = 20 GB
  4. Storage (with retention) — 20 GB × 1.3 ≈ 24.8 GB

The three core formulas

The three core formulas
QuantityFormulaWorked example
Average RPSrequests/day ÷ 86,400100M/day ÷ 86,400 ≈ 1,157 RPS
Peak RPSaverage RPS × peak factor1,157 × 3 ≈ 3,472 RPS
Storage (raw)records × avg record size10M records × 2 KB = 20 GB
Storage (with retention)raw storage × retention multiplier20 GB × 1.3 ≈ 24.8 GB

Together

text
100,000,000 requests/day, peak factor 3x:

  Average RPS = 100,000,000 / 86,400  ≈ 1,157
  Peak RPS    = 1,157 × 3             ≈ 3,472

10,000,000 records, 2 KB average size, 1.3x retention multiplier:

  Storage = 10,000,000 × 2 KB × 1.3   ≈ 24.8 GB

Remember: avg RPS = req/day ÷ 86,400; peak RPS = avg × peak factor; storage = records × size × retention multiplier.

See also: estimation categories · powers of ten

Advertisement

Using the estimate

Refining the raw numbers, estimating fast, and turning the result into real infrastructure.

Reads, writes, cache hits and growth

standardintermediate

A single RPS number is not enough — splitting it into reads vs writes, subtracting what a cache absorbs, and multiplying storage growth by a replication factor turns one estimate into the numbers each component actually needs.

Think of it as

Requests per second is a total that hides two very different loads: reads (usually the majority, and the ones a cache can absorb) and writes (the ones that always reach the database). A cache hit ratio says what fraction of reads never touch the database at all. Storage growth compounds by the replication factor, because every byte written is stored more than once for durability.

text
reads/sec    = total RPS * read_fraction
writes/sec   = total RPS * write_fraction
db_reads/sec = reads/sec * (1 - cache_hit_ratio)
growth/day   = writes/day * avg_write_size * replication_factor

What we're doing: Take one total RPS figure and derive the database-facing load and daily storage growth a design actually has to provision for.

reads-writes-and-growth.txttext
A news site: 8,000 total RPS, 9:1 read/write ratio,
90% cache hit ratio on article reads, 4 KB average
article size, 2x replication factor.

  Reads/sec    = 8,000 × 0.9          = 7,200
  Writes/sec   = 8,000 × 0.1          = 800
  DB reads/sec = 7,200 × (1 - 0.9)    = 720
  Writes/day   = 800 × 86,400         = 69,120,000
  Raw growth   = 69.12M × 4 KB        ≈ 263.7 GB/day
  With 2x rep  = 263.7 × 2            ≈ 527.4 GB/day
5
Reads dominate at 90% of total traffic — this is what makes caching worth building for this system.
7
A 90% cache hit ratio means only 10% of reads reach the database — 720 out of 7,200, not the full 7,200.
9
Writes/day comes from writes/sec, not reads/sec — the cache never absorbs a write.
10
Replication doubles the growth number the database actually has to be provisioned for.

Why this works: Provisioning a database for the total RPS figure massively overstates its read load and understates its storage growth. Splitting reads from writes, subtracting the cache, and applying the replication factor are what turn one number into the two the database actually needs sized for.

Sizing the database for total RPS instead of database-facing reads

Wrong

text
"We get 8,000 requests per second, so the
database needs to handle 8,000 reads per second."

Better

text
"8,000 total RPS, 90% cache hit ratio on the
7,200 reads/sec, so the database only sees
about 720 reads/sec plus 800 writes/sec."

What you see: The database is provisioned for 8,000 reads/sec and massively over-built — ten times more than it will ever actually see once the cache is in front of it, wasting cost the estimate should have avoided.

Why: Total RPS is not database load. A cache absorbing 90% of reads means the database-facing number is an order of magnitude smaller — sizing for the wrong number wastes budget in one direction and risks an outage in the other.

One RPS number, split into the three the database actually needs

Reads — 7,200/s

6,480/s never leave the cache

7,200 x 0.9 — the hit ratio is doing this much work

720/s reach the database

7,200 x (1 - 0.9). An order of magnitude below the 8,000 a naive estimate would provision for

Writes — 800/s

800/s reach the database

Every write is a database write. No hit ratio applies

69,120,000 writes/day

800 x 86,400

Storage growth

~263.7 GB/day raw

69.12M x 4 KB

~527.4 GB/day provisioned

x 2 replication — every byte is stored twice for durability

  • 8,000 RPS total
  • Reads — 7,200/s — 90% of traffic, and a cache can absorb most of it
    • 6,480/s never leave the cache — 7,200 x 0.9 — the hit ratio is doing this much work
    • 720/s reach the database — 7,200 x (1 - 0.9). An order of magnitude below the 8,000 a naive estimate would provision for
  • Writes — 800/s — 10% of traffic, and the cache absorbs none of it
    • 800/s reach the database — Every write is a database write. No hit ratio applies
    • 69,120,000 writes/day — 800 x 86,400
  • Storage growth — Driven by the writes, then multiplied again
    • ~263.7 GB/day raw — 69.12M x 4 KB
    • ~527.4 GB/day provisioned — x 2 replication — every byte is stored twice for durability

Second-layer estimates and their formulas

Second-layer estimates and their formulas
QuantityFormulaWorked example
Reads/sectotal RPS × read fraction5,000 × 0.8 = 4,000
Writes/sectotal RPS × write fraction5,000 × 0.2 = 1,000
DB reads/secreads/sec × (1 − cache hit ratio)4,000 × 0.2 = 800
Raw daily growthwrites/day × avg write size86.4M writes × 1 KB ≈ 82.4 GB
Growth with replicationraw growth × replication factor82.4 GB × 3 ≈ 247.2 GB

Together

text
5,000 total RPS, 4:1 read/write ratio,
80% cache hit ratio, 1 KB average write size,
3x replication factor:

  Reads/sec       = 5,000 × 0.8         = 4,000
  Writes/sec      = 5,000 × 0.2         = 1,000
  DB reads/sec    = 4,000 × (1 - 0.8)   = 800
  Writes/day      = 1,000 × 86,400      = 86,400,000
  Raw growth/day  = 86.4M × 1 KB        ≈ 82.4 GB
  With 3x replica = 82.4 GB × 3         ≈ 247.2 GB

Remember: Split RPS into reads/writes, subtract the cache hit ratio for DB-facing reads, and multiply storage growth by the replication factor.

See also: core formulas · infra translation

Powers-of-ten approximation

standardbeginner

Dividing by 86,400 in your head is slow; dividing by 100,000 (roughly 86,400 rounded up) is fast and close enough for an interview or a first-pass estimate — 100M requests/day is roughly 1,000 RPS before refining to the real 1,157.

Think of it as

86,400 seconds in a day is close enough to 100,000 that treating it as 10^5 turns any daily total into a per-second estimate by just moving a decimal point. It is a rounding-up approximation — the true answer is about 15% lower than the 10^5 shortcut gives — good enough for a first pass, not for a final capacity number.

text
quick avg RPS ≈ requests_per_day / 100,000   // 10^5, fast mental math
exact avg RPS  = requests_per_day / 86,400    // real answer, ~15.7% lower

What we're doing: Use the powers-of-ten shortcut to get an instant estimate, then refine it to the exact figure.

powers-of-ten.txttext
A checkout service handles 30,000,000 orders/day.

  Quick:  30,000,000 / 100,000        = 300 RPS
  Exact:  30,000,000 / 86,400         ≈ 347 RPS

  Difference: 347 - 300 = 47, about 15.7% higher
  than the quick estimate.
3
The quick shortcut moves a decimal point — no calculator needed, useful when talking through an estimate live.
4
The exact formula from L72 gives the real number the design should actually use.
6
The gap between the two is consistent — always about 15.7%, because 100,000 is always 15.7% larger than 86,400.

Why this works: A capacity conversation moves faster when a rough RPS figure can be produced instantly, without reaching for a calculator — the shortcut is for keeping a design discussion moving, not for the number that ends up in a capacity plan.

Using the powers-of-ten shortcut as the final number

Wrong

text
"30 million orders a day is 300 requests per
second." (stated as the final capacity figure,
sized directly into the infrastructure plan)

Better

text
"Roughly 300 RPS as a quick estimate — the
exact figure is about 347 RPS, which is what
we should size the peak calculation from."

What you see: Every downstream number — peak RPS, server count — is quietly 15.7% too low, because the fast approximation was never refined to the exact ÷86,400 figure before it fed into the rest of the capacity plan.

Why: The powers-of-ten shortcut is a speed tool for a live conversation, not a replacement for the exact formula — a 15.7% understatement compounds through every formula built on top of it (peak RPS, server count, cost).

Why the shortcut works, and exactly how wrong it is

The two bars are drawn to scale: 86,400 is 86.4% of 100,000, so the shortcut always overstates RPS by the same 15.7%.

  • Two horizontal bars drawn to scale against each other.
  • The upper bar is the real number of seconds in a day, 86,400.
  • The lower bar is the shortcut, 100,000 or ten to the fifth. It is visibly longer, and the overhang is marked plus 15.7 percent.
  • Below: 100 million a day divided by ten to the fifth is 1,000 RPS, while the same total divided by 86,400 is about 1,157 RPS.

Quick daily-total-to-RPS approximations

Quick daily-total-to-RPS approximations
Requests/dayQuick estimate (÷10^5)Exact (÷86,400)
1,000,000 (10^6)10 RPS≈ 12 RPS
10,000,000 (10^7)100 RPS≈ 116 RPS
100,000,000 (10^8)1,000 RPS≈ 1,157 RPS
1,000,000,000 (10^9)10,000 RPS≈ 11,574 RPS

Together

text
Quick check during a conversation:
  "500 million requests a day is roughly
   5,000 requests per second" (500M / 10^5)

Refined afterward:
  500,000,000 / 86,400 ≈ 5,787 RPS

Remember: 86,400 ≈ 100,000 for quick mental math — daily total ÷ 10^5 gives a fast RPS estimate, about 15.7% under the exact ÷86,400 figure.

See also: core formulas · reads writes and growth

From estimate to infrastructure

coreintermediate

A capacity estimate is only useful once it turns into a count — peak RPS divided by what one server can handle gives a server count. Naming a server count without this division is a guess wearing a number.

Think of it as

Every capacity number from the earlier concepts (peak RPS, storage growth, bandwidth) is an input to one more division: the total load divided by what a single unit of infrastructure can handle, rounded up, with headroom added on top. The unit's real capacity has to come from a benchmark or a vendor spec, not an assumption.

text
server_count = ceil(peak_rps / rps_per_server * (1 + headroom))
node_count   = ceil(total_storage / disk_per_node)

What we're doing: Take a peak RPS and a storage figure from earlier concepts and turn both into an actual infrastructure count.

infra-translation.txttext
Peak RPS: 2,315 (from the URL shortener example).
Benchmarked: one app server handles 400 RPS.
Storage: 8.4 GB, one database node holds 500 GB.
Headroom target: 30%.

  Servers  = ceil(2,315 / 400)             = 6
  With 30% headroom = ceil(6 × 1.3)        = 8 servers

  DB nodes = ceil(8.4 GB / 500 GB)         = 1 node
  (well under capacity — one node is enough for years)
6
Peak RPS divided by a real, benchmarked per-server figure — not an assumed or rounded one.
7
Headroom is applied after the base count, so a sudden 30% traffic increase over the peak estimate does not immediately saturate every server.
9
Storage translates the same way — total divided by per-node capacity, rounded up.

Why this works: A capacity estimate that stops at "3,472 peak RPS" has not answered the question a design review actually asks, which is "how many servers." Dividing by a real per-server figure and adding headroom is what turns an estimate into a number that can be provisioned.

Naming a server count without a benchmarked per-server capacity

Wrong

text
"We expect 3,472 peak RPS, so let's provision
about 10 servers." (10 is not derived from
anything — no per-server capacity was measured)

Better

text
"3,472 peak RPS ÷ 500 RPS per server
(benchmarked under realistic load) = 7,
plus 25% headroom = 9 servers."

What you see: The server count is either wildly over-provisioned (wasting cost) or under-provisioned (falling over at peak), because it was picked as a round number rather than derived from an actual per-server capacity figure.

Why: A server count with no per-server capacity behind it is not an estimate — it is a guess with a number attached. The whole point of capacity estimation is to replace that guess with a division that can be checked and re-derived when load changes.

Peak RPS to server count, with headroom

Peak RPS

3,472

Per-server capacity

500 RPS, benchmarked

Divide, round up

3,472 / 500 ≈ 6.9 -> 7

+25% headroom

7 × 1.25 ≈ 8.75 -> 9 servers

  • Peak RPS — 3,472
    • leads to Divide, round up
  • Per-server capacity — 500 RPS, benchmarked
    • leads to Divide, round up
  • Divide, round up — 3,472 / 500 ≈ 6.9 -> 7
    • leads to +25% headroom
  • +25% headroom — 7 × 1.25 ≈ 8.75 -> 9 servers

Estimate-to-infrastructure translations

Estimate-to-infrastructure translations
EstimateDivided byGives
Peak RPSRPS one app server handles (benchmarked)app server count
Storage (with retention)disk capacity per database nodedatabase node count
Peak bandwidthnetwork tier capacitynetwork/CDN tier choice
Queue volumejobs/sec one worker processesworker pool size

Together

text
Peak RPS: 3,472. One benchmarked app server
handles 500 RPS. Add 25% headroom.

  Servers needed = 3,472 / 500          ≈ 6.9 -> 7
  With 25% headroom = 7 × 1.25          ≈ 8.75 -> 9 servers

Remember: Server count = peak RPS ÷ benchmarked RPS-per-server, rounded up, plus headroom — never a guessed round number.

See also: core formulas · estimation categories

Advertisement