Load Testing vs Stress Testing: Differences, Metrics and k6 Examples
One proves your system handles the traffic you expect. The other finds out where it breaks and how it recovers. Learn when to run each, which numbers to trust, and how to plan a test that gives real answers.
- Load testing checks that a system meets its targets under expected traffic.
- Stress testing pushes past capacity to find the breaking point and study recovery.
- The tools are usually the same. What differs is the goal and the traffic pattern.
- Judge results with percentiles such as p95 and p99, since averages can hide serious problems.
Your application works perfectly with ten users. What happens on sale day, with ten thousand? Load testing vs stress testing describes two different ways to answer that, and mixing them up leads to tests that do not prove what you think they do.
This guide explains what each one shows, the related test types and how to read the results. We ran the small percentile calculation below on Python 3.13, with simulated numbers. The k6 scripts are illustrative examples that we did not execute, and the user counts and targets are invented, so replace them with your own. Only run these tests against systems you own or have permission to test, since heavy traffic can look like an attack and cause real outages.
The short answer
A load test asks: can we handle the traffic we expect, and meet our targets while doing it? A stress test asks: how much can we take before we fail, how do we fail, and how quickly do we recover?
Think of a bridge. A load test drives the expected number of trucks across and confirms that it is fine. A stress test keeps adding weight to learn its limit, and whether it gives warning first. Fortunately, software is cheaper to break than a bridge.
Load testing vs stress testing side by side
| Aspect | Load testing | Stress testing |
|---|---|---|
| Main question | Do we meet targets at expected load? | Where and how do we break? |
| Traffic level | Normal to expected peak | Beyond expected peak |
| Pattern | Ramp up, hold steady | Keep increasing until failure |
| Failure | A failed test | The expected outcome |
| Key metrics | Response time, throughput, errors | Breaking point, failure mode, recovery time |
| Typical timing | Before each major release or event | After the system is stable under load |
| Result | Pass or fail against targets | A capacity limit and a list of weak points |
What is load testing?
Load testing simulates the realistic traffic you expect, and measures how the system behaves. It needs a target to test against, for example: “at 500 concurrent users, 95% of checkout requests finish within 800 ms, with under 1% errors.”
Here is how that could look as a k6 script. k6 tests are written in JavaScript, and thresholds turn your targets into a pass or fail result:
import http from "k6/http";
import { sleep } from "k6";
export const options = {
stages: [
{ duration: "5m", target: 500 }, // ramp up
{ duration: "20m", target: 500 }, // hold at expected peak
{ duration: "2m", target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ["p(95)<800"],
http_req_failed: ["rate<0.01"],
},
};
export default function () {
http.get("https://staging.example.com/products");
sleep(1); // think time, as real users pause
}
The sleep matters. Without think time, each virtual user hammers the server continuously, producing far more traffic than the same number of real people would.
What is stress testing?
Stress testing deliberately goes beyond expected levels. You are not trying to pass. You want answers to questions such as:
- At what level does performance start to fall apart?
- Which component fails first: the database, the application servers or a third-party call?
- Does it fail gracefully with clear errors, or does it crash and lose data?
- Does it recover by itself when traffic drops?
export const options = {
stages: [
{ duration: "5m", target: 500 }, // expected peak
{ duration: "5m", target: 1000 }, // beyond it
{ duration: "5m", target: 2000 }, // further
{ duration: "5m", target: 4000 }, // likely to break
{ duration: "10m", target: 0 }, // recovery: does it come back?
],
};
That final recovery stage is frequently forgotten, and it may be the most valuable one. A system that handles failure well but never recovers without a manual restart still means an outage for someone to fix in the middle of the night. Ideally, under extreme load, a system should shed work in a controlled way, returning clear 503 responses instead of timing out.
Spike, soak and other types
Load and stress tests belong to a broader family of performance testing:
| Type | Pattern | What it finds |
|---|---|---|
| Smoke (performance) | A few users, briefly | Whether the script and system work at all |
| Load | Expected peak, held | Whether targets are met |
| Stress | Rising past capacity | Limits and failure modes |
| Spike | A sudden jump, then a drop | Autoscaling and queue behaviour |
| Soak (endurance) | Normal load for hours | Memory leaks, connections that run out |
| Capacity | Gradual increase | The maximum load within your targets |
Soak tests catch problems that short tests never see. A small memory leak can look harmless for twenty minutes, and only bring the service down after two days. Spike tests suit events such as flash sales, where traffic does not build gradually.
Metrics that matter
| Metric | Meaning | Watch for |
|---|---|---|
| Response time (percentiles) | How long requests take | p95 and p99 rising |
| Throughput | Requests handled per second | Flattening as users increase |
| Error rate | Percentage of failed requests | Any sudden rise |
| CPU and memory | Server resource use | Saturation, or steady growth |
| Database | Slow queries, connection pool use | Pool exhaustion, lock waits |
| Queue depth | Work waiting to be processed | Growth that never drains |
Why averages mislead
Averages hide the slow requests that real people complain about. To show this, we generated 100 simulated response times, where 95 were fast (80 to 200 ms) and 5 were very slow (2.5 to 4 seconds), then calculated the common statistics:
import math, random, statistics
random.seed(3)
times = [random.randint(80, 200) for _ in range(95)] \
+ [random.randint(2500, 4000) for _ in range(5)]
def pct(values, p):
values = sorted(values)
return values[math.ceil(p / 100 * len(values)) - 1]
print("avg", round(statistics.mean(times)), "p50", pct(times, 50),
"p95", pct(times, 95), "p99", pct(times, 99), "max", max(times))
# avg 302 p50 150 p95 199 p99 3740 max 3896
Look closely at those numbers. The average of 302 ms describes nobody. Typical requests took about 150 ms, while the slow ones took over 3 seconds. Even p95 looks healthy at 199 ms, and only p99 reveals the problem. A figure like p95 means 95% of requests were this fast or faster.
And one percent of requests is not a rare event. If a page makes 50 requests, a slow p99 affects a large share of page views. Set targets on the percentiles that matter to you, and look at several together.
Tip: Look for the “knee” in your graphs. As load increases, response times stay flat for a while, then suddenly climb, while throughput stops growing. That point is your practical capacity, and it often comes well before anything actually crashes.
Tools
| Tool | Scripting | Known for |
|---|---|---|
| k6 | JavaScript | Developer-friendly, thresholds suit CI |
| JMeter | GUI and XML | Mature, supports many protocols |
| Gatling | Scala, Java, Kotlin and others | Code-based, efficient |
| Locust | Python | Simple, Python-based user behaviour |
Any of these can run both load and stress tests. Pick the one that fits your team’s skills, rather than chasing a feature list. To use one in your pipeline, see what is CI/CD.
How to plan a test
- Set goals. Use real analytics to find your peak traffic, and define targets.
- Choose journeys. Model realistic user behaviour, such as browse, search and checkout, in realistic proportions. Hitting only the home page proves little.
- Use a realistic environment. A tiny staging server cannot predict production. Note any differences.
- Prepare data. Use realistic volumes, because a query that is fast on 100 rows may crawl on 10 million.
- Warn people. Tell your operations team and any third parties, and do not load test a payment provider by accident. Mock external services where appropriate.
- Start small. Run a small smoke test first to confirm that your script works.
- Monitor everything. Without server metrics, you will see that it slowed down, but not why.
- Repeat. Run it again after each fix, and keep a baseline to compare against.
Reading the results
| Symptom | Possible cause |
|---|---|
| Slow responses, but low CPU | Waiting on a database, lock or external service |
| Database CPU is high | Missing indexes or inefficient queries |
| Sudden connection errors | Connection pool or file limit reached |
| Memory climbs steadily | A leak, or an unbounded cache |
| Load generator CPU is high | The test tool is the bottleneck, not your system |
Databases are a common bottleneck, so if queries are the issue, read our guide to SQL query optimization. That final row of the table is an important trap: always check the health of the machine generating the load as well.
Common mistakes
- Confusing users with requests. Be clear whether a target means concurrent users or requests per second.
- Leaving out think time. This inflates traffic and gives misleading results.
- Testing too late. A week before launch leaves no time to fix architecture problems.
- Using a single endpoint. Real traffic mixes reads, writes and searches.
- Caching hides everything. Identical repeated requests may only test your cache, so vary the data.
- Testing once. Performance changes with every release, so run tests regularly.
Final thoughts
Run load tests to confirm that you can handle the traffic you expect, and stress tests to learn what happens when you cannot. Start small: choose your most important user journey, set one clear target, and run a short test against staging with monitoring on. That first run usually teaches you something, and you can build from there.
Frequently asked questions
What is the main difference between load and stress testing?
Load testing checks behaviour under expected traffic against set targets. Stress testing goes beyond capacity to find the breaking point and how the system recovers.
Should I run load testing or stress testing first?
Load test first. Once the system meets its targets under expected traffic, run stress tests to explore its limits. Stress results mean little on an unstable system.
What is the difference between a stress test and a spike test?
A stress test typically raises load step by step until failure. A spike test jumps suddenly to a high level, to see how the system copes with abrupt change.
What is p95 response time?
It means 95% of requests finished at or below that time. It describes the experience of most users better than an average does.
Can I run load tests on production?
You can, but carefully, with permission, monitoring and a way to stop quickly. Most teams begin with a production-like staging environment to reduce the risk to real users.
Keep learning
Need help with testing, WordPress or SEO?
We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.