Module 01 · Lesson 6 of 14Free
Capacity estimation: users, storage and traffic
- Level
- Beginner
- Topic
- System design fundamentals
- Reading time
- 14 min
When you finish this page you can
- Convert customer activity into average and peak request rates.
- Estimate CPU demand, requests in progress, bandwidth and storage with explicit units.
- Identify which assumptions could change a capacity decision.
- Turn an estimate into a focused measurement plan.
Builds on 2 lessons
Start with lesson 414 minVideo lesson · coming soon
At the festival sale, expects 1,000 requests a second. Its app server has 8 CPU cores. The launch checklist says “50,000 customers a day”, but that number doesn’t tell the team whether checkout will keep working.
How do you turn customers, clicks and saved orders into a useful capacity estimate?
Customers don’t arrive evenly
Capacity estimation means predicting resource demand from expected activity. It is a model with assumptions, not proof that a server can handle the load.
We stay at Kartly’s first stage: one app server and one PostgreSQL database. Start with the product targets from Functional and non-functional requirements. A useful estimate must support those targets, not just count requests.
Kartly has 50,000 customers a day. Would dividing that number by the seconds in a day tell you the server’s request rate?Show answer
For this lesson’s scenario, assume each daily customer generates 80 requests to Kartly’s app. This includes browsing, cart changes and checkout. It excludes image downloads, which we budget separately.
Count daily app requests
50,000 customers/day × 80 requests/customer = 4,000,000 requests/day
The customer count becomes useful only after we describe what customers do.
Find the daily average
4,000,000 requests/day ÷ 86,400 seconds/day ≈ 46 requests/second
Round this to about 50 requests a second for planning.
Preserve the sale peak
1,000 requests/second ÷ 50 requests/second = 20
The festival peak is about 20 times the rounded average. It is an independent scenario input, not something the daily average predicts.
Use seconds for server capacity, days for business activity, and years for retained data. Write the unit beside every input. Otherwise, a correct multiplication can answer the wrong question.
The average explains daily volume. The peak determines the next capacity question.
Size for the busy interval
A daily average spreads the festival rush across quiet hours. Customers cannot use spare capacity from midnight when checkout freezes at sale opening.
The interval matters too. A minute containing 60,000 requests averages 1,000 requests a second. It does not prove those requests arrived evenly within that minute.
Which traffic rate should Kartly use for launch planning?
Use the daily average.
This is easy to calculate, but it spreads the sale across the whole day. A plan based on about 50 requests a second ignores the expected launch rush.
Use the expected peak and record its time window.
Keep the 1,000-request-per-second first-minute target, then test shorter bursts within it. This matches the known sale behavior without pretending to know the exact arrival pattern.
Multiply the average by a large safety factor.
This covers more possible demand, but spends money on an unsupported assumption. It also does not explain which resource needs that extra capacity.
Why this choice
Kartly’s sale starts at a fixed time. That gives us a reason to expect a concentrated peak.
For this scenario, plan for the stated first-minute rate. Also test a brief burst above it. Choose that burst from launch evidence when available, rather than treating a universal multiplier as a rule.
For a slow-growing internal tool, observed busy-hour traffic may be enough. For a scheduled sale, shorter windows deserve attention.
We now know how much work arrives. We still need to know how expensive that work is.
Requests have different resource costs
A request rate is not a complete capacity description. Google’s overload guidance warns that different requests can require very different resources. It recommends considering the resources consumed, not only query counts.1
Use the distinction from Latency and throughput: time per request and requests per second describe different things.
For an initial calculation, assume the peak consists of requests like Kartly’s product-page example:
| Input | Scenario value |
|---|---|
| App CPU time per request | 5 ms |
| Database wait | 40 ms |
| Pricing-service wait | 50 ms |
| Total server-side elapsed time | 95 ms |
These are illustrative request costs, not measured limits. The database and pricing service are existing dependencies, not new components introduced by this estimate.
Estimate app CPU demand
1,000 requests/second × 0.005 CPU-seconds/request = 5 CPU-seconds/second
This is about five fully occupied cores. Against eight available cores, the modeled request work consumes about 63% of total CPU capacity.
Estimate requests in progress
1,000 requests/second × 0.095 seconds/request = 95 requests
Under steady conditions, about 95 requests are in progress on average. This uses the relationship taught in Server , not a count of people using the site.
Server concurrency explains how waiting requests share workers. Here, the important distinction is between CPU work and elapsed time.
The database wait is not app CPU demand. It also does not reveal the database’s CPU cost.
Our estimate therefore says “the modeled app CPU demand fits”. It does not say “the system handles the sale”.
For a real request mix, calculate each request class separately, then add its demand. If checkout costs more CPU than browsing, changing their proportions changes total demand even at the same request rate.1
The missing measurements are now specific: request mix, dependency capacity and behavior under load.
Bytes expose a different limit
For this scenario, assume an average app response body of 20 kB. We use decimal units throughout: one kB is 1,000 bytes; one GB is one billion bytes.
Estimate peak response traffic
1,000 responses/second × 20,000 bytes/response = 20 MB/second
This is the app’s outgoing response-body traffic under the stated assumptions.
Convert bytes to bits
20 MB/second × 8 bits/byte = 160 Mb/second
Compare bits per second with network capacity. Do not compare 20 MB/second directly with a link advertised in Mb/second.
This is a payload estimate. It excludes request bodies, protocol overhead and images. Compare it with usable network measured under the intended conditions, not just a connection’s advertised rate.
Now suppose each page also downloads three images. At an assumed 200 kB per image, that is 600 kB per page.
Do not multiply that by every app request. Cart updates and checkout calls are not page loads. First estimate image-bearing page views, then multiply by image bytes.
That separation prevents two mistakes: overlooking media traffic and charging every request for media it never downloads.
Storage needs the same separation between different kinds of data.
Retention turns writes into storage
Kartly takes 5,000 orders a day. For this scenario, assume each order and its line items need 2 kB of logical data.
Logical data means the estimated content before database storage overhead. PostgreSQL stores table data and indexes separately; wide values can also use additional storage. A payload estimate therefore does not equal physical disk usage.2
Estimate daily order growth
5,000 orders/day × 2,000 bytes/order = 10 MB/day
This counts newly created order data. It does not count every update as another complete order.
Choose a retention window
10 MB/day × 365 days = 3.65 GB
At an unchanged order rate, one year adds about 3.7 GB of logical order data.
Add an explicit planning allowance
3.65 GB logical data + 3.65 GB assumed overhead = 7.3 GB
For this first budget, allow an equal amount for indexes and storage overhead. This doubling is an assumption to replace with measurements, not a PostgreSQL rule.
Diagram not drawn yet: capacity-storage-ledger.svg
A storage ledger separates 3.65 GB of annual logical order growth from an assumed 3.65 GB allowance for indexes and overhead, totaling 7.3 GB of estimated physical growth. Separate unpriced rows name existing data, operational free space and backups. A media panel calculates 100,000 products times three images times 200 kB as 60 GB of image content.
The 7.3 GB estimate is annual growth, not a disk purchase recommendation. Existing tables, logs, temporary work and operational free space still need budgets. Backups need a separate budget too.
For comparison, Kartly’s 100,000 products would contain 60 GB of image content under our three-image assumption. That is a catalog snapshot, not annual growth.
Replace the database allowance by measuring representative tables and their indexes. PostgreSQL documents SQL functions as its recommended way to inspect disk usage.2
The storage result gives us a reason to measure actual bytes. It does not yet justify splitting the database.
Uncertainty should change the next action
Suppose Kartly’s team proposes a 70% app CPU planning budget. That leaves headroom: capacity deliberately left unused in the model.
Calculate the chosen CPU budget
8 cores × 0.70 = 5.6 CPU-seconds/second
This is a scenario policy, not a universal safe threshold.
Compare modeled demand
5 CPU-seconds/second ÷ 5.6 CPU-seconds/second ≈ 89%
The estimated request work consumes most of that chosen budget. Unmeasured overhead could change the decision.
Should Kartly buy more app capacity now?
Increase app capacity immediately.
This buys potential CPU room, but may leave the actual limit unchanged if the database or pricing service saturates first.
Measure the uncertain costs first.
Test the current server with the expected request mix and sale arrival pattern. Measure app CPU and dependency saturation before choosing what to expand.
Why this choice
The estimate identifies app CPU as worth checking. It does not identify it as the proven .
For this scenario, assume the team has time to test before launch. Keep the simple deployment while gathering evidence.
The choice changes if measured demand exceeds the budget, or procurement takes longer than the time remaining. In that case, reserve capacity earlier and validate the complete .
A small sensitivity check helps prioritize that test:
| Changed assumption | Derived consequence |
|---|---|
| App CPU cost doubles to 10 ms | Peak request work needs 10 cores |
| Response bodies double to 40 kB | Peak response-body traffic reaches 320 Mb/second |
| Order rate doubles for a full year | Logical annual order growth reaches 7.3 GB |
Each row changes one input. That makes the cause visible.
Measurements close the planning loop
Run the expected mix against representative data. Reproduce the sudden sale opening, rather than only increasing traffic slowly.
Record incoming requests, completed requests, errors and . Also inspect CPU, memory, waiting work and dependency limits. Resource consumption matters because equal request rates can impose different costs.1
For an illustrative failure, suppose 1,000 requests arrive each second but only 800 complete. If none are rejected or abandoned, waiting work grows by 200 requests a second. Customers see increasingly delayed cart and checkout responses.
More waiting is not extra capacity. Compare arrivals with completions to detect the gap, then identify the saturated resource. The load-testing lab develops that measurement process.
Where this shows up
- Design: a URL shortener on a single server: separate frequent redirects from less frequent link creation.
- Design: an e-commerce checkout: budget expensive order work separately from browsing.
Enough capacity under normal conditions still leaves another question: what happens when a component fails? Next, Core system properties separates serving requests, preserving data and agreeing on values.
Common mistakes
- Starting from registered accounts. Use active customers and their activity for the period you are estimating.
- Treating a request rate as a benchmark. State the request mix, data size and acceptable latency alongside it.
- Counting requests as writes. A read may store nothing; an order can create several records.
- Buying exactly the calculated storage. Separate growth, existing data, working space and backups.
- Hiding uncertainty in precise numbers. Keep the arithmetic inspectable, then round the result and name the uncertain inputs.
Check your understanding
Kartly’s customer count stays unchanged, but app requests per customer double. What happens to daily app request volume?Show answer
At 1,000 requests a second, app CPU cost rises from 5 ms to 10 ms per request. Can eight cores supply the modeled CPU demand?Show answer
A response averages 20 kB. Does a measured rate of 160 Mb/second prove that 1,000 requests a second will meet the latency target?Show answer
Your estimate gives 3.65 GB of annual logical order growth. Would a 4 GB disk be an adequate purchase?Show answer
Summary
- Turn customers into activity counts, preserve peak windows, then multiply by the resource cost of each activity.
- Keep units visible: requests per second, CPU-seconds, bytes per second and bytes retained.
- Estimate CPU work separately from elapsed time and dependency demand.
- Separate logical storage growth from physical storage, free space, media and backups.
- Treat uncertain inputs as measurement priorities. An estimate guides a load test; it does not replace one.