The Independent Blueprint for Modern Load Testing & Grafana k6 Performance Engineering

Enterprise k6 Scripting: Custom Telemetry, Thresholds, and SharedArray

Posted on May 24, 2026 in Workloads & Execution

Writing a basic load test script in k6 is simple. You import HTTP, write a default function, send a GET request, and run it. This works well for quick validations, but it falls apart when you need to run complex, production-grade load tests in an enterprise pipeline. A production script needs to do more than hit a URL: it must configure multiple execution profiles, manage large parameter datasets without wasting RAM, instrument custom business metrics, and enforce strict Service Level Objective (SLO) gates.

Let us look at how to build a robust, modular, and memory-optimized k6 test script designed for automated CI/CD evaluation.

Structuring Multi-Scenario Options

In a large engineering team, you do not want to maintain separate files for different test scenarios. You should configure multiple workloads (such as smoke validation, average load tests, and peak stress tests) within a single execution script. This is achieved by defining distinct scenarios within the options object:

export const options = {
  discardResponseBodies: true, // Optimizes memory by avoiding loading large responses
  scenarios: {
    smoke_check: {
      executor: 'constant-vus',
      exec: 'executeSmokeTest', // Routes execution to a specific function
      vus: 1,
      duration: '30s',
      gracefulStop: '5s',
    },
    throughput_ramp: {
      executor: 'ramping-arrival-rate',
      exec: 'executeLoadTest',
      startRate: 10,
      timeUnit: '1s',
      preAllocatedVUs: 50,
      maxVUs: 500, // Headroom headroom to handle backend stalls
      stages: [
        { target: 100, duration: '2m' }, // Ramp up to 100 RPS
        { target: 100, duration: '5m' }, // Hold steady
        { target: 0, duration: '1m' },   // Cool down
      ],
      gracefulStop: '30s',
    },
  },
};

By defining separate entry points using the exec property, you can keep your execution logic modular and cleanly separated within a single codebase.

Instrumenting Custom Telemetry and SLO Thresholds

Standard HTTP metrics (such as request duration and failure rates) are useful, but they do not tell you the whole story. You need to capture domain-specific metrics, such as database query delays, checkout failure rates, or active user sessions. k6 provides four classes of custom metrics:

  • Trend: Tracks statistical distributions (p90, p95, averages) for variables like custom latencies.
  • Rate: Calculates the percentage of positive results, which is ideal for error rates or success ratios.
  • Counter: Sums values over time, perfect for tracking total processed transactions.
  • Gauge: Displays the latest value, useful for monitoring thread concurrency or memory usage.

You can hook these metrics directly into k6's automated Thresholds engine to enforce SLOs. If a threshold is violated, k6 will exit with a non-zero code, failing the CI/CD build automatically:

import { Trend, Rate } from 'k6/metrics';

const customLatencyTrend = new Trend('custom_api_latency_ms');
const customErrorRate = new Rate('custom_error_rate');

export const options = {
  thresholds: {
    'http_req_failed': ['rate<0.01'], // Global HTTP failure rate under 1%
    'http_req_duration': ['p(95)<200', 'p(99)<400'], // Latency targets
    'custom_api_latency_ms': ['p(95)<150'], // SLO target for custom metric
    'custom_error_rate': ['rate<0.005'], // Custom error rate under 0.5%
  },
};

Optimizing Data Management with SharedArray

If your scenarios require thousands of virtual users to read credentials, search terms, or tenant details, loading this data inside the default loop will crush your system memory. As we explained in our architecture deep dive, each VU runs an isolated virtual machine. Loading a large JSON file inside each VU will duplicate that data across all instances.

To avoid this, use SharedArray. It reads your files once and stores them in a shared read-only heap space, keeping your memory footprint minimal:

import { SharedArray } from 'k6/data';

const testData = new SharedArray('user_credentials', function () {
  const data = JSON.parse(open('./data.json')); // Loaded exactly once on startup
  return data;
});

Combining multi-scenario orchestrators, custom telemetry instrumentation, SharedArray optimization, and strict SLO threshold gates is the only way to build a reliable load testing suite that scales seamlessly.

If you want to understand why open-loop scheduling is critical for this scripting pattern, read Coordinated Omission: Why Open-Loop Workloads Are Vital for Real Load. To learn how to integrate frontend user tests with this protocol-level script, check out Hybrid Load Testing: Combining k6 Protocol VUs with k6/browser and Playwright. If you are setting up this repository on Windows, see our framework guide on Architecting an Enterprise k6 Framework on Windows.