Skip to main content
RunBook Academy

LinuxLXVI · Capacity Planning for ClustersForecasting

Forecasting growth - turning a trend into a date

Intermediate⏱ ~15 minbashdfawkdate

What you'll learn

  • Fit a linear and a compound trend to a series of capacity measurements
  • Convert a growth rate into the date a resource crosses its ceiling
  • Subtract lead time to produce an order-by date rather than a run-out date
  • Recognise the forecasts that are wrong because the trend is not smooth

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

A capacity plan that ends in a percentage has not finished. “We are at 58%” is a measurement; it does not tell anyone whether to raise a purchase order this quarter or next year. The output of a capacity plan is a date, and the only way to a date is through a growth rate.

The arithmetic is small. The discipline is in doing it before the resource runs out rather than during the incident it causes.

The three inputs

To produce a date you need exactly three numbers, and you already have two of them from the baseline.

  1. Current value: where the resource is now, taken from the baseline - the p95, not the mean
  2. Ceiling: the value at which the resource stops being able to do its job, which for a cluster is the survivable ceiling rather than 100%
  3. Growth rate: how fast the current value is moving toward the ceiling, taken from at least three historical points

Get any of the three wrong and the date is wrong. The one people get wrong most often is the second: forecasting to 100% instead of to the survivable ceiling gives a date that is comfortably past the point at which a single node failure already takes the service down.

Linear growth

Linear growth adds a constant amount per period. Storage consumed by an append-only log, a metrics database with a fixed retention and a fixed number of series, or a customer base growing by a steady headcount all behave this way over the horizons that matter.

periods_remaining = (ceiling - current) / growth_per_period

Work a filesystem. A 2 TB volume holding 1.24 TB, growing 18 GB per week, with a ceiling of 85% - the point at which the filesystem’s allocator starts to struggle and the on-call gets nervous.

Ceiling   = 0.85 x 2048 GB = 1740.8 GB
Current   = 1240 GB
Remaining = 1740.8 - 1240   = 500.8 GB
Rate      = 18 GB/week

Weeks remaining = 500.8 / 18 = 27.8 weeks  (about 6.4 months)

So the volume crosses 85% around week 28. That is a date, and it is a date you can put in a plan.

Compound growth

Compound growth multiplies by a constant factor per period. User counts, request rates, database row counts and anything driven by a business that is itself growing behave this way. The mistake is to fit a straight line to compound growth, which always produces a date that is too far away.

value after n periods = current x (1 + g)^n

To find when it crosses the ceiling, solve for n:

n = ln(ceiling / current) / ln(1 + g)

Take a 4-node cluster at 50% per node, growing 20% per year. The survivable ceiling for four nodes is 75%, because 4 x 0.75 / 3 = 1.00 on the three survivors.

n = ln(75 / 50) / ln(1.20)
  = ln(1.5) / ln(1.2)
  = 0.4055 / 0.1823
  = 2.22 years

Year 2 lands at 50 x 1.2^2 = 72%, still under the ceiling. Year 3 lands at 50 x 1.2^3 = 86.4%, and the three survivors would need 4 x 86.4 / 3 = 115%. So the fifth node has to be in service before the cluster reaches 2.2 years from the measurement date.

Read-only / Safecompound forecast against the survivable ceiling
$ awk 'BEGIN { cur=50; ceil=75; g=0.20; n=log(ceil/cur)/log(1+g); printf "crosses ceiling in %.2f years\n", n; for (y=0; y<=4; y++) printf "  year %d: %.1f%% per node, survivors at %.1f%%\n", y, cur*(1+g)^y, 4*cur*(1+g)^y/3 }'
crosses ceiling in 2.22 years
year 0: 50.0% per node, survivors at 66.7%
year 1: 60.0% per node, survivors at 80.0%
year 2: 72.0% per node, survivors at 96.0%
year 3: 86.4% per node, survivors at 115.2%
year 4: 103.7% per node, survivors at 138.2%

Doubling time

For a quick sanity check without a calculator, the rule of 72 gets you close enough to argue with:

doubling time in periods = 72 / (growth rate as a percentage)

At 20% per year that is 3.6 years to double; the exact answer is 3.8. At 6% per month it is 12 months. If a colleague says growth is “about 5% a month” and you are at 40% utilisation, you have roughly fourteen months to double to 80% - which is enough to know whether this is a next-sprint problem or a next-year one.

Getting the rate from real data

A rate taken from two points is not a rate, it is a line through two points. Use at least three, and prefer a full quarter of the same statistic - p95 against p95, never p95 against a mean.

For filesystems, df sampled on a timer is enough. There is nothing to install.

Read-only / Safeone sample
$ df --output=target,used,pcent /srv; date -u +%FT%TZ
Mounted on        Used Use%
/srv        1300234240  62%
2026-08-11T15:41:07Z

Illustrative output

Append that to a file on a systemd timer, and the growth rate is a subtraction:

# Collected daily into /var/log/capacity/srv.log as: <epoch> <used_kib>
awk 'NR==1 {t0=$1; u0=$2} END {
  days = ($1 - t0) / 86400
  gib  = ($2 - u0) / 1048576
  printf "%.1f GiB over %.1f days = %.2f GiB/day\n", gib, days, gib/days
}' /var/log/capacity/srv.log

If your monitoring stack already holds the series, ask it directly. Prometheus has predict_linear, which fits a least squares line over a range and extrapolates:

predict_linear(node_filesystem_avail_bytes{mountpoint="/srv"}[7d], 30 * 24 * 3600) < 0

That expression is true when the seven-day trend says the filesystem hits zero within thirty days. It is a far better disk alert than a static 85% threshold, because it fires on speed rather than on level: a volume that has sat at 88% for two years does not page anybody, and one that went from 20% to 40% overnight does.

Subtract the lead time

The run-out date is not the date you act on. Work backwards through everything that has to happen first.

Run-out date               week 28
- Burn-in and validation      2 weeks
- Rack, cable, provision      1 week
- Delivery                    6 weeks
- Purchase order and approval 4 weeks
- Quotes and specification    2 weeks
                            ---------
Order-by date              week 13
Safety margin (20%)           3 weeks
                            ---------
Act by                     week 10

Twenty-eight weeks of runway became ten. That compression is the entire reason capacity planning is done in advance rather than on demand, and it is the number that turns a technical measurement into a business decision.

Lead times are organisational, not technical, and you must measure your own. Ask how long the last three purchases actually took, end to end, rather than how long procurement says they take. Cloud capacity shortens some of these steps to minutes and lengthens others - quota increases, reserved-instance commitments and regional availability all have lead times of their own.

When the forecast is wrong

Trends break, and knowing how they break stops you trusting a number past its useful range.

  • Step changes. A new customer, a new region, or a retention policy change moves the level rather than the slope. Re-fit after any of them; a trend fitted across a step describes neither side of it.
  • Seasonality. Retail in November, payroll at month-end, academic systems in September. Fit on whole cycles, and compare like periods - this September against last September, not against August.
  • Saturation of the trend itself. Growth that looks exponential is often the early part of an S-curve. Adoption slows. Forecasting five years of compound growth from six months of data is how organisations end up with a datacentre they do not need.
  • The forecast changing the behaviour. Announce that /srv is filling and people delete things. That is a success, not a modelling failure, but it invalidates the fit.

Re-forecast on a fixed cadence - quarterly is usual - and treat a forecast older than one cadence as unverified.

Knowledge check

Knowledge check · 6 questions

  1. Q1. A 2 TB volume holds 1240 GB and grows 18 GB per week. The ceiling is 85%. When does it cross the ceiling?

  2. Q2. A 4-node cluster is at 50% per node and growing 20% per year. When does a single-node failure stop being survivable?

  3. Q3. Why does fitting a straight line to compound growth produce a dangerous forecast?

  4. Q4. A filesystem alert uses predict_linear over a 7-day range instead of a static 85% threshold. What behaviour does that change?

  5. Q5. Which of these invalidate an existing capacity forecast and require a re-fit? Select all that apply.

  6. Q6. A capacity plan must state an order-by date that subtracts the full procurement lead time from the run-out date.

Passing score: 75%. Answers are checked in this browser.