k6-load-testing
k6 Load Testing
Install and Setup
brew install k6 # macOS
sudo apt-get install k6 # Debian/Ubuntu (after adding k6 repo)
choco install k6 # Windows
docker run --rm -i grafana/k6 run - <script.js # Docker
k6 version # verify
Basic Test Script
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = { vus: 10, duration: '30s' };
export default function () {
const getRes = http.get('https://test-api.k6.io/public/crocodiles/');
check(getRes, { 'GET status 200': (r) => r.status === 200 });
const payload = JSON.stringify({ name: 'test', sex: 'M', date_of_birth: '2020-01-01' });
const postRes = http.post('https://test-api.k6.io/anything', payload, {
headers: { 'Content-Type': 'application/json' },
});
check(postRes, { 'POST status 2xx': (r) => r.status >= 200 && r.status < 300 });
sleep(1);
}
Run with k6 run script.js.
Virtual Users and Duration
export const options = {
vus: 50, // 50 concurrent virtual users
duration: '5m', // run for 5 minutes
iterations: 1000, // or cap at 1000 total iterations across all VUs
};
Stages (Ramp Up, Steady State, Ramp Down)
export const options = {
stages: [
{ duration: '2m', target: 50 }, // ramp up
{ duration: '5m', target: 50 }, // steady state
{ duration: '2m', target: 0 }, // ramp down
],
};
Test Types
// Load test -- typical expected traffic
stages: [{ duration: '5m', target: 100 }, { duration: '10m', target: 100 }, { duration: '5m', target: 0 }]
// Stress test -- beyond normal capacity, stepped increases
stages: [
{ duration: '2m', target: 100 }, { duration: '5m', target: 100 },
{ duration: '2m', target: 200 }, { duration: '5m', target: 200 },
{ duration: '2m', target: 300 }, { duration: '5m', target: 300 },
{ duration: '5m', target: 0 },
]
// Soak test -- sustained load over hours
stages: [{ duration: '5m', target: 100 }, { duration: '8h', target: 100 }, { duration: '5m', target: 0 }]
// Spike test -- sudden surge
stages: [
{ duration: '1m', target: 10 }, { duration: '10s', target: 500 },
{ duration: '3m', target: 500 }, { duration: '10s', target: 10 }, { duration: '2m', target: 0 },
]
// Breakpoint test -- find system limits
{ executor: 'ramping-arrival-rate', startRate: 1, timeUnit: '1s', preAllocatedVUs: 500,
stages: [{ duration: '30m', target: 500 }] }
Checks (Assertions)
import { check } from 'k6';
check(res, {
'status is 200': (r) => r.status === 200,
'body contains expected text': (r) => r.body.includes('crocodile'),
'response time < 500ms': (r) => r.timings.duration < 500,
'content-type is JSON': (r) => r.headers['Content-Type'].includes('application/json'),
});
Thresholds (Pass/Fail Criteria)
export const options = {
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95th percentile < 500ms
http_req_failed: ['rate<0.01'], // error rate < 1%
checks: ['rate>0.99'], // 99% of checks pass
http_reqs: ['rate>100'], // throughput > 100 req/s
'http_req_duration{name:login}': ['p(95)<300'], // threshold on tagged request
},
};
k6 exits non-zero when any threshold fails, suitable for CI gating.
HTTP Methods
import http from 'k6/http';
export default function () {
http.get('https://api.example.com/items');
http.post('https://api.example.com/items',
JSON.stringify({ name: 'widget' }), { headers: { 'Content-Type': 'application/json' } });
http.put('https://api.example.com/items/1',
JSON.stringify({ name: 'updated' }), { headers: { 'Content-Type': 'application/json' } });
http.del('https://api.example.com/items/1');
// Multipart file upload
const file = open('/path/to/file.png', 'b');
http.post('https://api.example.com/upload', {
file: http.file(file, 'file.png', 'image/png'),
});
}
Headers and Authentication
// Bearer token
http.get('https://api.example.com/protected', {
headers: { Authorization: 'Bearer eyJhbGciOi...', 'Content-Type': 'application/json' },
});
// Login in setup, pass token to default function
export function setup() {
const res = http.post('https://api.example.com/login',
JSON.stringify({ username: 'user', password: 'pass' }),
{ headers: { 'Content-Type': 'application/json' } });
return { token: res.json('token') };
}
export default function (data) {
http.get('https://api.example.com/dashboard', {
headers: { Authorization: `Bearer ${data.token}` },
});
}
// Cookies
const jar = http.cookieJar();
jar.set('https://api.example.com', 'session_id', 'abc123');
Groups and Tags
import { group } from 'k6';
export default function () {
group('user flow', function () {
group('login', function () {
http.post('https://api.example.com/login',
JSON.stringify({ user: 'test', pass: 'test' }),
{ headers: { 'Content-Type': 'application/json' }, tags: { name: 'login' } });
});
group('browse', function () {
http.get('https://api.example.com/items', { tags: { name: 'list-items' } });
});
});
}
Tags enable filtering in thresholds and outputs: http_req_duration{name:login}.
Parameterization
import { SharedArray } from 'k6/data';
import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js';
const users = new SharedArray('users', function () {
return JSON.parse(open('./users.json')); // [{username: "a", password: "b"}, ...]
});
const csvData = new SharedArray('csv', function () {
return papaparse.parse(open('./data.csv'), { header: true }).data;
});
export default function () {
const user = users[Math.floor(Math.random() * users.length)];
http.post('https://api.example.com/login',
JSON.stringify({ username: user.username, password: user.password }),
{ headers: { 'Content-Type': 'application/json' } });
}
SharedArray loads data once and shares across VUs to reduce memory.
Custom Metrics
import { Counter, Gauge, Rate, Trend } from 'k6/metrics';
const errorCount = new Counter('custom_errors'); // cumulative count
const cacheHitRate = new Rate('cache_hits'); // percentage of true values
const pageLoadTime = new Trend('page_load_time'); // distribution (min/max/avg/p90/p95)
const activeConns = new Gauge('active_connections'); // last value
export default function () {
const res = http.get('https://api.example.com/');
pageLoadTime.add(res.timings.duration);
cacheHitRate.add(res.headers['X-Cache'] === 'HIT');
if (res.status !== 200) errorCount.add(1);
}
export const options = {
thresholds: { page_load_time: ['p(95)<600'], custom_errors: ['count<10'] },
};
Output
k6 run script.js # stdout summary
k6 run --out json=results.json script.js # JSON file
k6 run --out csv=results.csv script.js # CSV file
k6 run --out influxdb=http://localhost:8086/k6 script.js # InfluxDB
K6_CLOUD_TOKEN=token k6 run --out cloud script.js # Grafana Cloud
k6 run --out json=results.json --out influxdb=http://localhost:8086/k6 script.js # multiple
// Custom summary handler
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';
export function handleSummary(data) {
return {
stdout: textSummary(data, { indent: ' ', enableColors: true }),
'summary.json': JSON.stringify(data),
};
}
Browser Testing
import { browser } from 'k6/browser';
import { check } from 'k6';
export const options = {
scenarios: {
ui: { executor: 'shared-iterations', options: { browser: { type: 'chromium' } } },
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://test.k6.io/my_messages.php');
await page.locator('input[name="login"]').type('admin');
await page.locator('input[name="password"]').type('123');
await page.locator('input[type="submit"]').click();
const header = await page.locator('h2').textContent();
check(header, { 'logged in': (h) => h === 'Welcome, admin!' });
} finally { await page.close(); }
}
CI/CD Integration (GitHub Actions)
name: Load Test
on: [push, pull_request]
jobs:
k6-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/k6-action@v0.3.1
with:
filename: tests/load-test.js
env:
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
Thresholds serve as the quality gate -- the step fails when any threshold is breached.
Common Patterns
API Endpoint Test
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [{ duration: '1m', target: 50 }, { duration: '3m', target: 50 }, { duration: '1m', target: 0 }],
thresholds: { http_req_duration: ['p(95)<500'], http_req_failed: ['rate<0.01'] },
};
export default function () {
check(http.get('https://api.example.com/health'), { 'status 200': (r) => r.status === 200 });
sleep(1);
}
Login Flow Test
import http from 'k6/http';
import { check, sleep } from 'k6';
export default function () {
const loginRes = http.post('https://api.example.com/auth/login',
JSON.stringify({ email: 'user@test.com', password: 'password123' }),
{ headers: { 'Content-Type': 'application/json' }, tags: { name: 'login' } });
check(loginRes, { 'login succeeded': (r) => r.status === 200 });
const token = loginRes.json('access_token');
const profileRes = http.get('https://api.example.com/me', {
headers: { Authorization: `Bearer ${token}` }, tags: { name: 'profile' } });
check(profileRes, { 'profile loaded': (r) => r.status === 200 });
sleep(1);
}
File Upload Test
import http from 'k6/http';
import { check } from 'k6';
const testFile = open('/path/to/test-file.pdf', 'b');
export default function () {
const res = http.post('https://api.example.com/upload', {
file: http.file(testFile, 'report.pdf', 'application/pdf'),
});
check(res, { 'upload ok': (r) => r.status === 200, 'under 5s': (r) => r.timings.duration < 5000 });
}
More from 1mangesh1/dev-skills-collection
curl-http
HTTP request construction and API testing with curl and HTTPie. Use when user asks to "test API", "make HTTP request", "curl POST", "send request", "test endpoint", "debug API", "upload file", "check response time", "set auth header", "basic auth with curl", "send JSON", "test webhook", "check status code", "follow redirects", "rate limit testing", "measure API latency", "stress test endpoint", "mock API response", or any HTTP calls from the command line.
28database-indexing
Database indexing internals, index type selection, query plan analysis, and write-overhead tradeoffs across PostgreSQL, MySQL, and MongoDB. Use when user asks to "optimize queries", "create indexes", "fix slow queries", "read EXPLAIN output", "reduce query time", "index strategy", "database performance", "composite index", "covering index", "partial index", "index bloat", "unused indexes", or needs help diagnosing and resolving database performance problems.
13testing-strategies
Testing strategies, patterns, and methodologies across the full testing spectrum. Use when asked about unit tests, integration tests, e2e tests, test pyramid, mocking, test doubles, TDD, property-based testing, snapshot testing, test coverage, mutation testing, contract testing, performance testing, test data management, CI/CD testing, flaky tests, test anti-patterns, test organization, test isolation, test fixtures, test parameterization, or any testing strategy, approach, or methodology.
10secret-scanner
This skill should be used when the user asks to "scan for secrets", "find API keys", "detect credentials", "check for hardcoded passwords", "find leaked tokens", "scan for sensitive keys", "check git history for secrets", "audit repository for credentials", or mentions secret detection, credential scanning, API key exposure, token leakage, password detection, or security key auditing.
10terraform
Terraform infrastructure as code for provisioning, modules, state management, and workspaces. Use when user asks to "create infrastructure", "write Terraform", "manage state", "create module", "import resource", "plan changes", or any IaC tasks.
10kubernetes
Kubernetes and kubectl mastery for deployments, services, pods, debugging, and cluster management. Use when user asks to "deploy to k8s", "create deployment", "debug pod", "kubectl commands", "scale service", "check pod logs", "create ingress", or any Kubernetes tasks.
10