RousselTMLEARNING

Observability

k6 and Alloy: sending load test results over OpenTelemetry

Send k6 metrics to Alloy with the OpenTelemetry output, convert them for Mimir and compare them with service metrics. Real results with k6 v2.3.0.

1. Why go through Alloy #

Results and metrics in one place.

The principle #

k6 prints a summary at the end of a test, but during a load test you want to see client-side latency and service saturation in real time. By sending k6 metrics to Alloy, they join Mimir next to production metrics: one dashboard for both.

From k6 to Mimir via Alloy

2. Configuration and run #

Alloy then k6.

config.alloy #

otelcol.receiver.otlp "k6" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }
  output {
    metrics = [otelcol.processor.batch.k6.input]
  }
}

otelcol.processor.batch "k6" {
  output {
    metrics = [otelcol.exporter.prometheus.mimir.input]
  }
}

otelcol.exporter.prometheus "mimir" {
  forward_to = [prometheus.remote_write.mimir.receiver]
}

prometheus.remote_write "mimir" {
  endpoint {
    url = "http://mimir:9009/api/v1/push"
  }
}

Configuration validated with alloy validate (Alloy v1.21.3, November 2026).

Running the test #

import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = { vus: 5, duration: '15s', thresholds: { http_req_duration: ['p(95)<500'] } };
export default function () {
  const res = http.get('http://127.0.0.1:8099/');
  check(res, { 'statut 200': (r) => r.status === 200 });
  sleep(0.5);
}
K6_OTEL_GRPC_EXPORTER_INSECURE=true K6_OTEL_METRIC_PREFIX=k6_ \
  k6 run --tag testid=checkout-001 -o opentelemetry load.js
VariableDefaultRole
K6_OTEL_EXPORTER_PROTOCOLgrpcor http/protobuf
K6_OTEL_GRPC_EXPORTER_ENDPOINTlocalhost:4317Alloy address
K6_OTEL_GRPC_EXPORTER_INSECUREfalseNo TLS (local network)
K6_OTEL_METRIC_PREFIXemptyMetric prefix, here k6_
K6_OTEL_EXPORT_INTERVAL10sExport frequency

3. What Alloy receives: a real test #

The produced series.

The metrics #

With otelcol.exporter.debug, Alloy printed every 5 seconds 16 metrics sent by k6 v2.3.0 (excerpt):

ResourceMetrics service.name=k6 service.version=2.3.0
k6_http_reqs{method=GET,scenario=default,status=200,testid=checkout-001,url=http://127.0.0.1:8099/} 90
k6_http_req_duration{…,testid=checkout-001} count=90 sum=467.6 min=0.446 max=21.05 le5=52 le10=27 le25=11

Durations are in milliseconds and arrive as histograms. The testid tag passed on the command line is on every series: it lets you compare two runs. At the end, k6 reported 150 requests, 0% failures and a p95 of 12.38 ms.

k6 metrics and service metrics together in Mimir
Cardinality. The url and name labels create one series per URL: group dynamic URLs with the name parameter of http.get (for example /product/{id}).

Sources #

Related articles

Alloy and the Grafana Labs ecosystem: which component for which tool?

The map of Alloy integrations with Faro, Beyla, k6, Loki, Mimir, Tempo, Pyroscope, Database Observability, Fl…

Faro and Alloy: receiving front-end RUM with faro.receiver

Configure faro.receiver: CORS, API key, rate limiting, source maps, then send browser errors, Web Vitals and …

Beyla and Alloy: zero-code RED metrics and traces with beyla.ebpf

Enable Beyla eBPF instrumentation in Alloy: service discovery, RED metrics to Mimir, traces to Tempo, require…

Keep learning

Support us

♥ SUPPORT US

Support RousselTM Learning

Courses, articles, labs and certificates stay free with no intrusive ads. A voluntary contribution, of any amount you choose, pays for hosting, lab environments and new content.

Contribute with PayPal Any amount · card or PayPal accountLearn more
QR code PayPal
Or scan this QR code with your phone

Thank you, every contribution counts!