Skip to main content
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

1

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 }
2

Steady State

Maintain peak load to measure sustained performance. This is where you gather meaningful metrics.
{ duration: '10m', target: 100 }
3

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

Key Performance Indicators

  • 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 },
  ],
};

Build docs developers (and LLMs) love