All Articles
Performance Testing with Grafana K6
Photo by Rahul Mishra on Unsplash

Performance Testing with Grafana K6

Karla Silva · March 20, 2026

Slow websites can frustrate users because they cannot access information instantly. As a result, performance testing plays a critical role in establishing acceptable quality levels, so our goal as software engineers is to catch performance issues before they reach the production environment. While there are many tools available, both paid and open source, this article will focus specifically on Grafana k6.

Grafana k6 is an open-source tool that allows you to test the performance of your applications. Tests are written in JavaScript, which makes them easier to write, read, and maintain. Additionally, k6 offers various extensions to support a wide range of use cases. The code can also be easily integrated into existing CI/CD pipelines, enabling automated performance testing as part of your development workflow.

Before You Start

Before running any performance tests, it’s important to consider the potential impact and plan accordingly. Here are some key questions to ensure your tests are safe and that proper communication has been done:

  • Are my performance testing goals clear? Do I know how many users my system can handle, or at which point it starts to degrade?
  • Do I have a strategy for the type of load test to execute? There are many types of load tests. Please refer to this article on the Grafana website to learn more. It’s usually best to start with a smoke test using a small number of users (less than 5) to verify that the script works as expected.
  • Are you or your team the owner of the application under test? You should never run K6 tests against random applications.
  • Is your application connected to other systems? If yes, are the relevant teams aware of the performance testing initiative and aligned regarding the test execution? Keep in mind that external APIs could be affected by the high number of requests.
  • Have you coordinated with your IT/SRE team or hosting provider (if applicable)? Make sure it is safe to run the tests without causing unintended issues or being blocked because of company security policies.
  • Do you have a proper environment for testing? Running tests in production is not recommended to avoid impacting users. Ideally, an environment with specifications like production should be used.

After taking the time to assess the impact, consider the questions above, and do the necessary communications and alignments, you can move on to the practical part.

Install k6

k6 does not offer a graphical user interface; however, the installation process is very simple. Especially if you are familiar with package managers. For example, macOS users with Homebrew can install it with just a single command:

brew install k6

For other operational systems, please check the dedicated page on their website regarding the setup process.

Getting Started

After installing K6, you are ready to start writing your tests. To do this, create a JavaScript file. For instance, a folder named tests or scripts can be created to better organize the test scripts.

Example 1

This example demonstrates a very simple test. It imports the http module to make requests and the sleep function to pause execution. Next, it defines an options block, where the number of virtual users (VUs) and the test duration are configured. If needed, the number of iterations can also be defined in this block. These options can alternatively be set at execution time via the command line.

The script then sends a GET request to a specific URL and waits one second before continuing.

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

export const options = {
  vus: 5,
  duration: "10s",
};

export default function () {
  http.get("https://quickpizza.grafana.com/test.k6.io/");
  sleep(1);
}

First Test – View code on GitHub Gist

To run the test, execute:

k6 run FILENAME.js

If everything runs successfully, you should see an output like the screenshot below.

Test output

Notice that the defined number of VUs and duration were applied. In our case, the script ran for 10 seconds with 5 users simultaneously calling the specified URL.

From the output, you can observe that:

  • 45 requests were made (http_reqs).
  • None of them failed (http_req_failed).
  • The average request duration (http_req_duration) was 138.23ms.
  • Considering 90% of the requests, the duration was 146.85ms (p(90)) or less.

Those are some of the built-in metrics included in the default K6 summary report, but you can also create custom metrics.

It’s important to highlight that when analyzing performance results, percentiles are more meaningful metrics than minimum (min) and maximum (max). The minimum and maximum can represent outliers, while percentiles show how the system behaves for most requests.

Example 2

This example builds on the previous one but introduces new concepts: Checks, Assertions, and Thresholds.

Checks are like assertions, a concept you are probably already familiar with. They are used to validate whether the application under test behaves as expected. To use them, you need to import the check function from the k6 library.

For example, a check can verify that the response status code is 200 or that the response body contains a specific text:

import http from "k6/http";
import { sleep, check } from "k6";

export const options = {
  vus: 5,
  duration: "10s",
};

export default function () {
  const response = http.get("https://quickpizza.grafana.com/test.k6.io/");

  check(response, {
    "is status 200": (res) => res.status === 200,
    "response body contains text": (res) => res.body.includes("QuickPizza page"),
  });

  sleep(1);
}

Example using Checks – View code on GitHub Gist

When executing this test, you will notice a new section in the output dedicated to checks. It shows the total number of checks executed, as well as how many passed and failed. It is important to mention that a failed check does not interrupt the test execution, nor does it cause the test to return a failed exit status.

Checks data on the output

Assertions can also be used to validate that your application behaves as expected during the testing; however, they immediately fail and interrupt the test execution when expectations are not met. In order to use it, you need to import the expect function and define an assertion passing a value to the expect function.

It is also important to mention that, at the time of writing this article, assertions are a preview feature in K6, and some functionality may still be missing.

As can be noted on the example below, the assertions API is inspired by Playwright’s assertion syntax, and they are easy to read and understand.

import { expect } from "https://jslib.k6.io/k6-testing/0.6.1/index.js";
import http from "k6/http";
import { sleep } from "k6";

export const options = {
  vus: 5,
  duration: "10s",
};

export default function () {
  const response = http.get("https://quickpizza.grafana.com/test.k6.io/");

  expect(response.status).toBe(200);
  sleep(1);
}

Example using Assertions – View code on GitHub Gist

As mentioned earlier, if an assertion fails, the test is immediately aborted. In the screenshot below, you can see that 0 iterations were completed because the assertion was deliberately set to fail. If all assertions pass, there is no change in the output. Unlike checks, assertions do not collect metrics, so the output does not include a dedicated section for them.

Test output when an assertion is failing

Thresholds are commonly used in performance contexts to define the expectations for the applications, and on k6, it’s a way to pass or fail the test based on the conditions. The thresholds are defined in the options block, and you can add more than one, like the example below.

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

export const options = {
  vus: 5,
  duration: "10s",
  thresholds: {
    http_req_failed: ["rate<0.01"],
    http_req_duration: ["p(95)<200"],
  },
};

export default function () {
  http.get("https://quickpizza.grafana.com/test.k6.io/");
  sleep(1);
}

Example using thresholds – View code on GitHub Gist

The example has 2 thresholds. One to evaluate that less than 1% of the requests return an error and the second one to check that 95% of the requests have a response time below 200ms. Similar to the checks, there is also a dedicated section on the output.

Test output when using thresholds

Integrating with CI/CD

To run tests continuously and automatically, you can integrate K6 test execution into your CI/CD pipeline.

For GitHub Actions, K6 provides official actions that simplify the setup, making the workflow file very straightforward, as shown in the example below. It’s important to update the path to your test files; otherwise, they will not be found during execution.

name: Run Grafana K6 Loading Tests

on:
  push:
    branches:
      - main

jobs:
  run-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup k6
        uses: grafana/setup-k6-action@v1

      - name: Run k6 tests
        uses: grafana/run-k6-action@v1
        with:
          path: |
            ./tests/*test.js
            ./tests/*script.js

GitHub Actions workflow – View code on GitHub Gist

Like the official actions available in the GitHub Actions Marketplace, there is also an extension for Azure Pipelines. This option requires installing the extension in your Azure DevOps organization, but once installed, the setup process is straightforward. Please refer to this article for more details.

For Jenkins, there is also an article that explains how to create the Jenkinsfile and run the tests.

Conclusion

Like any tool, K6 has some limitations, for example, it does not connect directly to the server, so it cannot provide metrics such as memory or CPU usage. However, it does offer other metrics that are equally important for your project.

In one of our projects, K6 was integrated into the pipelines and runs after each deployment. This allows the team to automatically verify that no performance issues were introduced. For example, if a specific test fails, they know that in this specific version something went wrong, causing the assertions or thresholds to fail.

Additionally, the official documentation and articles available on the website are clear, well-structured, and very detailed. The community has also done a great job; you can easily find a post or even a video that addresses your needs.

Share this Article
Techsphere

Share your
excellence