TESTEVERYTHING

Showing posts with label k6 performance report. Show all posts
Showing posts with label k6 performance report. Show all posts

Monday, 19 October 2026

How to Read a k6 Performance Report

How to Read a k6 Performance Report

A complete, line-by-line explanation of every core metric, percentile, and threshold—with a realistic report example and practical bottleneck-diagnosis tips.

Introduction

A k6 report packs latency distributions, throughput, concurrency, error rates, and pass/fail verdicts into one summary. Misreading it is easy—and dangerous. A team can ship a broken release by looking only at average response time or overlooking a rising http_req_failed rate.

This guide explains the report zone by zone, shows how to read percentiles, and provides a disciplined way to decide whether a system is ready for real-world traffic.

What is k6?

k6 is an open-source, developer-centric load testing tool from Grafana Labs. JavaScript test scripts simulate virtual users (VUs) hitting an application concurrently while k6 measures how the system behaves.

  • Validate response times as concurrency grows.
  • Find capacity limits before real users do.
  • Catch regressions in CI/CD.
  • Verify SLOs and SLAs using objective measurements.

The structure of a k6 summary report

When you run k6 run script.js, the final output has four useful zones: the execution header, functional checks, the metrics table, and thresholds.

execution: local script: script.js output: - scenarios: (100.00%) 1 scenario, 50 max VUs, 1m30s max duration * default: Up to 50 looping VUs for 1m0s over 2 stages ✓ status is 200 checks.........................: 100.00% ✓ 4500 ✗ 0 data_received...................: 3.8 MB 63 kB/s data_sent.......................: 410 kB 6.8 kB/s http_req_duration...............: avg=210.5ms min=98ms med=190ms max=1.9s p(90)=320ms p(95)=482.11ms http_req_failed.................: 0.42% ✓ 19 ✗ 4481 http_req_waiting................: avg=207.5ms min=96ms med=188ms max=1.88s p(95)=478ms http_reqs.......................: 4500 75.2/s iteration_duration..............: avg=1.2s min=0.9s med=1.15s max=3.1s p(95)=1.8s iterations......................: 4500 75.2/s vus.............................: 1 min=1 max=50 THRESHOLDS http_req_duration ✓ p(95)<500 http_req_failed ✗ rate<0.01

Header describes execution mode, script, scenario, VUs, and duration. Checks are custom assertions. The metrics table contains timing, count, rate, and gauge data. Thresholds are the authoritative automated verdict.

Key metrics explained

http_reqs

Total HTTP requests and requests/second. This is your throughput indicator; a plateau as VUs rise can signal saturation.

http_req_duration

Total request time: sending + waiting + receiving. Read its percentiles, not only its average.

http_req_waiting

Time to first byte (TTFB), usually the largest component. High waiting with low send/receive time points to backend processing.

http_req_blocked

Time waiting for a free client connection slot. High values can indicate connection-pool exhaustion.

http_req_connecting

TCP connection setup time, excluding TLS. High values suggest network or connection overhead.

http_req_tls_handshaking

HTTPS handshake time. Slow values can indicate poor reuse or an overloaded TLS terminator.

http_req_sending

Time to send request data; typically relevant for large uploads.

http_req_receiving

Time to receive the response. Large bodies and slow delivery increase this value.

http_req_failed

Percentage and count of failed requests. Read it alongside latency: a service can be fast because it fails fast.

checks

Pass rate of custom assertions. A 200 response can still fail a body or business-logic check.

iteration_duration

Time for a complete VU loop, including all requests, sleeps, and script processing.

iterations · vus

Completed loop count and current/max virtual users. Compare concurrency with throughput to assess scaling.

data_sent · data_received

Total network bytes transferred; useful for bandwidth planning and finding oversized payloads.

group_duration

Duration of each group() block, useful for locating the slowest step in a user journey.

Percentiles: p50, p90, p95, p99

Never trust the average alone. Two systems can average 200ms while one has a painful long tail. Percentiles expose the experience of slower users.

MeasureMeaningUse it for
p50 (median)Half of requests were faster.Typical user experience
p9090% were faster; 1 in 10 slower.Broad tail monitoring
p9595% were faster; 1 in 20 slower.Common SLO target
p9999% were faster; 1 in 100 slower.High-scale tail risk
maxOne worst-case sample.Outlier investigation, not typical behavior

At 10,000 requests per minute, a 3-second p99 still means roughly 100 requests per minute are that slow. “Rare” does not mean irrelevant at scale.

Thresholds and pass/fail criteria

Thresholds turn measurements into an automated CI/CD decision. Define latency, error-rate, and functional criteria together:

export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.01'],
    checks: ['rate>0.99'],
  },
};

Use a baseline before setting targets, keep safety margin below external SLAs, add tagged thresholds for critical endpoints, and consider abortOnFail for costly tests. A failed threshold makes the overall run fail—even if other metrics look healthy.

Metric types

TypeExamplesHow to read it
Trendhttp_req_durationFocus on p95/p99, not just avg.
Counterhttp_reqs, data_sentCumulative total and throughput rate.
GaugevusCurrent value with min/max.
Ratehttp_req_failed, checksPercentage of true/non-zero events.

Example report walkthrough

  1. Check thresholds first. The displayed checkmarks are authoritative; verify the configured expression and measured value together.
  2. Check errors. Pair http_req_failed with checks; protocol success does not guarantee correct content.
  3. Inspect latency. Compare p95/p99 against the SLO, then break duration into waiting, sending, receiving, blocked, and connection time.
  4. Check load and throughput. Compare http_reqs/s with VU levels and look for a plateau.
  5. Separate warm-up from steady state. Final summaries combine the whole run, so use time-series output for precise windows.

Important: Never infer pass/fail from a metric value alone. The threshold expression, tags, abort settings, and actual ✓/✗ output are the source of truth.

Common pitfalls

  • Trusting averages over p95/p99.
  • Ignoring http_req_failed because latency is low.
  • Mixing ramp-up measurements with steady-state results.
  • Confusing checks with HTTP request failures.
  • Treating a single maximum as typical behavior.
  • Comparing runs with different VU counts, durations, or load profiles.
  • Overlooking blocked and connection time, which may indicate a client or network bottleneck.

Exporting and visualizing

  • JSON: k6 run --out json=results.json script.js for raw measurements.
  • HTML: use a community reporter such as k6-reporter for shareable reports.
  • Grafana: stream to InfluxDB or Prometheus for dashboards and capacity-knee analysis.
  • k6 Cloud: get hosted visualization, history, and geographic load distribution.
  • CI/CD: rely on k6’s non-zero exit code when thresholds fail.

Actionable checklist

  1. Check thresholds first.
  2. Read p95/p99, not averages alone.
  3. Pair latency with error rate.
  4. Use checks for business and response correctness.
  5. Break duration down to localize bottlenecks.
  6. Judge steady state separately from ramp-up.
  7. Export results to track trends across releases.

FAQ

What does p(95) mean?

95% of measured requests completed faster than the stated value; 5% were slower.

Why is http_req_duration different from http_req_waiting?

Duration covers sending, waiting, and receiving. Waiting is the server-response/TTFB portion. Their difference helps locate network versus backend time.

What is the difference between checks and http_req_failed?

Request failure is protocol-level; checks are custom assertions. A 200 response can pass the former and fail the latter.

Should an SLA use average or percentile?

Use a percentile—typically p95 or p99—because averages hide tail latency.


Translate







Tweet