Load testing assesses how your system performs under expected normal and peak load conditions. It helps you understand capacity, identify bottlenecks, and validate that your system meets performance requirements.
Purpose
Load tests help you:
Measure response times under typical load
Identify performance bottlenecks
Validate system capacity and scalability
Ensure SLAs are met under normal conditions
Configuration Pattern
Load tests use ramping stages to gradually increase load:
import http from 'k6/http' ;
import { check , sleep } from 'k6' ;
export const options = {
stages: [
{ duration: '5m' , target: 100 }, // Ramp up to 100 users
{ duration: '10m' , target: 100 }, // Stay at 100 users
{ duration: '5m' , target: 0 }, // Ramp down to 0 users
],
thresholds: {
http_req_duration: [ 'p(95)<500' , 'p(99)<1000' ],
http_req_failed: [ 'rate<0.01' ],
},
};
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com' );
check ( res , {
'status is 200' : ( r ) => r . status === 200 ,
});
sleep ( 1 );
}
The stages configuration above creates a classic load test profile: gradual ramp-up, steady state, and controlled ramp-down.
Using the Ramping VUs Executor
The ramping-vus executor provides fine-grained control over VU ramping:
export const options = {
scenarios: {
load_test: {
executor: 'ramping-vus' ,
startVUs: 0 ,
stages: [
{ duration: '2m' , target: 50 }, // Ramp up
{ duration: '5m' , target: 50 }, // Steady state
{ duration: '2m' , target: 100 }, // Step up
{ duration: '5m' , target: 100 }, // Higher steady state
{ duration: '2m' , target: 0 }, // Ramp down
],
gracefulRampDown: '30s' ,
},
},
};
Ramp Up 2-5 minutes to target load
Steady State 5-15 minutes at peak load
Ramp Down 2-5 minutes to zero
Load Test Stages Explained
Ramp Up
Gradually increase VUs to give the system time to scale and warm up caches. This prevents overwhelming the system immediately. { duration : '5m' , target : 100 }
Steady State
Maintain peak load to measure sustained performance. This is where you gather meaningful metrics. { duration : '10m' , target : 100 }
Ramp Down
Gracefully reduce load to allow the system to recover and finish processing requests. { duration : '5m' , target : 0 }
Advanced Load Testing Pattern
Multi-stage load test with multiple peaks:
import http from 'k6/http' ;
import { check , sleep } from 'k6' ;
export const options = {
stages: [
// Morning traffic
{ duration: '2m' , target: 50 },
{ duration: '5m' , target: 50 },
// Lunch peak
{ duration: '2m' , target: 100 },
{ duration: '5m' , target: 100 },
// Afternoon dip
{ duration: '2m' , target: 50 },
{ duration: '5m' , target: 50 },
// Evening peak
{ duration: '2m' , target: 150 },
{ duration: '5m' , target: 150 },
// Ramp down
{ duration: '5m' , target: 0 },
],
};
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com/api/pizza' );
check ( res , { 'status is 200' : ( r ) => r . status === 200 });
sleep ( 1 );
}
Using Constant Arrival Rate
For more realistic load patterns, use arrival rate instead of VUs:
export const options = {
scenarios: {
load_test: {
executor: 'constant-arrival-rate' ,
rate: 100 , // 100 iterations per second
timeUnit: '1s' ,
duration: '10m' ,
preAllocatedVUs: 50 , // Initially allocated VUs
maxVUs: 200 , // Max VUs if needed
},
},
};
Arrival rate executors maintain a constant request rate regardless of response times, providing more realistic load simulation.
Metrics to Monitor
Response time percentiles : p(95), p(99)
Request rate : Requests per second
Error rate : Failed requests percentage
Throughput : Data transferred
Setting Thresholds
export const options = {
thresholds: {
// 95% of requests should be below 500ms
http_req_duration: [ 'p(95)<500' ],
// 99% of requests should be below 1s
'http_req_duration{name:http://quickpizza.grafana.com/api/pizza}' : [ 'p(99)<1000' ],
// Error rate should be below 1%
http_req_failed: [ 'rate<0.01' ],
// 95% of checks should pass
checks: [ 'rate>0.95' ],
},
};
Best Practices
Load Test Duration
Minimum Duration Run for at least 15-20 minutes to observe steady-state behavior
Recommended Duration 30-60 minutes for comprehensive testing
Realistic User Behavior
import { sleep } from 'k6' ;
import http from 'k6/http' ;
export default function () {
// Browse homepage
http . get ( 'https://quickpizza.grafana.com' );
sleep ( 2 ); // Think time
// View products
http . get ( 'https://quickpizza.grafana.com/api/pizza' );
sleep ( 3 );
// Add to cart
http . post ( 'https://quickpizza.grafana.com/api/cart' , {
pizzaId: 1 ,
quantity: 2 ,
});
sleep ( 1 );
}
Add sleep() calls to simulate realistic user think time between requests. Without sleep, k6 will execute requests as fast as possible.
When to Use
Pre-production : Validate performance before releases
Capacity planning : Determine maximum sustainable load
SLA validation : Ensure performance requirements are met
Baseline establishment : Create performance benchmarks
Common Patterns
Gradual Load Increase
Test multiple load levels in one test:
export const options = {
stages: [
{ duration: '2m' , target: 25 }, // 25% load
{ duration: '5m' , target: 25 },
{ duration: '2m' , target: 50 }, // 50% load
{ duration: '5m' , target: 50 },
{ duration: '2m' , target: 100 }, // 100% load
{ duration: '5m' , target: 100 },
{ duration: '2m' , target: 0 },
],
};