Every year, stores discover their limits at the worst possible moment: during a sale, a TV mention or Black Friday. A load test lets you find those limits in advance, on your terms.
This guide shows how to load test an online store with k6, a free, open-source load testing tool, and how to interpret the results.
What a load test tells you
- How many concurrent shoppers your store can handle
- Which pages slow down first (usually search, cart and checkout)
- Where the bottleneck is: PHP workers, database, search engine, external APIs
- Whether caching works under load
- What error rate customers would see at peak
Types of tests
| Test | Purpose |
|---|---|
| Smoke test | Small load to check the script works |
| Load test | Expected peak traffic for a sustained period |
| Stress test | Increase until the system breaks, to find the limit |
| Spike test | Sudden jump in traffic (email campaign, TV mention) |
| Soak test | Normal load for hours, to find memory leaks and slow degradation |
Before you start
- Test against staging or a production-like environment with the same hardware and configuration. If you must test production, do it at a quiet time and inform your host
- Tell your hosting provider and CDN, otherwise your test may be blocked as an attack
- Disable real payments and emails on the test environment
- Exclude test traffic from analytics
- Know your target: use your analytics to estimate peak sessions, page views per minute and orders per hour, then add a safety margin
Install k6
On Ubuntu/Debian, macOS and Windows, k6 can be installed from Grafana’s package repositories, with Homebrew (brew install k6) or via Docker. Check the official k6 documentation for your system.
Step 1: A simple browsing scenario
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<1500'],
},
};
const BASE = 'https://staging.example.com';
export default function () {
let res = http.get(`${BASE}/`);
check(res, { 'home 200': (r) => r.status === 200 });
sleep(Math.random() * 3 + 2);
res = http.get(`${BASE}/shoes/trail-running.html`);
check(res, { 'category 200': (r) => r.status === 200 });
sleep(Math.random() * 3 + 2);
res = http.get(`${BASE}/trail-shoe-x200.html`);
check(res, { 'product 200': (r) => r.status === 200 });
sleep(Math.random() * 5 + 3);
}
Run it:
k6 run browse.js
The sleep calls simulate real users reading pages. Without them, 50 virtual users behave like hundreds of real visitors.
Step 2: Add realistic shopper behaviour
Real traffic is a mix. A typical split could be:
- Most visitors browse home, categories and products
- A smaller share searches
- Fewer add to cart
- A small share starts checkout
Use separate scenarios with different weights:
export const options = {
scenarios: {
browsers: { executor: 'ramping-vus', exec: 'browse',
stages: [{ duration: '3m', target: 160 }, { duration: '10m', target: 160 }] },
searchers: { executor: 'ramping-vus', exec: 'search',
stages: [{ duration: '3m', target: 30 }, { duration: '10m', target: 30 }] },
buyers: { executor: 'ramping-vus', exec: 'addToCart',
stages: [{ duration: '3m', target: 10 }, { duration: '10m', target: 10 }] },
},
};
Base the split on your own analytics funnel.
Step 3: Test add-to-cart and checkout
Cart and checkout are the most important and least cacheable parts. Add-to-cart usually requires a session cookie and a form key or nonce, which you must extract from the product page:
- Magento: the
form_keyfrom the page or cookie - WooCommerce: add-to-cart via
?add-to-cart=PRODUCT_IDor the Store API - Shopify: the AJAX Cart API (
/cart/add.js). Note: Shopify runs the infrastructure; check Shopify’s policies before load testing a Shopify store, and focus on your own apps and integrations instead
k6 keeps cookies per virtual user automatically. Use check() to verify the cart really contains the product.
For checkout, test up to the payment step with a test payment method, or mock the payment provider on staging. Never generate real orders against real payment providers.
Step 4: Monitor the server during the test
The k6 output shows response times and errors. You also need to see why:
- CPU and memory per server
- PHP-FPM active processes and queue (
pm.status_path) - Database: connections, slow queries, locks
- Cache hit rate (Varnish, CDN, Redis)
- Search engine load (OpenSearch/Elasticsearch)
- External API response times (shipping, payment, tax)
An APM tool such as New Relic, Tideways or Blackfire makes finding the bottleneck much easier.
Reading the results
| Metric | What to look for |
|---|---|
http_req_duration p(95) |
95% of requests faster than this. Watch how it grows with load |
http_req_failed |
Error rate. Anything above a fraction of a percent needs attention |
iterations |
Completed user journeys |
| Server CPU / PHP workers | Saturated workers mean requests start queueing |
The breaking point is where response times rise sharply or errors appear. Your expected peak should be well below that point.
Common bottlenecks found by load tests
- Too few PHP-FPM workers, or too many for the available memory
- Full page cache not working for certain pages or parameters
- Slow database queries on layered navigation or search
- Session storage on disk instead of Redis (see Redis object cache guide)
- External API calls in the checkout (shipping rates, address validation)
- Heavy plugins or extensions on cart pages
After fixing: test again
Load testing is a loop: test, find bottleneck, fix, test again. Plan time for at least two or three rounds before your peak.
Frequently asked questions
Can I load test production?
It is possible, but risky. Prefer a production-like staging environment. If you test production, do it at night, start small and inform your host.
How many virtual users do I need?
Calculate from analytics: peak concurrent sessions, with realistic think times, plus a safety margin. Virtual users with sleeps are not the same as page views per second.
Is k6 free?
The k6 command-line tool is open source. Grafana also offers a cloud service for larger distributed tests.
Want a professional load test?
I run load tests for Magento and WooCommerce stores, find the bottlenecks and fix them before peak season. See my servers and backups service or request a load test.