Cheatsheet: k6

Last updated 2026-10-06

Minimal Test

Minimal script: one GET request per iteration

import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  http.get('{{target_url}}');
  sleep(1);
}

Run a script

k6 run {{script_file}}

CLI Flags

Run with a fixed number of virtual users and duration

k6 run --vus 50 --duration 30s {{script_file}}

Run a fixed number of iterations instead of a duration

k6 run --iterations 100 {{script_file}}

Pass an environment variable into the script (read via __ENV)

k6 run -e BASE_URL=https://example.com {{script_file}}

Export results to a JSON file

k6 run --out json=results.json {{script_file}}

Stream results to an InfluxDB or Prometheus remote write endpoint

k6 run --out influxdb=http://localhost:8086/k6 {{script_file}}

Quiet mode: suppress progress bars (useful in CI logs)

k6 run --quiet {{script_file}}

Validate a script without running a full load test

k6 run --vus 1 --iterations 1 {{script_file}}

Load Profile (Stages)

Ramp up, hold, then ramp down

export const options = {
  stages: [
    { duration: '30s', target: 20 }, // ramp up to 20 VUs
    { duration: '1m',  target: 20 }, // hold at 20 VUs
    { duration: '10s', target: 0 },  // ramp down to 0
  ],
};

Thresholds

Fail the run if latency or error rate is too high

export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
    http_req_failed:   ['rate<0.01'], // error rate under 1%
    checks:            ['rate>0.99'], // 99%+ of checks pass
  },
};

Scope a threshold to requests tagged with a name

export const options = {
  thresholds: {
    'http_req_duration{name:login}': ['p(95)<300'],
  },
};

Checks (Assertions)

Assert response status and body

import http from 'k6/http';
import { check } from 'k6';

const res = http.get('{{target_url}}');
check(res, {
  'status is 200': (r) => r.status === 200,
  'body not empty': (r) => r.body.length > 0,
});

HTTP Requests

POST a JSON payload with headers

import http from 'k6/http';

const payload = JSON.stringify({ name: 'abc' });
const params = { headers: { 'Content-Type': 'application/json' } };
http.post('https://api.example.com/users', payload, params);

Send multiple requests in parallel with http.batch

import http from 'k6/http';

const responses = http.batch([
  ['GET', 'https://test.k6.io/'],
  ['GET', 'https://test.k6.io/news.php'],
]);

Scenarios & Executors

Constant arrival rate: fixed request rate regardless of response time

export const options = {
  scenarios: {
    steady: {
      executor: 'constant-arrival-rate',
      rate: 100,
      timeUnit: '1s',
      duration: '1m',
      preAllocatedVUs: 50,
    },
  },
};

Executor types at a glance

constant-vus          -- fixed number of VUs for a duration
ramping-vus           -- VUs ramp per stages (like options.stages)
constant-arrival-rate -- fixed iterations/sec, independent of VU response time
ramping-arrival-rate  -- iteration rate ramps per stages
per-vu-iterations     -- each VU runs a fixed number of iterations
shared-iterations     -- a fixed pool of iterations shared across VUs

Metrics Reference

http_req_duration -- total time for the request (DNS + connect + TLS + send + wait + receive)

http_req_duration

http_req_waiting -- time waiting for the response (a.k.a. TTFB)

http_req_waiting

http_req_failed -- rate of requests considered failed

http_req_failed

http_reqs -- total number of HTTP requests made

http_reqs

iterations -- total number of times the default function ran

iterations

vus / vus_max -- current and configured maximum virtual users

vus / vus_max

data_sent / data_received -- total bytes sent and received

data_sent / data_received

Custom Metrics

Track a custom timing with Trend

import http from 'k6/http';
import { Trend } from 'k6/metrics';

const waitingTime = new Trend('waiting_time');

export default function () {
  const res = http.get('{{target_url}}');
  waitingTime.add(res.timings.waiting);
}

Other custom metric types

Counter -- cumulative count, e.g. new Counter('errors')
Rate    -- percentage of true/false values, e.g. new Rate('success_rate')
Gauge   -- latest value only (not cumulative), e.g. new Gauge('queue_size')

Lifecycle

setup / default / teardown

export function setup() {
  // runs once, before VUs start -- e.g. fetch an auth token
  return { token: 'abc123' };
}

export default function (data) {
  // runs per-iteration, per-VU -- data.token is available here
}

export function teardown(data) {
  // runs once, after the test completes
}

Init code vs VU code

Code at the top level of the script (imports, options) runs once per VU during
initialization, before any iterations. Code inside the default function (or
setup/teardown) runs per-iteration. Opening files or requiring modules must
happen in init code -- it is not allowed inside the default function.

Quick Recipes

Reuse an auth token fetched once in setup()

import http from 'k6/http';

export function setup() {
  const res = http.post('https://api.example.com/login', JSON.stringify({ user: 'a', pass: 'b' }), {
    headers: { 'Content-Type': 'application/json' },
  });
  return { token: res.json('token') };
}

export default function (data) {
  http.get('https://api.example.com/profile', {
    headers: { Authorization: 'Bearer ' + data.token },
  });
}

Group related requests for clearer summary output

import http from 'k6/http';
import { group } from 'k6';

export default function () {
  group('checkout flow', () => {
    http.get('https://example.com/cart');
    http.post('https://example.com/checkout');
  });
}

Add think time between requests

import { sleep } from 'k6';

sleep(Math.random() * 3 + 1); // 1-4 second pause, mimics real user pacing

Load CSV test data once and share it across VUs with SharedArray

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 papaparse.parse(open('./users.csv'), { header: true }).data;
});

export default function () {
  const user = users[Math.floor(Math.random() * users.length)];
}

Run k6 in a GitHub Actions step

- name: Run k6 load test
  uses: grafana/k6-action@v0.3.1
  with:
    filename: script.js

FAQ