k6, the open-source load testing tool, can stream its results directly to InfluxDB, a time-series database, which Grafana, a popular visualization platform, can then query to build live, interactive load testing dashboards.

Let’s see this in action. Imagine you’re running a k6 test against your API, and you want to see how latency and error rates are trending in real-time without waiting for the test to finish.

First, you need k6 to output metrics. The --out influxdb=http://localhost:8086 flag tells k6 to send data to an InfluxDB instance running locally on the default port. If your InfluxDB is elsewhere, you’d use its IP address or hostname.

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

Now, k6 is happily sending metrics like http_req_duration, http_req_failed, and checks directly to InfluxDB. But how do you see them? That’s where Grafana comes in.

In Grafana, you’ll add InfluxDB as a data source. You’ll typically configure it with the same URL k6 uses: http://localhost:8086. You’ll also need to specify the InfluxDB database k6 is writing to. By default, k6 uses a database named k6. If you didn’t change this in your InfluxDB setup, you’re all set.

Once InfluxDB is configured as a data source, you can start building dashboards. Let’s say you want to visualize the 95th percentile of HTTP request duration. In a Grafana panel, you’d select your InfluxDB data source and write a query. For InfluxDB v1.x (often used with older k6 setups), it might look like this:

SELECT non_negative_derivative(mean("value"), 1s) FROM "http_req_duration" WHERE $timeFilter GROUP BY time($__interval) fill(null)

For InfluxDB v2.x and Flux, the query is different:

from(bucket: "k6")
  |> range($range)
  |> filter(fn: (every, _start, _stop) => true)
  |> filter(fn: (every, _start, _stop) => _start >= _start and _stop <= _stop)
  |> filter(fn: (every, _start, _stop) => _field == "value" and r["name"] == "http_req_duration")
  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
  |> yield(name: "mean")

To get the 95th percentile, you’d change mean to percentile(0.95) in your Flux query.

The magic here is that k6 is pushing data as it’s generated. Grafana is then pulling that data and displaying it with minimal delay. This gives you a live view of your system’s performance under load. You can add panels for:

  • Request Rate: Count of requests per second.
  • Error Rate: Percentage of failed requests.
  • Latency Percentiles: P95, P99 latency.
  • Throughput: Data transferred.
  • Custom Metrics: Any metrics your k6 script emits.

The mental model to build around this is one of continuous data flow. k6 acts as a high-volume producer, InfluxDB is the durable, time-ordered buffer, and Grafana is the intelligent consumer that transforms raw data points into actionable insights. You’re not just looking at a static report; you’re observing a dynamic system state.

A common point of confusion is how k6 handles different metric names. While http_req_duration is standard, if you define custom metrics in your k6 script, like http_req_custom_latency, you’ll need to adjust your Grafana queries accordingly. For instance, if you have a custom metric my_api_response_time, your Flux query might include |> filter(fn: (every, _start, _stop) => _field == "value" and r["name"] == "my_api_response_time").

This setup allows you to see performance degradation or anomalies as they happen during a load test, enabling you to stop the test early or identify the root cause much faster than waiting for a post-test report.

The next step in building out your load testing infrastructure is often configuring alerting within Grafana based on these live metrics.

Want structured learning?

Take the full K6 course →