k6 Load Testing
Script Structure & Basic HTTP
import http from 'k6/http';
import { check, sleep } from 'k6';
// Options — configure VUs and duration
export const options = {
vus: 10, // virtual users
duration: '30s', // test duration
};
// Default function runs per VU per iteration
export default function () {
const res = http.get('https://httpbin.org/get');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains json': (r) => r.headers['Content-Type'].includes('json'),
});
sleep(1); // think time between iterations
}
// Run: k6 run script.js
// Run with override: k6 run --vus 20 --duration 60s script.js
Stages — Ramp Up / Down
export const options = {
stages: [
{ duration: '30s', target: 10 }, // ramp up to 10 VUs
{ duration: '1m', target: 10 }, // stay at 10 VUs
{ duration: '20s', target: 50 }, // spike to 50 VUs
{ duration: '1m', target: 50 }, // sustain spike
{ duration: '30s', target: 0 }, // ramp down to 0
],
};
// POST request with JSON body
export default function () {
const payload = JSON.stringify({
username: 'testuser',
password: 'password123',
});
const params = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${__ENV.AUTH_TOKEN}`,
},
timeout: '10s',
};
const res = http.post('https://api.example.com/login', payload, params);
check(res, {
'login succeeds': (r) => r.status === 200,
'token present': (r) => r.json('token') !== undefined,
});
sleep(Math.random() * 2 + 1); // random think time 1-3s
}
Thresholds — Pass/Fail Criteria
export const options = {
thresholds: {
// HTTP error rate < 1%
http_req_failed: ['rate<0.01'],
// 95th percentile response time < 500ms
http_req_duration: ['p(95)<500'],
// 99th percentile < 1000ms
'http_req_duration{expected_response:true}': ['p(99)<1000'],
// Custom metric threshold
my_counter: ['count>1000'],
// Threshold with abort
http_req_failed: [{ threshold: 'rate<0.05', abortOnFail: true }],
},
};
// k6 exits with non-zero if thresholds fail
// Great for CI gates
Custom Metrics
import { Counter, Gauge, Rate, Trend } from 'k6/metrics';
const loginDuration = new Trend('login_duration');
const loginSuccessRate = new Rate('login_success_rate');
const activeUsers = new Gauge('active_users');
const totalRequests = new Counter('total_requests');
export default function () {
const start = Date.now();
const res = http.post('/api/login', JSON.stringify({ user: 'alice' }), {
headers: { 'Content-Type': 'application/json' },
});
loginDuration.add(Date.now() - start);
loginSuccessRate.add(res.status === 200);
totalRequests.add(1);
activeUsers.add(1);
sleep(1);
activeUsers.add(-1);
}
Scenarios
export const options = {
scenarios: {
// Constant VUs
constant_load: {
executor: 'constant-vus',
vus: 20,
duration: '2m',
},
// Ramping VUs
ramp_up: {
executor: 'ramping-vus',
stages: [
{ duration: '30s', target: 20 },
{ duration: '1m', target: 20 },
{ duration: '30s', target: 0 },
],
},
// Constant arrival rate (RPS)
constant_rps: {
executor: 'constant-arrival-rate',
rate: 100, // 100 requests per second
timeUnit: '1s',
duration: '2m',
preAllocatedVUs: 50,
},
// Shared iterations
smoke_test: {
executor: 'shared-iterations',
vus: 5,
iterations: 50,
},
},
};
CLI Commands & Output
# Basic run
k6 run script.js
# With environment variables
k6 run -e AUTH_TOKEN=abc123 script.js
# Output to JSON
k6 run --out json=results.json script.js
# Output to InfluxDB
k6 run --out influxdb=http://localhost:8086/k6 script.js
# Output to Grafana Cloud k6
k6 run --out cloud script.js
# Summary output
k6 run --summary-export=summary.json script.js
# Quiet mode (no progress bar)
k6 run -q script.js
# Built-in metrics output:
# http_req_duration — response time (min/avg/max/p90/p95)
# http_req_failed — error rate
# http_reqs — total requests
# vus — active virtual users
# data_received — bytes received