Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

Managed DevOps

Capacity Planning and Load Testing Before Traffic Finds the Limits

Capacity planning estimates how much infrastructure your product needs for expected demand, and load testing proves it. We model growth, run realistic tests with tools like k6, find bottlenecks, and tune autoscaling and sizing so peaks are handled without permanent over-provisioning.

Key takeaways

  • Capacity plans without load tests are guesses; load tests without a model are noise.
  • Realistic traffic shape matters more than raw request volume.
  • The first bottleneck is usually a database, a connection pool or a downstream API, not CPU.
  • Tests run against production-like environments, with care taken around shared data and third parties.
  • Results become autoscaling settings, alerts and a forward-looking capacity view.

Why launches fail on the day

Most capacity incidents follow the same script. A campaign or release lands, traffic climbs, and something unexpected saturates first: the database connection pool, a single-threaded queue consumer, a rate-limited third-party API, or a node group that cannot scale quickly enough. CPU on the web tier was fine. Nobody tested the path that actually broke.

Capacity planning and load testing exist to find that first weak point while it is cheap to fix. It is part of our Managed DevOps service, and it links closely to cost reviews, since the same data tells you where you are overbuilt as well as underbuilt.

What good looks like

  • A written statement of the demand you must handle: peak requests per second, concurrent users, data growth, and the events that cause spikes.
  • Defined success criteria such as p95 latency and error rate under that load.
  • Load tests that reproduce real user journeys, not a single endpoint hammered.
  • A known first bottleneck and a known second one.
  • Autoscaling configured from test evidence, not defaults.
  • Headroom that survives the loss of an availability zone.

How it works in practice

Start with a traffic model

Raw volume is the least interesting number. What matters is the mix: reads versus writes, cacheable versus uncached, cheap versus expensive endpoints, anonymous versus authenticated. We derive it from access logs and analytics, then add the growth assumption your business plans for. Arrival rate matters too. Real traffic is open: users arrive whether or not the server is keeping up. Many naive tests are closed-loop, where each virtual user waits for a response, which hides overload because the test slows down with the system.

A representative k6 test

An arrival-rate scenario keeps pressure constant regardless of response time:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  scenarios: {
    checkout_peak: {
      executor: 'ramping-arrival-rate',
      startRate: 20,
      timeUnit: '1s',
      preAllocatedVUs: 200,
      maxVUs: 1000,
      stages: [
        { target: 150, duration: '5m' },
        { target: 150, duration: '15m' },
        { target: 0, duration: '2m' },
      ],
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    http_req_duration: ['p(95)<800'],
  },
};

export default function () {
  const res = http.get(`${__ENV.BASE_URL}/api/products`);
  check(res, { 'status is 200': (r) => r.status === 200 });
  sleep(1);
}

The numbers above are examples; real targets come from your own goals. Thresholds make the test pass or fail automatically, so it can run in a pipeline.

Watch the system, not just the tester

The test client reports what users would feel. Prometheus and Grafana show why. During every run we watch saturation at each tier: CPU throttling, memory, connection pool wait time, database locks and replication lag, queue depth, and node scaling events. Distributed traces help find the slow hop. Our monitoring page describes the signals we already collect.

Find and fix the bottleneck

Typical findings, roughly in order of frequency:

  1. Database: missing indexes, N+1 queries, undersized connection pools, or a single primary handling reads.
  2. Application: synchronous calls that should be queued, blocking work on request threads.
  3. Autoscaling: scaling that triggers too late, or pods that take minutes to become ready.
  4. Cluster: no node capacity for new pods, or quotas hit.
  5. Third parties: rate limits and slow responses that cascade.

We fix one thing, rerun the same test, and compare. Changing five things at once leaves you unsure what helped.

Turning results into settings

Load tests are worth little if they stay in a slide deck. We convert them to configuration:

  • Pod requests and limits based on measured usage at target load.
  • Horizontal pod autoscaler thresholds and minimum replicas so scaling begins before saturation. Event-driven scaling with KEDA suits queue-based workloads.
  • The vertical pod autoscaler in recommendation mode to keep requests honest over time.
  • Node capacity and autoscaler settings, including how fast new nodes arrive. See cluster operations.
  • Alerts on saturation trends, so the next limit is visible before it is hit.

Decisions and trade-offs

Test environment fidelity. A smaller staging environment can still reveal bottlenecks, but results do not scale linearly. We state what a result does and does not prove.

Pre-scaling versus autoscaling. A flash sale may spike faster than nodes can start. Pre-scaling before a known event is cheap insurance. Autoscaling is better for organic variation.

Over-provisioning. Running at peak capacity all year is safe and costly. The goal is enough headroom, not maximum headroom.

When not to do this. An early product with a few hundred users has more urgent problems than a formal capacity study. A simple smoke-level load test before a launch is usually enough. The full process pays off when downtime has a clear cost.

A worked example of reading results

Suppose a test ramps to the target rate and p95 latency climbs after twelve minutes while CPU on the application pods sits at a modest level. Adding replicas does not help. Looking one layer down, the database shows connections waiting on a pool capped at a low number, and a single slow query holding locks. The fix order is then clear: add the missing index, raise the pool carefully (a larger pool can overload the database), and retest. Only after that does it make sense to tune autoscaling.

This pattern repeats often enough that we treat pods and CPU as the last place to look, not the first. A useful habit is to write down a hypothesis before each run (“we expect the database to saturate first”) and compare it with what happens. Wrong guesses are where the learning is.

Common failure modes

  • Testing with an empty database. Queries behave differently on realistic data volumes.
  • Hitting real third parties. Use sandboxes or mocks, or you pay and risk being blocked.
  • Ignoring cold starts. Fresh pods and cold caches behave differently from warm ones.
  • One-off tests. Performance regresses release by release. We keep a lightweight test in CI for key paths.
  • Forgetting the downstream. Your service survives, then the shared database or queue does not.

Delivery and handover

You get the test scripts in your repository, a short report of limits and bottlenecks, updated autoscaling configuration, and a capacity forecast reviewed with you in the monthly report. Before major releases we pair this with an architecture review.

How to tell it is working

Look for a known maximum sustainable load, a stable p95 latency at expected peak, autoscaling that activates before alerts fire, and launch days that are uneventful. To plan for an upcoming event, book a consultation.

References

How we deliver it

  1. 01

    Define demand and goals

    We agree the events to survive (launch, campaign, seasonal peak) and the latency and error targets that count as success.

  2. 02

    Model realistic traffic

    Real request mixes, user journeys and data sizes are taken from logs and analytics.

  3. 03

    Prepare a safe test environment

    A production-like environment, seeded data and mocked or sandboxed third parties are set up.

  4. 04

    Run staged tests

    Baseline, ramp, stress, spike and soak tests run while we watch metrics for the first bottleneck.

  5. 05

    Fix and retest

    Findings are fixed in order of impact, then the same test repeats to prove the change.

  6. 06

    Turn results into settings

    Autoscaling thresholds, requests and limits, headroom and alerts are updated, with a capacity forecast.

Tools and technologies

k6LocustGatlingPrometheusGrafanaHorizontal Pod AutoscalerKEDAKarpenterpgbenchAWS Distributed Load TestingOpenTelemetry

Frequently asked questions

What is the difference between load, stress and soak testing?
Load testing checks behaviour at expected traffic. Stress testing pushes beyond expected limits to find the breaking point and how the system fails. Soak testing holds moderate load for hours to expose leaks and slow degradation. Spike testing adds a sudden surge. Each answers a different question, so we usually run several.
Can we load test production?
Sometimes, with care, but we usually prefer a production-like environment. Testing production risks real customer impact, polluted data and surprise bills from third-party APIs. If production testing is the only option, we use low volumes, off-peak windows, clear stop conditions and a tester who can abort instantly.
How do you plan capacity for a launch?
We take the expected peak, add a safety margin, model the request mix, and test to that level and beyond. Then we check headroom for losing a zone, set autoscaling to react in time, and pre-scale if scaling is slower than the spike. We also confirm limits on cloud quotas and external services.
Which load testing tool do you use?
k6 is our default because tests are written in JavaScript, run well in CI and export metrics to Prometheus and Grafana. Locust suits Python teams and Gatling suits JVM teams. The best tool is the one your developers will maintain, so we adapt to your stack.
How often should capacity be reviewed?
Review at least quarterly, and before any major launch, marketing push or architectural change. Growth rates change, features shift the request mix, and dependencies drift. A monthly look at saturation trends is cheap and catches problems well before they cause outages.

Talk to an engineer about capacity planning and load testing

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.