Github url


by loadimpact

loadimpact /k6

A modern load testing tool, using Go and JavaScript -

6.8K Stars 388 Forks Last release: 4 months ago (v0.26.2) GNU Affero General Public License v3.0 3.5K Commits 54 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:


Like unit testing, for performance

A modern load testing tool for developers and testers in the DevOps era.

Github releaseBuild statusGo Report CardCodecov branch
@k6_io on TwitterSlack channel

Download · Install · Documentation · Community


k6 is a modern load testing tool, building on Load Impact's years of experience in the load and performance testing industry. It provides a clean, approachable scripting API, local and cloud execution, and flexible configuration.

This is how load testing should look in the 21st century.



There's even more! See all features available in k6.



Install with Homebrew by running:

brew install k6


You can manually download and install the [official


installation package]( or, if you use the chocolatey package manager, follow these instructions to set up the k6 repository.


For Debian-based Linux distributions, you can install k6 from the private deb repo like this:

sudo apt-key adv --keyserver hkp:// --recv-keys 379CE192D401AB61 echo "deb stable main" | sudo tee -a /etc/apt/sources.list sudo apt-get update sudo apt-get install k6

And for rpm-based ones like Fedora and CentOS:

wget -O bintray-loadimpact-rpm.repo sudo mv bintray-loadimpact-rpm.repo /etc/yum.repos.d/ sudo dnf install k6 # use yum instead of dnf for older distros


docker pull loadimpact/k6

Pre-built binaries & other platforms

If there isn't an official package for your operating system or architecture, or if you don't want to install a custom repository, you can easily grab a pre-built binary from the GitHub Releases page. Once you download and unpack the release, you can optionally copy the


binary it contains somewhere in your


, so you are able to run k6 from any location on your system.

Build from source

k6 is written in Go, so it's just a single statically-linked executable and very easy to build and distribute. To build from source you need Git and Go (1.12 or newer). Follow these instructions:

  • Run
    go get
    which will:
    • git clone the repo and put the source in
    • build a
      binary and put it in
  • Make sure you have
    in your
    (or copy the
    binary somewhere in your
    ), so you are able to run k6 from any location.
  • Tada, you can now run k6 using
    k6 run script.js

Running k6

k6 works with the concept of virtual users (VUs) that execute scripts - they're essentially glorified, parallel


loops. Scripts are written using JavaScript, as ES6 modules, which allows you to break larger tests into smaller and more reusable pieces, making it easy to scale tests across an organization.

Scripts must contain, at the very least, an exported


function - this defines the entry point for your VUs, similar to the


function in many languages. Let's create a very simple script that makes an HTTP GET request to a test website:

import http from "k6/http"; export default function() { let response = http.get(""); };

The script details and how we can extend and configure it will be explained below, but for now simply save the above snippet as a


file somewhere on your system. Assuming that you've installed k6 correctly, on Linux and Mac you can run the saved script by executing

k6 run script.js

from the same folder. For Windows the command is almost the same -

k6.exe run script.js


If you decide to use the k6 docker image, the command will be slightly different. Instead of passing the script filename to k6, a dash is used to instruct k6 to read the script contents directly via the standard input. This allows us to to avoid messing with docker volumes for such a simple single-file script, greatly simplifying the docker command:

docker run -i loadimpact/k6 run - <script.js>.

<p>In some situations it may also be useful to execute remote scripts. You can do that with HTTP<strong>S</strong> URLs in k6 by <a href="">importing them</a> in the script via their URL or simply specifying their URL in the CLI command: </p>
<pre class="">k6 run</pre> (k6 "knows" a bit about github and cdnjs URLs, so this command is actually shorthand for <pre class="">k6 run</pre>)

<p>For more information on how to get started running k6, please look at the <a href="">Running k6</a> documentation page. If you want to know more about making and measuring HTTP requests with k6, take a look <a href="">here</a> and <a href="">here</a>. And for information about the commercial k6 services like distributed cloud execution (the </p>
<pre class="">k6 cloud</pre> command) or Cloud Results (<pre class="">k6 run -o cloud</pre>), you can visit <a href=""></a> or view the <a href="">cloud documentation</a>.


<p>In this section we'll briefly explore some of the basic concepts and principles of how k6 works. If you want to learn more in-depth about the k6 scripting API, results output, and features, you can visit the full k6 documentation website at <a href=""></a>.</p>

<h3>Init and VU stages</h3>

<p>Earlier, in the <a href="">Running k6</a> section, we mentioned that scripts must contain a </p>
<pre class="">default</pre> function. <em>"Why not just run my script normally, from top to bottom"</em>, you might ask - the answer is: we do, but code <strong>inside</strong> and <strong>outside</strong> your <pre class="">default</pre> function can do different things.

<p>Each virtual user (VU) executes your script in a completely separate JavaScript runtime, parallel to all of the other running VUs. Code inside the </p>
<pre class="">default</pre> function is called <em>VU code</em>, and is run over and over, for as long as the test is running. Code outside of the <pre class="">default</pre> function is called <em>init code</em>, and is run only once per VU, when that VU is initialized.

<p>VU code can make HTTP and websocket requests, emit metrics, and generally do everything you'd expect a load test to do, with a few important exceptions - you can't load anything from your local filesystem, or import any other modules. This all has to be done from the <a href="">init code</a>.</p>

<p>There are two reasons for this. The first is, of course: performance. If you read a file from disk on every single script iteration, it'd be needlessly slow. Even if you cache the contents of the file and any imported modules, it'd mean the <em>first run</em> of the script would be much slower than all the others. Worse yet, if you have a script that imports or loads things based on things that can only be known at runtime, you'd get slow iterations thrown in every time you load something new. That's also the reason why we initialize <em>all</em> needed VUs before any of them starts the actual load test by executing the </p>
<pre class="">default</pre> function.

<p>But there's another, more interesting reason. By forcing all imports and file reads into the init context, we design for distributed execution. We know which files will be needed, so we distribute only those files to each node in the cluster. We know which modules will be imported, so we can bundle them up in an <a href="">archive</a> from the get-go. And, tying into the performance point above, the other nodes don't even need writable file systems - everything can be kept in-memory.</p>

<p>This means that if your script works when it's executed with </p>
<pre class="">k6 run</pre> locally, it should also work without any modifications in a distributed execution environment like <pre class="">k6 cloud</pre> (that executes it in the commercial <a href="">k6 cloud infrastructure</a>) or, in the future, with the <a href="">planned</a> k6 native cluster execution mode.

<h3>Script execution</h3>

<p>For simplicity, unlike many other JavaScript runtimes, a lot of the operations in k6 are synchronous. That means that, for example, the </p>
<pre class="">let response = http.get("")</pre> call from the <a href="">Running k6</a> example script will block the VU execution until the HTTP request is completed, save the <a href="">response information</a> in the <pre class="">response</pre> variable and only then continue executing the rest of the script - no callbacks and promises needed.

<p>This simplification works because k6 isn't just a single JavaScript runtime. Instead each VU independently executes the supplied script in its own separate and semi-isolated JavaScript runtime, in parallel to all of the other running VUs. This allows us to fully utilize modern multi-core hardware, while at the same time lowering the script complexity by having mostly synchronous functions. Where it makes sense, we also have in-VU parallelization as well, for example the <a href=""><pre class="">http.batch()</pre></a> function (which allows a single VU to make multiple simultaneous HTTP requests like a browser/real user would) or the <a href="">websocket</a> support.</p>

<p>As an added bonus, there's an actual <a href=""><pre class="">sleep()</pre> function</a>! And you can also use the VU separation to reuse data between iterations (i.e. executions of the </p>
<pre class="">default</pre> function) in the same VU:
<pre class="">js
var vuLocalCounter = 0;
export default function() {

<h3>Script options and execution control</h3>

<p>So we've mentioned VUs and iterations, but how are those things controlled?</p>

<p>By default, if nothing is specified, k6 runs a script with only 1 VU and for 1 iteration only. Useful for debugging, but usually not very useful when doing load testing. For actual script execution in a load test, k6 offers a lot of flexibility - there are a few different configuration mechanisms you can use to specify script options, and several different options to control the number of VUs and how long your script will be executed, among <a href="">other things</a>.</p>

<p>Let's say that you want to specify number of VUs in your script. In order of precedence, you can use any of the following configuration mechanisms to do it:
1. Command-line flags: </p>
<pre class="">k6 run --vus 10 script.js</pre>, or via the short <pre class="">-u</pre> flag syntax if we want to save 3 keystrokes (<pre class="">k6 run -u 10 script.js</pre>).
2. Environment variables: setting <pre class="">K6_VUS=20</pre> before you run the script with k6. Especially useful when using the <a href="">docker k6 image</a> and when running in containerized environments like Kubernetes.
3. Your script can <pre class="">export</pre> an <pre class="">options</pre> object that k6 reads and uses to set any options you want; for example, setting VUs would look like this:
    <pre class="">js
    export let options = {
        vus: 30,
    export default function() { /* ... do whatever ... */ }
    This functionality is very useful, because here you have access to key-value <a href="">environment variables</a> that k6 exposes to the script via the global <pre class="">__ENV</pre> object, so you can use the full power of JavaScript to do things like:
    <pre class="">js
    if (__ENV.script_scenario == "staging") {
        export let options = { /* first set of options */ };
    } else {
        export let options = { /* second set of options */ };
    Or any variation of the above, like importing different config files, etc. Also, having most of the script configuration right next to the script code makes k6 scripts very easily version-controllable.

<li>A global JSON config. By default k6 looks for it in the config home folder of the current user (OS-dependent, for Linux/BSDs k6 will look for <pre class="">config.json</pre> inside of <pre class="">${HOME}/.config/loadimpact/k6</pre>), though that can be modified with the <pre class="">--config</pre>/<pre class="">-c</pre> CLI flag.
It uses the same option keys as the exported <pre class="">options</pre> from the script file, so we can set the VUs by having <pre class="">config.json</pre> contain <pre class="">{ "vus": 1 }</pre>. Although it rarely makes sense to set the number of VUs there, the global config file is much more useful for storing things like login credentials for the different <a href="">outputs</a>, as used by the <pre class="">k6 login</pre> subcommand...</li>

<p>Configuration mechanisms do have an order of precedence. As presented, options at the top of the list can override configuration mechanisms that are specified lower in the list. If we used all of the above examples for setting the number of VUs, we would end up with 10 VUs, since the CLI flags have the highest priority. Also please note that not all of the available options are configurable via all different mechanisms - some options may be impractical to specify via simple strings (so no CLI/environment variables), while other rarely-used ones may be intentionally excluded from the CLI flags to avoid clutter - refer to <a href="">options docs</a> for more information.</p>

<p>As shown above, there are several ways to configure the number of simultaneous virtual users k6 will launch. There are also different ways to specify how long those virtual users will be running. For simple tests you can:
- Set the test duration by the </p>
<pre class="">--duration</pre>/<pre class="">-d</pre> CLI flag (or the <pre class="">K6_DURATION</pre> environment variable and the <pre class="">duration</pre> script/JSON option). For ease of use, <pre class="">duration</pre> is specified with human readable values like <pre class="">1h30m10s</pre> - <pre class="">k6 run --duration 30s script.js</pre>, <pre class="">k6 cloud -d 15m10s script.js</pre>, <pre class="">export K6_DURATION=1h</pre>, etc. If set to <pre class="">0</pre>, k6 wouldn't stop executing the script unless the user manually stops it.
- Set the total number of script iterations with the <pre class="">--iterations</pre>/<pre class="">-i</pre> CLI flag (or the <pre class="">K6_ITERATIONS</pre> environment variable and the <pre class="">iterations</pre> script/JSON option). k6 will stop executing the script whenever the <strong>total</strong> number of iterations (i.e. the number of iterations across all VUs) reaches the specified number. So if you have <pre class="">k6 run --iterations 10 --vus 10 script.js</pre>, then each VU would make only a single iteration.

<p>For more complex cases, you can specify execution stages. They are a combination of </p>
<pre class="">duration,target-VUs</pre> pairs. These pairs instruct k6 to linearly ramp up, ramp down, or stay at the number of VUs specified for the period specified. Execution stages can be set via the <pre class="">stages</pre> script/JSON option as an array of <pre class="">{ duration: ..., target: ... }</pre> pairs, or with the <pre class="">--stage</pre>/<pre class="">-s</pre> CLI flags and the <pre class="">K6_STAGES</pre> environment variable via the <pre class="">duration:target,duration:target...</pre> syntax.

<p>For example, the following options would have k6 linearly ramping up from 5 to 10 VUs over the period of 3 minutes (k6 starts with </p>
<pre class="">vus</pre> number of VUs, or 1 by default), then staying flat at 10 VUs for 5 minutes, then ramping up from 10 to 35 VUs over the next 10 minutes before finally ramping down to 0 VUs for another 90 seconds.
<pre class="js">export let options = {
    vus: 5,
    stages: [
        { duration: "3m", target: 10 },
        { duration: "5m", target: 10 },
        { duration: "10m", target: 35 },
        { duration: "1m30s", target: 0 },
<p>Alternatively, you can use the CLI flags </p>
<pre class="">--vus 5 --stage 3m:10,5m:10,10m:35,1m30s:0</pre> or set the environment variables <pre class="">K6_VUS=5 K6_STAGES="3m:10,5m:10,10m:35,1m30s:0"</pre> to achieve the same results.

<p>For a complete list of supported k6 options, refer to the documentation at <a href=""></a>.</p>

<p><em>Hint: besides accessing the supplied <a href="">environment variables</a> through the `</em><em>ENV<pre class="">global object briefly mentioned above, you can also use the [execution context variables](</pre></em><em>VU<pre class="">and</pre></em><em>ITER<pre class="">to access the current VU number and the number of the current iteration **for that VU**. These variables can be very useful if you want VUs to execute different scripts/scenarios or to aid in generating different data per VU.</pre>`<pre class="">"", {username:</pre>testuser${</em><em>VU}<pre class="">, /* ... */})</pre>``</em></p>

<p>For even more complex scenarios, you can use the k6 <a href="">REST API</a> and the </p>
<pre class="">k6 status</pre>, <pre class="">k6 scale</pre>, <pre class="">k6 pause</pre>, <pre class="">k6 resume</pre> CLI commands to manually control a running k6 test. For <a href="">cloud-based tests</a>, executed on our managed infrastructure via the <pre class="">k6 cloud</pre> command, you can also specify the VU distribution percentages for different load zones when executing load tests, giving you scalable and geographically-distributed test execution.

<h3>Setup and teardown</h3>

<p>Beyond the init code and the required VU stage (i.e. the </p>
<pre class="">default</pre> function), which is code run for each VU, k6 also supports test wide setup and teardown stages, like many other testing frameworks and tools. The <pre class="">setup</pre> and <pre class="">teardown</pre> functions, like the <pre class="">default</pre> function, need to be <pre class="">export</pre>ed. But unlike the <pre class="">default</pre> function, <pre class="">setup</pre> and <pre class="">teardown</pre> are only called once for a test - <pre class="">setup()</pre> is called at the beginning of the test, after the init stage but before the VU stage (<pre class="">default</pre> function), and <pre class="">teardown()</pre> is called at the end of a test, after the last VU iteration (<pre class="">default</pre> function) has finished executing. This is also supported in the distributed <a href="">cloud execution</a> mode via <pre class="">k6 cloud</pre>.
<pre class="js">export function setup() {
    return {v: 1};

export default function(data) {

export function teardown(data) {
    if (data.v != 1) {
        throw new Error("incorrect data: " + JSON.stringify(data));
<p>A copy of whatever data </p>
<pre class="">setup()</pre> returns will be passed as the first argument to each iteration of the <pre class="">default</pre> function and to <pre class="">teardown()</pre> at the end of the test. For more information and examples, refer to the k6 docs <a href="">here</a>.

<h3>Metrics, tags and groups</h3>

<p>By default k6 measures and collects a lot of metrics about the things your scripts do - the duration of different script iterations, how much data was sent and received, how many HTTP requests were made, the duration of those HTTP requests, and even how long did the TLS handshake of a particular HTTPS request take. To see a summary of these built-in metrics in the output, you can run a simple k6 test, e.g. </p>
<pre class="">k6 run</pre>. More information about the different built-in metrics collected by k6 (and how some of them can be accessed from inside of the scripts) is available in the docs <a href="">here</a>.

<p>k6 also allows the creation of user-defined </p>
<pre class="">Counter</pre>, <pre class="">Gauge</pre>, <pre class="">Rate</pre> and <pre class="">Trend</pre> metrics. They can be used to more precisely track and measure a custom subset of the things that k6 measures by default, or anything else the user wants, for example tracking non-timing information that is returned from the remote system. You can find more information about them <a href="">here</a> and a description of their APIs <a href="">here</a>.

<p>Every measurement metric in k6 comes with a set of key-value tags attached. Some of them are automatically added by k6 - for example a particular </p>
<pre class="">http_req_duration</pre> metric may have the <pre class="">method=GET</pre>, <pre class="">status=200</pre>, <pre class="">url=</pre>, etc. system tags attached to it. Others can be added by users - globally for a test run via the <pre class="">tags</pre> <a href="">option</a>, or individually as a <a href="">parameter</a> in a specific HTTP request, websocket connection, <pre class="">userMetric.Add()</pre> call, etc.

<p>These tags don't show in the simple summary at the end of a k6 test (unless you reference them in a <a href="">threshold</a>), but they are invaluable for filtering and investigating k6 test results if you use any of the <a href="">outputs</a> mentioned below. k6 also supports simple hierarchical groups for easier code and result organization. You can find more information about groups and system and user-defined tags <a href="">here</a>.</p>

<h3>Checks and thresholds</h3>

<p>Checks and thresholds are some of the k6 features that make it very easy to use load tests like unit and functional tests and integrate them in a CI (continuous integration) workflow.</p>

<p><a href="">Checks</a> are similar to asserts, but differ in that they don't halt execution. Instead they just store the result of the check, pass or fail, and let the script execution continue. Checks are great for codifying assertions relating to HTTP requests/responses. For example, making sure an HTTP response code is 2xx.</p>

<p><a href="">Thresholds</a> are global pass/fail criteria that can be used to verify if any result metric is within a specified range. They can also reference a subset of values in a given metric, based on the used metric tags. Thresholds are specified in the options section of a k6 script. If they are exceeded during a test run, k6 would exit with a nonzero code on test completion, and can also optionally abort the test early. This makes thresholds ideally suited as checks in a CI workflow!</p>
<pre class="js">import http from "k6/http";
import { check, group, sleep } from "k6";
import { Rate } from "k6/metrics";

// A custom metric to track failure rates
var failureRate = new Rate("check_failure_rate");

// Options
export let options = {
    stages: [
        // Linearly ramp up from 1 to 50 VUs during first minute
        { target: 50, duration: "1m" },
        // Hold at 50 VUs for the next 3 minutes and 30 seconds
        { target: 50, duration: "3m30s" },
        // Linearly ramp down from 50 to 0 50 VUs over the last 30 seconds
        { target: 0, duration: "30s" }
        // Total execution time will be ~5 minutes
    thresholds: {
        // We want the 95th percentile of all HTTP request durations to be less than 500ms
        "http_req_duration": ["p(95)&lt;500"],
        // Requests with the staticAsset tag should finish even faster
        "http_req_duration{staticAsset:yes}": ["p(99)&lt;250"],
        // Thresholds based on the custom metric we defined and use to track application failures
        "check_failure_rate": [
            // Global failure rate should be less than 1%
            // Abort the test early if it climbs over 5%
            { threshold: "rate&lt;=0.05", abortOnFail: true },

// Main function
export default function () {
    let response = http.get("");

    // check() returns false if any of the specified conditions fail
    let checkRes = check(response, {
        "http2 is used": (r) =&gt; r.proto === "HTTP/2.0",
        "status is 200": (r) =&gt; r.status === 200,
        "content is present": (r) =&gt; r.body.indexOf("Welcome to the demo site!") !== -1,

    // We reverse the check() result since we want to count the failures

    // Load static assets, all requests
    group("Static Assets", function () {
        // Execute multiple requests in parallel like a browser, to fetch some static resources
        let resps = http.batch([
            ["GET", "", null, { tags: { staticAsset: "yes" } }],
            ["GET", "", null, { tags: { staticAsset: "yes" } }]
        // Combine check() call with failure tracking
        failureRate.add(!check(resps, {
            "status is 200": (r) =&gt; r[0].status === 200 &amp;&amp; r[1].status === 200,
            "reused connection": (r) =&gt; r[0].timings.connecting == 0,

    sleep(Math.random() * 3 + 2); // Random sleep between 2s and 5s
<p>You can save the above example as a local file and run it, or you can also run it directly from the <a href="">github copy of the file</a> with the </p>
<pre class="">k6 run</pre> command. You can find (and contribute!) more k6 script examples here: <a href=""></a>


<p>To make full use of your test results and to be able to fully explore and understand them, k6 can output the raw metrics to an external repository of your choice.</p>

<p>The simplest output option, meant primarily for debugging, is to send the JSON-encoded metrics to a file or to </p>
<pre class="">stdout</pre>. Other output options are sending the metrics to an InfluxDB instance, an Apache Kafka queue, or even to the k6 cloud. This allows you to run your load tests locally or behind a company firewall, early in the development process or as a part of a CI suite, while at the same time being able store their results in the k6 cloud, where you can compare and analyse them. You can find more information about the available outputs <a href="">here</a> and about k6 Cloud Results <a href="">here</a> and <a href="">here</a>.

<h3>Modules and JavaScript compatibility</h3>

<p>k6 comes with several built-in modules for things like making (and measuring) <a href="">HTTP requests</a> and <a href="">websocket connections</a>, <a href="">parsing HTML</a>, <a href="">reading files</a>, <a href="">calculating hashes</a>, setting up checks and thresholds, tracking <a href="">custom metrics</a>, and others.</p>

<p>You can, of course, also write your own ES6 modules and </p>
<pre class="">import</pre> them in your scripts, potentially reusing code across an organization. The situation with importing JavaScript libraries is a bit more complicated. You can potentially use <strong>some</strong> JS libraries in k6, even ones intended for Node.js if you use browserify, though if they depend on network/OS-related APIs, they likely won't work. You can find more details and instructions about writing or importing JS modules <a href="">here</a>.


<p>To get help about usage, report bugs, suggest features, and discuss k6 with other users see <a href=""></a>.</p>


<p>If you want to contribute or help with the development of k6, start by reading <a href=""></a>. Before you start coding, especially when it comes to big changes and features, it might be a good idea to first discuss your plans and implementation details with the k6 maintainers. You can do this either in the <a href="">github issue</a> for the problem you're solving (create one if it doesn't exist) or in the </p>
<pre class="">#developers</pre> channel on <a href="">Slack</a>.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.