k6 Load Testing

k6 cheatsheet — write load tests in JavaScript, define VUs, thresholds, scenarios. k6 run script.js, k6 run --vus 100 --duration 30s. Full performance testing reference.

9 min read

What it is

k6 is an open-source load testing tool that lets you write load tests in JavaScript and run them from the command line, designed for developers.

Installation

Linux (using package manager):

# Debian/Ubuntu
sudo apt-get update
sudo apt-get install k6

# Fedora
sudo dnf install k6

Linux (downloading binary):

wget https://github.com/grafana/k6/releases/download/v0.40.0/k6-v0.40.0-linux_amd64.tar.gz
tar xvf k6-v0.40.0-linux_amd_64.tar.gz
sudo mv k6-v0.40.0-linux_amd64/k6 /usr/local/bin/
rm -rf k6-v0.40.0-linux_amd64*

macOS (using Homebrew):

brew install k6

Windows (using Chocolatey):

choco install k6

Windows (downloading binary):

# Download the latest release from https://k6.io/releases/
# Extract the k6.exe file to a directory in your PATH

Core Concepts

  • VUs (Virtual Users): Represent concurrent users interacting with your application. k6 simulates these VUs executing your test script.
  • Iterations: A single execution of the test script by a VU.
  • Durations: The total time for which the load test should run.
  • RPS (Requests Per Second): The target rate at which requests are sent to the target system.
  • Checks: Assertions made within your script to verify the correctness of responses (e.g., status code, content).
  • Thresholds: Conditions that, if not met during the test, will cause the test to fail (e.g., 99% of requests must complete in under 500ms).
  • Metrics: Data points collected during the test (e.g., request duration, success rate, data sent/received). k6 collects standard metrics and allows custom metrics.

Commands / Usage

Running a Test Script:

k6 run --vus 10 --duration 30s ./my_test.js

Runs the my_test.js script with 10 virtual users for 30 seconds.

Running a Test with a Target RPS:

k6 run --vus 10 --iterations 1000 ./my_test.js

Runs the my_test.js script for 1000 total iterations, distributed among VUs. This is useful for controlling the total work done. To control rate, use --rps.

k6 run --vus 10 --rps 50 ./my_test.js

Runs the my_test.js script with 10 VUs, aiming for a total rate of 50 requests per second.

Running a Test with Thresholds:

k6 run --vus 10 --duration 1m --summary-trend-stats "avg,min,max,med,p(95),p(99)" --out json=results.json --outף html=report.html --outףinfluxdb=http://localhost:8086/k6 my_test.js

Runs the my_test.js script, outputs summary trend statistics, saves results to results.json, report.html, and sends data to InfluxDB.

Running Tests with Options from a File:

k6 run --config k6-config.json ./my_test.js

Runs the my_test.js script using configuration options defined in k6-config.json.

Running Tests with Environment Variables:

TARGET_URL=https://test.k6.io k6 run --vus 10 --duration 10s ./my_test.js

Sets the TARGET_URL environment variable to https://test.k6.io for use within the my_test.js script.

Running Tests with Stages (Ramping):

k6 run --stages '
  [
    {"duration": "30s", "target": 20},
    {"duration": "1m", "target": 20},
    {"duration": "10s", "target": 0}
  ]
' ./my_test.js

Runs the test with a gradual ramp-up to 20 VUs over 30 seconds, stays at 20 VUs for 1 minute, then ramps down to 0 VUs over 10 seconds.

Listing Available Options:

k6 run --help

Displays detailed help information for the run command and its flags.

Inspecting a Test Script:

k6 run --log-level debug --quiet --no-thresholds --no-checks ./my_test.js

Executes the script with debug logging, suppresses output unless there’s an error, and ignores thresholds and checks to see the raw script behavior.

Exporting k6 Results to Other Formats:

k6 run --out json=output.json ./my_test.js
k6 run --out html=report.html ./my_test.js
k6 run --out csv=output.csv ./my_test.js
k6 run --out datadog=api_key=YOUR_API_KEY,site=YOUR_SITE ./my_test.js
k6 run --out influxdb=http://localhost:8086/k6 ./my_test.js

Runs the test and exports results in JSON, HTML, CSV, Datadog, or InfluxDB format.

Running a Test in Distributed Mode (k6 Cloud):

k6 cloud ./my_test.js --token YOUR_CLOUD_TOKEN --project my-project --name "My Distributed Test"

Uploads and runs the my_test.js script on k6 Cloud, using your provided API token, project, and test name.

Managing k6 Extensions:

k6 extension build ./my_extension_dir
k6 extension init ./my_extension_dir

Builds or initializes a k6 extension.

Version Check:

k6 version

Displays the installed k6 version.


General Flags (apply to most commands)

  • --help: Show help for a command.
  • --config <file>: Load configuration from a JSON file.
  • --out <format>=<params>: Output results to a specific format (e.g., json=results.json, html=report.html). Multiple --out flags can be used.
  • --log-level <level>: Set the logging level (e.g., debug, info, warn, error).
  • --quiet: Suppress non-error output.
  • --no-color: Disable colored output.
  • --http-debug <filter>: Print HTTP request/response details (e.g., full, headers, body).

run Command Specific Flags

  • --vus <number>: Number of virtual users.
  • --duration <duration>: Duration of the test (e.g., 30s, 5m).
  • --iterations <number>: Total number of iterations to perform.
  • --rps <number>: Target requests per second.
  • --stages '<json_array>': Define stages for gradual load changes.
  • --max-redirects <number>: Maximum number of HTTP redirects to follow.
  • --insecure-skip-tls-verify: Skip TLS certificate verification (use with caution).
  • --ssl-skip: Alias for --insecure-skip-tls-verify.
  • --arkeit <file>: Path to a k6 ARKIT file for advanced configuration.
  • --system <driver>: Specify the system driver for execution (e.g., local).
  • --no-usage-report: Disable sending anonymous usage data to k6.
  • --no-thresholds: Do not fail the test if thresholds are not met.
  • --no-checks: Do not fail the test if checks fail.
  • --summary-trend-stats <stats>: Comma-separated list of trend statistics to display (e.g., avg,min,max,med,p(95),p(99)).
  • --summary-time-unit <unit>: Unit for time in summary output (e.g., ms, s).
  • --executor <executor>: Specify the VU code executor (e.g., per-vu, shared-iterations, constant-vus, ramping-vus).
  • --graceful-stop <duration>: Duration to wait for VUs to finish their current iteration before shutting down.
  • --graceful-ramp-down <duration>: Duration to wait for VUs to finish their current iteration during a ramp-down.
  • --env <key>=<value>: Set environment variables for the test script.

browser Command Specific Flags

  • --browser-ui: Enable browser UI for visual feedback.
  • --browser-timeout <duration>: Timeout for browser actions.
  • --browser-executable-path <path>: Path to the browser executable.
k6 browser ./my_browser_test.js

Runs a browser-based test script.

Common Patterns

Load Testing an API Endpoint:

# my_api_test.js
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  vus: 20,
  duration: '1m',
};

export default function () {
  http.get('https://api.example.com/users');
  sleep(1);
}

Run with:

k6 run ./my_api_test.js

Ramping Up Users and Checking Response Times:

# my_ramp_test.js
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 50 }, // ramp up to 50 users over 1 minute
    { duration: '2m', target: 50 }, // stay at 50 users for 2 minutes
    { duration: '30s', target: 0 },  // ramp down to 0 users over 30 seconds
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests should be below 500ms
    http_req_failed: ['rate<0.01'],   // error rate should be less than 1%
  },
};

export default function () {
  http.get('https://ecommerce.example.com/products');
  sleep(Math.random() * 2); // Random sleep between 0 and 2 seconds
}

Run with:

k6 run --out html=report.html ./my_ramp_test.js

Testing a Login Flow with Custom Metrics:

# my_login_test.js
import http from 'k6/http';
import { sleep, check, fail } from 'k6';
import { Counter, Rate, Trend } from 'k6/metrics';

const loginFailures = new Counter('login_failures');
const loginDuration = new Trend('login_duration');

export const options = {
  vus: 10,
  duration: '30s',
};

export default function () {
  const payload = JSON.stringify({
    username: 'testuser',
    password: 'password123',
  });

  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  const res = http.post('https://auth.example.com/login', payload, params);

  const isSuccess = check(res, {
    'login status is 200': (r) => r.status === 200,
    'response body contains token': (r) => r.body.includes('token'),
  });

  if (!isSuccess) {
    loginFailures.add(1);
    // Optionally fail the test immediately if critical
    // fail('Login failed or token not found');
  } else {
    loginDuration.add(res.timings.duration);
  }

  sleep(1);
}

Run with:

k6 run --out influxdb=http://localhost:8086/k6 ./my_login_test.js

Parameterized Tests using Environment Variables:

# my_param_test.js
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  vus: 5,
  duration: '15s',
};

export default function () {
  const targetUrl = __ENV.TARGET_URL || 'https://default.example.com';
  http.get(`${targetUrl}/health`);
  sleep(1);
}

Run with:

TARGET_URL=https://api.prod.example.com k6 run ./my_param_test.js

Using shared-iterations Executor for Fixed Number of Total Iterations:

# my_fixed_iterations_test.js
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  iterations: 1000,
  vus: 10,
  // executor defaults to 'per-vu' which means each VU runs iterations until total is met.
  // 'shared-iterations' is useful when you want to ensure a fixed total number of requests regardless of VU speed.
  // executor: 'shared-iterations',
};

export default function () {
  http.get('https://service.example.com/status');
  sleep(0.5);
}

Run with:

k6 run ./my_fixed_iterations_test.js

Running Tests with Different Executors:

# Constant VUs (default if not specified)
k6 run --executor constant-vus --vus 10 --duration 1m ./my_test.js

# Ramping VUs
k6 run --executor ramping-vus --stages '[{"duration": "1m", "target": 50}]' ./my_test.js

# Shared Iterations
k6 run --executor shared-iterations --iterations 1000 --vus 10 ./my_test.js

# Per-VU Iterations (default)
k6 run --executor per-vu --iterations 100 --vus 10 ./my_test.js

Gotchas

  • sleep() is Crucial: Without sleep(), VUs will run as fast as possible, potentially overwhelming your system and skewing results. Use sleep(1) or sleep(Math.random() * N) to simulate realistic user think times.
  • http_req_failed is a Rate: The default threshold http_req_failed: ['rate<0.01'] means less than 1% of requests should fail. It’s a rate, not a count.
  • Thresholds vs. Checks: Checks assert conditions within a single iteration and report pass/fail. Thresholds are global conditions evaluated over the entire test run and determine the test’s exit code. A test can pass locally but fail the CI/CD pipeline if thresholds are not met.
  • --duration vs. --iterations: --duration specifies how long the test runs. --iterations specifies the total number of iterations to complete. If the VUs can’t complete all iterations within the duration, the test will stop at the duration.
  • --rps is a Target: The --rps flag aims for a specific rate, but the actual achieved RPS can be lower due to network latency, server response times, or the number of VUs not being sufficient to generate that rate.
  • Environment Variables in Scripts: Access environment variables using __ENV.VARIABLE_NAME. These are injected into the k6 process.
  • Browser Testing Limitations: Browser tests are significantly more resource-intensive than protocol-level tests. Running many browser VUs concurrently requires substantial local resources or k6 Cloud.
  • Default VU Executor: If not specified, k6 run defaults to the per-vu executor. This means each VU will run iterations until the total iterations count is met or the duration is reached. For specific control over iteration distribution, use shared-iterations or ramping-vus.
  • Graceful Shutdown: When stopping a test (e.g., by pressing Ctrl+C or reaching the end of a duration), k6 attempts a graceful shutdown. The --graceful-stop flag allows you to configure how long k6 waits for VUs to finish their current iteration before forcibly terminating them. This is important for ensuring that all in-flight requests have a chance to complete.