Releases: grafana/k6
Release list
v1.8.1
k6 v1.8.1 is here! This patch release includes:
- Bug fix for the cloud secret source being enabled by default, which broke scripts that configure their own secret source
- Bug fix for HTTP/2 negotiation and error classification under Go 1.27
- Security updates for gRPC,
golang.org/x/net,golang.org/x/text,golang.org/x/crypto, OpenTelemetry,klauspost/compress, and the Go toolchain
Bug fixes
- #6274 Stops enabling the Grafana Cloud secret source by default in
k6 cloud run --local-execution, reverting #5875. Scripts that configured their own secret source failed withno secret source with name "default" is configured. Explicitly configured secret sources now work again as they did inv1.7. Fixes #6093. - #6275 Fixes HTTP/2 negotiation and error classification under Go 1.27, which changed HTTP/2 negotiation and error behaviour. Keeps VUs on HTTP/2, classifies connection and
GOAWAYerrors consistently across Go versions, and preserves unknown HTTP/2 error buckets. Backport of #6234.
Maintenance and security updates
- #6193, #6194 Updates
google.golang.org/grpctov1.82.1[security]. - #6187, #6189 Updates
golang.org/x/nettov0.56.0[security]. - #6188, #6190 Updates
golang.org/x/texttov0.39.0[security]. - #6207 Updates
go.opentelemetry.io/oteltov1.44.0[security]. - #6220 Updates
github.com/klauspost/compresstov1.18.7[security]. - #6135 Updates the Go toolchain to
v1.25.12[security]. - Updates
golang.org/x/cryptotov0.53.0[security].
v2.2.0
k6 v2.2.0 is here 🎉! This release includes:
k6 cloud run --local-executionnow streams k6's logs to Grafana Cloud, so the test run's log view works for local execution too.chromium.connectOverCDP(), which connects browser tests to an already-running Chromium instance.TextEncoderandTextDecoderavailable as globals, andWritableStreamsupport ink6/experimental/streams.- A
k6 cloud load-zone listcommand. - Two new experimental feature flags:
merge-run-tagsandfreeze-env.
Breaking changes
There are no breaking changes in this release.
New features
k6 cloud run --local-execution streams logs to Grafana Cloud #6171
When a cloud test runs locally with k6 cloud run --local-execution, k6's logs now stream to the Grafana Cloud test run, so the run's log view is populated the same way it is for cloud execution. Previously, local-execution logs stayed on the machine running k6 and never reached the cloud. Work with Grafana's secrets management to safely work with secrets and redact them if they're accidentally leaked into logs are pushed. Use the new --no-cloud-logs flag opts out to opt out of streaming of logs when working with --local-execution:
k6 cloud run --local-execution script.js
k6 cloud run --local-execution --no-cloud-logs script.jsConnect to a running browser with chromium.connectOverCDP() #6165
The browser module can now attach to an existing Chromium-based browser over the Chrome DevTools Protocol, mirroring Playwright's browserType.connectOverCDP(). Pass the browser's WebSocket endpoint and k6 manages the returned browser's connection — it's auto-closed at the end of the iteration, though you can call close() earlier to release the connection on demand.
import { chromium } from 'k6/browser';
export default async function () {
const browser = await chromium.connectOverCDP('ws://localhost:9222/devtools/browser/<id>');
const page = await browser.newPage();
try {
await page.goto('https://quickpizza.grafana.com/');
} finally {
await page.close();
await browser.close();
}
}Unlike the K6_BROWSER_WS_URL environment variable, the endpoint is a runtime value — you can, for example, request a fresh session URL from a browser provider's API in setup() and connect to it from the iterations.
TextEncoder and TextDecoder globals #6182
TextEncoder and TextDecoder are now available as standard globals in both the init and VU contexts, no import required — matching how they are exposed in browsers and other JavaScript runtimes.
const encoded = new TextEncoder().encode('Hello, world!');
const decoded = new TextDecoder().decode(encoded);WritableStream in k6/experimental/streams #6132
The experimental streams module now implements WritableStream and WritableStreamDefaultWriter following the WHATWG Streams specification, complementing the existing ReadableStream and paving the way for a future TransformStream implementation.
import { WritableStream } from 'k6/experimental/streams';
export default async function () {
const stream = new WritableStream({
write(chunk) {
console.log(`wrote ${chunk}`);
},
});
const writer = stream.getWriter();
await writer.write('hello');
await writer.close();
}k6 cloud load-zone list command #6142
A new k6 cloud load-zone list subcommand lists the load zones — public and private — available in the configured Grafana Cloud k6 stack, mirroring the existing k6 cloud project list command. Output defaults to a human-readable table; pass --json to emit a JSON array instead.
$ k6 cloud load-zone list
Load zones for https://example.grafana.net:
ID NAME TYPE AVAILABLE
amazon:us:ashburn Ashburn, US (Amazon) public yes
amazon:sa:cape town Cape Town, SA (Amazon) public yesConfigurable handleSummary() timeout #5854
The time budget for the handleSummary() callback — previously hardcoded to 120 seconds — is now configurable through the handleSummaryTimeout option or the K6_HANDLE_SUMMARY_TIMEOUT environment variable, so long-running tests with heavy summaries no longer fail with handleSummary() execution timed out. Thanks, @LBaronceli!
export const options = {
handleSummaryTimeout: '5m',
};New experimental feature flags: merge-run-tags and freeze-env
Two new experimental flags join the feature-flag system introduced in v2.1.0:
- #5714
merge-run-tagsmerges run tags per key across config layers, sooptions.tagsin a script is no longer silently discarded when--tagorK6_TAGSis also used — higher-priority layers win on conflicting keys instead of replacing the whole map. Thanks, @yordis! - #6032
freeze-envfreezes the__ENVobject, so modifications from script code throw aTypeError(in strict mode) instead of silently persisting across iterations and scenarios. Thanks, @lohitkolluri!
k6 run --features merge-run-tags,freeze-env script.jsUX improvements and enhancements
- #5631 Makes the browser module's header accessors —
response.allHeaders(),headerValue(),headerValues(), andheadersArray()— return the raw wire headers (includingSet-Cookieand security-related headers), correctly paired with each hop of a redirect chain instead of Chrome's provisional headers. As part of this,headerValues()now matches header names case-insensitively and splits repeated values on newlines rather than commas, and thebrowser_data_sent/browser_data_receivedmetrics now include the raw header bytes and no longer vary run-to-run with CDP event ordering. - #6208 Makes
k6 cloudreject the run flags (for example,--vus) with anunknown flagerror and a non-zero exit code. Previouslyk6 cloud --vus 10 script.jsaccepted the flags, printed the help text, and exited 0 — running tests withk6 clouddirectly was deprecated in v2.0.0 in favor ofk6 cloud run. - #6096 Points the cloud secrets error at
K6_CLOUD_SECRETS_TOKENandK6_CLOUD_SECRETS_ENDPOINTwhen a test run is reused viaK6_CLOUD_PUSH_REF_IDunder--local-execution, instead of suggesting the--local-executionflag the user is already using. - #6196 Adds
catchblocks to the browser examples so a failing iteration reports the original error instead of a subsequentpage.close()failure. Thanks, @locker95!
Bug fixes
- #6234 Classifies HTTP/2 errors by message so the
error_codemetric tag stays correct when k6 is built with Go 1.27 (whosex/net/http2delegates to the standard library), and explicitly enables HTTP/2 negotiation on VU transports. - #6232 Drains queued log entries in the Loki hook at shutdown so
--out lokiand cloud log streaming no longer lose the final batch, and emits ak6 dropped N log messageswarning when the cloud log buffer overflows instead of dropping logs silently. - #6125 Serializes the first concurrent open of a file in the caching filesystem so parallel
fs.open()calls on the same file no longer read zero or truncated bytes. - #6147 Fixes a data race and inconsistent request-interception state when browser routes are added or removed concurrently. Thanks, @somak2kai!
- #6070 Flushes buffered file log output once per second so recent logs aren't lost when k6 is killed before shutdown. Thanks, @rohan-patnaik!
- #6205 Stops sending an invalid
Sec-WebSocket-Protocolheader when tailing Grafana Cloud logs; spec-strict servers rejected the handshake withwebsocket: bad handshake. - #6200 Leaves a counter's
rateunset when the observed duration is zero, instead of computing+Infand spuriously failingratethresholds. Thanks, @samarth70! - #6195 Initializes a gauge's maximum from the first sample so all-negative gauge series no longer report
max=0. Thanks, @Solaris-star! - #6145 Prevents the OpenTelemetry output from panicking at startup when basic auth is configured without
K6_OTEL_HEADERS. Thanks, @lukdz! - #6140 Stops
SharedArraydeep-freezing JS primitives, which needlessly wrapped large strings inStringobjects — cutting memory usage in the reported reproduction from roughly 1 GB to 100 MB.
Maintenance and internal improvements
- #6126, #6224, #6229 Adds anonymous extension usage to the k6 usage report: a run reports the Go module path, version, and type of registry-cataloged extensions it actually uses (imported
k6/x/modules, output extensions selected with--out, andk6 xsubcommands). Private and unlisted extensions are never reported, and the existing--no-usage-reportopt-out covers it. - #6183, #6218 Updates Sobek and regexp2, making
WeakMap/WeakSetentries garbage-collectable, improving string and typed-array correctness and performance, and bo...
v2.1.0
k6 v2.1.0 is here 🎉
This release includes:
- An opt-in feature-flag system —
--features, theK6_FEATURESenvironment variable, and ak6 featuresdiscovery command — shipping with experimental native histograms for trend metrics as its first flag. - A context-level
proxyoption for browser contexts. - Subcommand discovery in
k6 x, so binaries can report which extension commands they expose.
Breaking changes
There are no breaking changes in this release.
New features
k6 cloud test list command #6007
A new k6 cloud test command group has been added, with a k6 cloud test list subcommand that lists the load tests of a Grafana Cloud k6 project. It complements the k6 cloud project list command introduced in v2.0.0.
The project to list tests for is resolved in the following order:
- The
--project-idflag. - The
K6_CLOUD_PROJECT_IDenvironment variable (cloud configprojectID). - The default project of the configured stack, populated by
k6 cloud login.
Output defaults to a human-readable table. Pass --json to emit a JSON array instead, mirroring the format established by k6 cloud project list.
k6 cloud test list
k6 cloud test list --project-id 12345
k6 cloud test list --jsonFeature flags and experimental native histograms #6055, #6056
k6 now has an opt-in feature-flag mechanism for trialing new, not-yet-stable behavior without affecting existing runs. Flags can be enabled on k6 run and k6 cloud run through the --features flag (comma-separated or repeated), the K6_FEATURES environment variable, or the features key in config.json. Enabled flags are surfaced as metric tags and propagated into archives and cloud workers so a run behaves consistently wherever it executes.
Use k6 features (or k6 features --json) to discover the available flags and their lifecycle:
$ k6 features
FEATURE LIFECYCLE DESCRIPTION
native-histograms Experimental Use native histograms for trend metricsThe first flag shipped is native-histograms, an experimental flag that makes k6 use native histograms for trend metrics:
k6 run --features native-histograms script.js
# or
K6_FEATURES=native-histograms k6 run script.jsSubcommand discovery in k6 x #5972
Running k6 x now lists the available subcommands — both the ones baked into the binary and those advertised by the extension registry (official and community). Tab-completion surfaces the same set once the catalog has been cached locally by a prior k6 x run, so completion never blocks on the network.
$ k6 x
...
Available Commands:
agent Bootstrap an AI-assisted k6 testing workflow in any editor
docs CLI k6 docs for AI agents and users
explore Explore k6 extensions for Automatic Resolution
mcp An MCP server for k6 for AI agents
This makes a k6 binary self-describing — particularly useful for AI agents driving k6, which previously had no way to introspect which extension subcommands were available.
Browser context proxy option #5924
Browser contexts can now be configured with a context-level proxy option, letting you route a context's traffic through a proxy without launching a custom-built binary or relying on environment proxy variables (which only affected k6's DevTools WebSocket connection). The option is wired to Chromium through Target.createBrowserContext, and invalid proxy configuration now fails early when proxy.server is missing. Thanks, @nightt5879!
const context = await browser.newContext({
proxy: {
server: 'http://proxy.test:8080',
bypass: 'localhost,127.0.0.1',
},
});Browser locator.isInViewport() #6023
A new locator.isInViewport() method reports whether an element intersects the browser viewport. It accepts an optional ratio (0 to 1) that sets how much of the element must be visible, defaulting to 0 so any visible pixel counts, matching Playwright's toBeInViewport semantics. The call waits for the element to attach, honoring the timeout option, then measures the intersection once. Thanks, @Anuragp22!
const button = page.locator('button#submit');
if (await button.isInViewport()) {
await button.click();
}Basic auth for the OpenTelemetry HTTP exporter #5997
The OpenTelemetry output's HTTP exporter can now send HTTP Basic Auth credentials. Set them through the K6_OTEL_HTTP_EXPORTER_USERNAME and K6_OTEL_HTTP_EXPORTER_PASSWORD environment variables, or the username and password keys in the output config.
K6_OTEL_HTTP_EXPORTER_USERNAME=user \
K6_OTEL_HTTP_EXPORTER_PASSWORD=secret \
k6 run --out opentelemetry script.jssingle() selection helper in k6/html #6002
Selection.single(selector) returns at most one matching element, backed by goquery's Single matcher for a faster lookup than find() when you only need the first match. Thanks, @rohan-patnaik!
import { parseHTML } from 'k6/html';
const doc = parseHTML(content);
const title = doc.single('h1').text();UX improvements and enhancements
- #5971 Tags browser API failures with
module=browserso that browser errors surfaced in Grafana Cloud Logs can be filtered separately from other log sources.
Bug fixes
- #5794 Makes the
--vusflag work as a standalone execution shortcut instead of being silently ignored when a script defines scenarios. Runningk6 run script.js --vus Nnow creates ashared-iterationsscenario withNVUs andNiterations, overriding any script-defined scenarios with a warning — consistent with how--iterations,--duration, and--stagesalready behave. Thanks, @Reranko05! - #6013 Rejects invalid threshold percentiles. A percentile aggregation value outside the 0 to 100 range (or
NaN) now fails parsing with a clear message instead of being silently accepted. Thanks, @immanuwell! - #6011 Writes the on-disk k6 config file with owner-only permissions (
0o600, inside a0o700directory). The file can hold the Grafana Cloud API token (collectors.cloud.token), so tightening it keeps other local users on shared hosts (CI runners, multi-user boxes, sidecar containers) from reading the token. Existing configs are upgraded on the next write, for example the nextk6 cloud login.
Maintenance and internal improvements
- #6033 Moves Docker Hub image publishing to a Google Artifact Registry mirror.
- #5953 Installs
s3cmdviaaptinstead ofpipto fix thek6packagerimage build. - #6052 Fixes the browser end-to-end test workflow.
- #5984 Routes per-test logger output to the test instance instead of the global
logrus, improving test isolation. - #5985 Fixes the flaky
TestPageScreenshotFullpagebrowser test. - #5981 Lets Renovate track the Go minor version in the
setup-goworkflows. - #5968 Honors the caller-pinned ref in the shared lint action.
- #6041, #6054 Aligns the Go module directive and toolchain (1.25.0 / 1.25.11).
- #5967 Updates the release notes template after v2.0.0.
- #6015 Updates
golang.org/x/nettov0.55.0[security]. - #6026 Updates
golang.org/x/cryptotov0.52.0[security]. - #5962, #6028 Updates
google.golang.org/grpctov1.81.1. - #5960 Updates
grafana/shared-workflows/get-vault-secretstov1.3.2. - #5958 Updates
grafana/shared-workflows/azure-trusted-signingtov1.0.2. - #5957 Updates
github/codeql-actiontov4.35.4. - #5959 Updates
grafana/shared-workflows/dockerhub-logintov1.0.4. - #6009 Moves the browser
PageScreenshotOptionsparsing into the mapping layer. - #5964 Adds the k6 feature-flags specification under
openspec/. - #6067 Forces the legacy
x/net/http2implementation on thegotipCI test job. - #6065 Lets Renovate update the
Dockerfileon the v1.x branch. - #6031 Updates
go.opentelemetry.io/oteltov1.44.0. - #6086 Updates
golang.org/xdependencies (cryptotov0.53.0,nettov0.56.0,termtov0.44.0). - #6061 Updates
golang.org/x/synctov0.21.0. - #6030 Updates
github.com/tidwall/gjsontov1.19.0. - #6029 Updates
github.com/mccutchen/go-httpbin/v2tov2.23.0. - [#6063](https://gi...
v1.8.0
k6 v1.8.0 is here! This maintenance release on v1.x includes:
- Cloud secrets are now automatically available in
k6 cloud run --local-execution— use--no-cloud-secretsto opt out. - Pre-manifest extension dependencies are captured in archive metadata for cloud consumers.
- Structured logging from k6provider for better visibility into automatic extension provisioning.
- Multiple bug fixes for Browser, WebSocket, and Cloud modules.
- Dependency updates and reliability improvements.
Breaking changes
There are no breaking changes in this release.
New features
Cloud secrets now automatically available in local execution #5738 & #5875
When running k6 cloud run --local-execution, secrets stored in Grafana Cloud are now automatically available to your script via secrets.get() without any additional configuration.
To opt out, pass --no-cloud-secrets:
k6 cloud run --local-execution --no-cloud-secrets script.jsThanks, @vortegatorres!
Pre-manifest extension dependencies captured in archive metadata #5819 & #5977
When creating a k6 archive (k6 archive), extension dependency information is now captured in metadata.json under a "dependencies" field, using the constraints declared by the script before any external manifest overrides are applied. This ensures that k6/x/ imports are preserved correctly during auto-extension-resolution re-execution.
UX improvements and enhancements
- #5815 & #5993 Warns when
http.get()orhttp.head()receive extra arguments that are silently ignored, helping catch common scripting mistakes. Thanks, @moko-poi! - #5657 Adds automatic retries to
newAction-based Locator APIs in the browser module, improving reliability of browser tests. Thanks, @janHildebrandt98! - #5845 Wires k6provider's structured logging into k6's logger. Provisioning operations (artifact resolution, cache hits, downloads, retries, and cache pruning) now appear in k6's log output at the correct level.
Bug fixes
- #5855 Fixes auto-extension-resolution incorrectly triggering binary provisioning when a script manifest specifies a dependency as a Go module pseudo-version (
v0.0.0+shaorv0.0.0-timestamp-sha), even when the running binary already satisfied the constraint. - #5574 Fixes position parser for base pointer options. Thanks, @chrismooreproductions!
- #5630 & #5987 Fixes duplicate redirect request metric emissions in the browser module, where each redirect was incorrectly emitting metrics for all prior redirects in the chain.
- #5620 & #5991 Preserves context cancellation causes across the scheduler, browser, and secret-source layers, improving the accuracy of error messages surfaced when a test is interrupted. Thanks, @LBaronceli!
- #5716 & #5990 Fixes WebSocket
bufferedAmountnot being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng! - #5814 & #5975 Fixes
k6 cloud run --local-executionignoringK6_CLOUD_PUSH_REF_IDand unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05! - #5786 Fixes swapped Min and Max values for Gauge metrics in Cloud output v2, which caused incorrect peak and floor values in cloud test result queries. Thanks, @esquonk!
- #5785 Fixes a race condition in the browser module's
handleExitEventwhereDone()was signalled before the subscription was removed, causing tests to hang until the timeout. - #5905 Fixes a deadlock where WebSocket connections hang forever during teardown when the server sends pings. A server ping arriving during shutdown could permanently stall the k6 process.
- #5923 & #5989 Fixes a nil pointer panic in
ElementHandle.DefaultTimeout(and any method that calls it, such asGetAttribute) when invoked on a nil or partially-initialized handle. Thanks, @SAY-5!
Maintenance and internal improvements
- #5896 & #5897 Updates
go.opentelemetry.io/otel/exporters/otlp/otlptracehttpandotlpmetrichttptov1.43.0[security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers. - #5857, #5950 Updates Go to
v1.25.10. - #5946 Updates
golang.org/x/nettov0.55.0[security]. - #6042 Updates Go toolchain to
v1.25.11[security]. - #6047 Updates
golang.org/x/cryptotov0.52.0[security]. - #5863, #5880, #5908 Updates
github.com/grafana/k6providertov0.5.0. - #5740, #5796, #5910 Updates
github.com/grafana/sobekdigest. - #6035 Move Docker Hub publishing to GAR mirror.
- #6064 Bumps Dockerfile images for security updates.
External contributors
A huge thank you to the external contributors who helped during this release: @moko-poi, @janHildebrandt98, @Reranko05, @prakharbirla-ng, @esquonk, @chrismooreproductions, @vortegatorres, @SAY-5, and @LBaronceli! 🙏
v2.0.0
k6 v2.0.0 is here 🎉!
k6 v2.0.0 is the final release of the v2 major version, completing the cleanup of deprecated APIs, old commands, and obsolete configuration options that was started with v2.0.0-rc1. If you were already running the release candidate, this release includes a handful of additional changes on top — they are marked with (new since v2.0.0-rc1) throughout these notes.
Here's a glimpse of what's changed in this release:
- The Go module path has changed to
go.k6.io/k6/v2— all extensions must update their import paths to be compatible with v2. - Removal of all long-deprecated CLI commands and flags:
k6 login,k6 pause,k6 resume,k6 scale,k6 status,--no-summary,--upload-only, and more. - The
externally-controlledexecutor has been removed — scripts usingexecutor: externally-controlledwill no longer run. - Cloud run non-threshold aborts (aborted by user, system, timeout, etc.) now return exit code
97instead of0. options.ext.loadimpactis no longer supported — useoptions.cloud.k6/experimental/redismodule has been removed.- The
k6 cloud script.jspositional form has been fully removed — usek6 cloud run script.js. - A stack is now required for all
k6 cloudcommands — the previous fallback to the first available stack has been removed. - The web-vitals library has been updated to v5.1.0, removing the deprecated FID metric.
- (new since v2.0.0-rc1) easyjson has been dropped in favor of stdlib
encoding/json— extension authors relying on easyjson-generated methods on k6 types must update. - (new since v2.0.0-rc1) The k6 HTTP API server no longer starts by default — pass
--addressto enable it. - (new since v2.0.0-rc1) New
k6 cloud project listcommand to list Grafana Cloud k6 projects. - (new since v2.0.0-rc1) Cloud secrets are now automatically available in
k6 cloud run --local-execution; use--no-cloud-secretsto opt out.
Breaking changes
These are changes that require you to update your scripts, CI/CD pipelines, or configuration files before upgrading.
Go module path changed to go.k6.io/k6/v2 #5777
Following the Go module versioning conventions, the k6 module path has changed from go.k6.io/k6 to go.k6.io/k6/v2.
Any extension or external package that imports go.k6.io/k6 must update all import paths to go.k6.io/k6/v2. For the vast majority of extensions this is the only change needed — a mechanical find-and-replace across the codebase:
go.k6.io/k6/ → go.k6.io/k6/v2/
For example:
// Before
import "go.k6.io/k6/js/modules"
// After
import "go.k6.io/k6/v2/js/modules"Removed CLI commands #5653
The following commands for controlling a running test have been removed. They have not been functional for most use cases since the REST API they relied on was limited to specific execution modes:
k6 pausek6 resumek6 scalek6 status
Migration: There is no replacement. These commands relied on the externally-controlled executor, which has also been removed in v2.0.0 (see below).
Removed externally-controlled executor #5846
The externally-controlled executor has been removed. It was legacy code from an older k6 Cloud architecture that allowed external systems to scale VUs and pause/resume a running test via the k6 REST API — the capability that k6 pause, k6 resume, k6 scale, and k6 status relied on.
Migration: There is no replacement. Any test script with executor: externally-controlled will fail to start. Migrate to a different executor based on the desired load profile (e.g., ramping-vus, constant-vus, constant-arrival-rate).
Removed k6 login command #5134
The top-level k6 login command and its subcommands (k6 login cloud, k6 login influxdb) have been removed.
Migration:
- Replace
k6 login cloud→k6 cloud login - InfluxDB authentication is no longer configurable via a login command. Use environment variables such as
K6_INFLUXDB_*to configure the InfluxDB output directly.
Removed k6 cloud script.js positional form #5624, #5912 (completed in v2.0.0)
The old positional-argument form k6 cloud script.js has been fully removed. In v2.0.0-rc1 it was changed to show help instead of running; in v2.0.0 the deprecated command handler itself has been removed entirely. The run subcommand has been the recommended path since k6 cloud run was introduced.
Migration: Replace k6 cloud script.js with k6 cloud run script.js.
Removed --upload-only flag #5844
The --upload-only flag on the k6 cloud command has been removed.
Migration: Use k6 cloud upload script.js to upload a test without running it.
Removed --no-summary flag #5729
The --no-summary flag has been removed.
Migration: Replace --no-summary with --summary-mode=disabled.
Removed --summary-mode=legacy #5730
The legacy value for --summary-mode has been removed.
Migration: There is no direct equivalent — the new summary format is different from the legacy one. Review the available summary modes and choose the one that best fits your needs: compact (the default) or full for more detailed output.
Removed options.ext.loadimpact support #5774
The options.ext.loadimpact configuration block in test scripts is no longer supported.
Migration: Move all cloud-related configuration from options.ext.loadimpact to options.cloud:
// Before
export const options = {
ext: {
loadimpact: {
projectID: 12345,
name: "My Test",
},
},
};
// After
export const options = {
cloud: {
projectID: 12345,
name: "My Test",
},
};Removed k6/experimental/redis module #5485
The k6/experimental/redis module has been removed from the k6 core binary. It was shipped as an experiment and has not been promoted to stable.
Migration: Change your import from k6/experimental/redis to k6/x/redis. With auto-extension-resolution, k6 will automatically provision the xk6-redis extension when it sees the k6/x/redis import.
Removed ExporterType option from OpenTelemetry output #5754
The deprecated exporterType configuration option for the OpenTelemetry output has been removed.
Migration: Replace K6_OTEL_EXPORTER_TYPE with K6_OTEL_EXPORTER_PROTOCOL. The accepted values are grpc and http/protobuf.
Removed SingleCounterForRate option from OpenTelemetry output #5830
The temporary SingleCounterForRate escape-hatch option for the OpenTelemetry output has been removed. It was introduced as a one-release migration aid in #5164 to let users revert to the old pair-of-counters format (<metric>.occurred + <metric>.total) while upgrading. Rate metrics are now always exported as a single counter with a condition attribute (nonzero/zero).
Migration: Remove any K6_OTEL_SINGLE_COUNTER_FOR_RATE=true configuration. If you were using the old pair-of-counters format, update your dashboards and queries to use the single counter with condition attribute instead.
Removed K6_BINARY_PROVISIONING environment variable #5734
The K6_BINARY_PROVISIONING environment variable, deprecated in v1.2.0, has been removed.
Migration: Remove K6_BINARY_PROVISIONING from your environment. Auto-extension-resolution is enabled by default; K6_AUTO_EXTENSION_RESOLUTION only needs to be set explicitly if you want to disable it.
Removed K6_ENABLE_COMMUNITY_EXTENSIONS environment variable #5733
The K6_ENABLE_COMMUNITY_EXTENSIONS environment variable has been removed. The community and cloud extension catalogs were merged server-side, making this flag a no-op since the catalogs were unified.
Migration: Remove K6_ENABLE_COMMUNITY_EXTENSIONS from your environment. Community extensions are now resolved through the default build service URL automatically.
Stack is now required for all k6 cloud commands #5833
Providing a stack is now mandatory for all k6 cloud commands (k6 cloud run, k6 cloud upload, k6 cloud run --local-execution). Previously, omitting a stack would fall back to the first available stack with a deprecation warning — that fallback has been removed. Likewise, k6 cloud login now requires both a token and a stack; passing one without the other fails with an explicit error.
Migration: Run k6 cloud login which will ask you for the stack and setup correctly for the new version of k6. Alternatively, the K6_CLOUD_STACK_ID environment variable, or the stackID script option are available to be set before running any k6 cloud command .
Removed legacy configuration file path migration #5609
k6 no longer automatically migrates configuration files from the old {USER_CONFIG_DIR}/loadimpact/config.json path introduced before k6 v1.0.0.
Migration: If you still have a config file at the old path, move it to {USER_CONFIG_DIR}/k6/config.json. You can also r...
v2.0.0-rc1
k6 v2.0.0-rc1 is here 🎉!
This release marks the first release candidate for k6 v2.0.0 — a major version that completes a long-running cleanup of deprecated APIs, old commands, and obsolete configuration options. Like the v1.0.0-rc1 before it, the purpose of this release is to give the community a chance to test the upgrade path, identify any issues, and migrate scripts or workflows affected by breaking changes. If you encounter any problems, please report them.
Here's a glimpse of what's changed in this release:
- The Go module path has changed to
go.k6.io/k6/v2— all extensions must update their import paths to be compatible with v2. - Removal of all long-deprecated CLI commands and flags:
k6 login,k6 pause,k6 resume,k6 scale,k6 status,--no-summary,--upload-only, and more. - The
externally-controlledexecutor has been removed — scripts usingexecutor: externally-controlledwill no longer run. - Cloud run non-threshold aborts (aborted by user, system, timeout, etc.) now return exit code
97instead of0. options.ext.loadimpactis no longer supported — useoptions.cloud.k6/experimental/redismodule has been removed.- The
k6 cloudcommand now shows help by default instead of attempting to run. - A stack is now required for all
k6 cloudcommands — the previous fallback to the first available stack has been removed. - The web-vitals library has been updated to v5.1.0, removing the deprecated FID metric.
Breaking changes
These are changes that require you to update your scripts, CI/CD pipelines, or configuration files before upgrading.
Go module path changed to go.k6.io/k6/v2 #5777
Following the Go module versioning conventions, the k6 module path has changed from go.k6.io/k6 to go.k6.io/k6/v2.
Any extension or external package that imports go.k6.io/k6 must update all import paths to go.k6.io/k6/v2. For the vast majority of extensions this is the only change needed — a mechanical find-and-replace across the codebase:
go.k6.io/k6/ → go.k6.io/k6/v2/
For example:
// Before
import "go.k6.io/k6/js/modules"
// After
import "go.k6.io/k6/v2/js/modules"As a result, no existing extensions will be compatible with v2.0.0-rc1. As we move toward the final v2.0.0 release, we expect most extensions to either already support the new path or to do so shortly after.
Removed CLI commands #5653
The following commands for controlling a running test have been removed. They have not been functional for most use cases since the REST API they relied on was limited to specific execution modes:
k6 pausek6 resumek6 scalek6 status
Migration: There is no replacement. These commands relied on the externally-controlled executor, which has also been removed in v2.0.0 (see below).
Removed externally-controlled executor #5846
The externally-controlled executor has been removed. It was legacy code from an older k6 Cloud architecture that allowed external systems to scale VUs and pause/resume a running test via the k6 REST API — the capability that k6 pause, k6 resume, k6 scale, and k6 status relied on.
Migration: There is no replacement. Any test script with executor: externally-controlled will fail to start. Migrate to a different executor based on the desired load profile (e.g., ramping-vus, constant-vus, constant-arrival-rate).
Removed k6 login command #5134
The top-level k6 login command and its subcommands (k6 login cloud, k6 login influxdb) have been removed.
Migration:
- Replace
k6 login cloud→k6 cloud login - InfluxDB authentication is no longer configurable via a login command. Use environment variables such as
K6_INFLUXDB_*to configure the InfluxDB output directly.
k6 cloud script.js no longer runs a test #5624
Running k6 cloud without an explicit subcommand (i.e., the old positional-argument form k6 cloud script.js) now shows the help output instead of attempting to run the test. The run subcommand was already the recommended path since k6 cloud run was introduced.
Migration: Replace k6 cloud script.js with k6 cloud run script.js.
Removed --upload-only flag #5844
The --upload-only flag on the k6 cloud command has been removed.
Migration: Use k6 cloud upload script.js to upload a test without running it.
Removed --no-summary flag #5729
The --no-summary flag has been removed.
Migration: Replace --no-summary with --summary-mode=disabled.
Removed --summary-mode=legacy #5730
The legacy value for --summary-mode has been removed.
Migration: There is no direct equivalent — the new summary format is different from the legacy one. Review the available summary modes and choose the one that best fits your needs: compact (the default) or full for more detailed output.
Removed options.ext.loadimpact support #5774
The options.ext.loadimpact configuration block in test scripts is no longer supported.
Migration: Move all cloud-related configuration from options.ext.loadimpact to options.cloud:
// Before
export const options = {
ext: {
loadimpact: {
projectID: 12345,
name: "My Test",
},
},
};
// After
export const options = {
cloud: {
projectID: 12345,
name: "My Test",
},
};Removed k6/experimental/redis module #5485
The k6/experimental/redis module has been removed from the k6 core binary. It was shipped as an experiment and has not been promoted to stable.
Migration: Change your import from k6/experimental/redis to k6/x/redis. With auto-extension-resolution, k6 will automatically provision the xk6-redis extension when it sees the k6/x/redis import.
Removed ExporterType option from OpenTelemetry output #5754
The deprecated exporterType configuration option for the OpenTelemetry output has been removed.
Migration: Replace K6_OTEL_EXPORTER_TYPE with K6_OTEL_EXPORTER_PROTOCOL. The accepted values are grpc and http/protobuf.
Removed SingleCounterForRate option from OpenTelemetry output #5830
The temporary SingleCounterForRate escape-hatch option for the OpenTelemetry output has been removed. It was introduced as a one-release migration aid in #5164 to let users revert to the old pair-of-counters format (<metric>.occurred + <metric>.total) while upgrading. Rate metrics are now always exported as a single counter with a condition attribute (nonzero/zero).
Migration: Remove any K6_OTEL_SINGLE_COUNTER_FOR_RATE=true configuration. If you were using the old pair-of-counters format, update your dashboards and queries to use the single counter with condition attribute instead.
Removed K6_BINARY_PROVISIONING environment variable #5734
The K6_BINARY_PROVISIONING environment variable, deprecated in v1.2.0, has been removed.
Migration: Remove K6_BINARY_PROVISIONING from your environment. Auto-extension-resolution is enabled by default; K6_AUTO_EXTENSION_RESOLUTION only needs to be set explicitly if you want to disable it.
Removed K6_ENABLE_COMMUNITY_EXTENSIONS environment variable #5733
The K6_ENABLE_COMMUNITY_EXTENSIONS environment variable has been removed. The community and cloud extension catalogs were merged server-side, making this flag a no-op since the catalogs were unified.
Migration: Remove K6_ENABLE_COMMUNITY_EXTENSIONS from your environment. Community extensions are now resolved through the default build service URL automatically.
Stack is now required for all k6 cloud commands #5833
Providing a stack is now mandatory for all k6 cloud commands (k6 cloud run, k6 cloud upload, k6 cloud run --local-execution). Previously, omitting a stack would fall back to the first available stack with a deprecation warning — that fallback has been removed. Likewise, k6 cloud login now requires both a token and a stack; passing one without the other fails with an explicit error.
Migration: Run k6 cloud login which will ask you for the stack and setup correctly for the new version of k6. Alternatively, the K6_CLOUD_STACK_ID environment variable, or the stackID script option are available to be set before running any k6 cloud command .
Removed legacy configuration file path migration #5609
k6 no longer automatically migrates configuration files from the old {USER_CONFIG_DIR}/loadimpact/config.json path introduced before k6 v1.0.0.
Migration: If you still have a config file at the old path, move it to {USER_CONFIG_DIR}/k6/config.json. You can also re-run k6 cloud login to regenerate the file at the correct location.
Cloud run non-threshold aborts now exit with code 97 #5769
Pre...
v1.7.1
k6 v1.7.1 is here 🎉! This release includes:
- Dependency updates for
google.golang.org/grpc.
Maintenance and internal improvements
- #5746 Updates
google.golang.org/grpcwhich contains a fix for CVE-2026-33186.
v1.7.0
k6 v1.7.0 is here 🎉! This release includes:
- Automatic resolution for subcommand extensions — no more manual
xk6builds required to use them! K6_SECRET_SOURCEenv var as an alternative to--secret-source, accepting the same syntax.
Breaking changes
There are no breaking changes in this release.
New features
Automatic resolution for subcommand extensions #5664
You can now rely on automatic extension resolution
also when using a subcommand extension
that isn't included in the current binary.
Previously, using extension subcommands required manually building a custom k6 binary with xk6. Now, k6 detects the
missing extension, provisions the binary on demand, and executes the command transparently — the same experience already
available for JavaScript extensions.
For instance, if you run:
k6 x httpbinand the xk6-subcommand-httpbin subcommand extension isn't in the
current binary, k6 will automatically provision a binary with it on demand and execute the command transparently.
UX improvements and enhancements
- #5655 Recommends the OpenTelemetry output instead of InfluxDB.
- #5724 Adds
K6_SECRET_SOURCEenv var as an alternative to--secret-source, accepting the same syntax. Thanks @vortegatorres, for the contribution!
Bug fixes
- #5629 Ensures that all redirected requests are handled for
page.on('response')andpage.on('requestfinished').
Maintenance and internal improvements
- #5447 Updates the
actions/setup-goaction to7a3fe6c. - #5471 Uses WPT's harness to test the WebCrypto API. Thanks @bjchris32, for the contribution!
- #5583, #5687 Move
SizeandPageEmulateMediaOptionsoption parsing to the Browser's mapping layer. Thanks @baeseokjae, for the contribution! - #5598 Updates the
anchore/sbom-actionaction tov0.22.2. - #5599 Updates
github.com/klauspost/compresstov1.18.4. - #5600 Updates the
docker/login-actionaction tov3.7.0. - #5610 Improves error wrapping for Browser's actionability functions. Thanks @joaquinalmora, for the contribution!
- #5612 Adds a
AGENTS.mdfor guiding coding agents. - #5615 Updates the
actions/checkoutaction tode0fac2. - #5618 Updates
protoreflecttov1.18.0. - #5619 Updates
google.golang.org/grpctov1.78.0. - #5626 Uses
cmd/stateconstants when possible. - #5643, #5647 Fix goroutine leak when running the browser tests.
- #5646 Update the Go toolchain version to
1.24.13. - #5654, #5666 Update the tag of the Docker image for Go.
- #5656 Fixes browser tests teardown races.
- #5660 Trims away the use of
aferooutside of thefsextmodule. - #5678 Updates
golangci-lintto2.10.1. - #5688 Updates
go.opentelemetry.io/otel/sdktov1.40.0. - #5689 Fixes browser tests by increasing the CI test run timeout.
- #5691 Updates the
actions/staleaction tob5d41d4. - #5692 Updates the
grafana/shared-workflows/get-vault-secretsaction tov1.3.1. - #5695 Updates the
github/codeql-actionaction tov4.32.4. - #5702 Bumps the Go min version to
1.25and default to1.26, and fixes lint issues enabled by the new minimum. - #5708 Bumps TC39 tests.
- #5728 Updates
xk6-dashboardtov0.8.1. - #5735 Excludes non-essential administrative and metadata files from the
vendordirectory.
Roadmap
k6 v2.0.0 is the next planned release. It will include a set of breaking changes that have been discussed and planned.
You can find the list of planned breaking changes in #5062.
v1.6.1
k6 v1.6.1 is here! This patch release includes:
- Bug fix for a race condition in the experimental CSV module
- Bug fix for manifest k6 version override
- Version updates for Go toolchain and Docker images
Bug fixes
- #5632 Fixes a race condition in the
experimental/csvmodule when multiple files with async code usecsv.parsein parallel during initialization. - #5642 Fixes an issue where k6 was not always added as a build dependency, preventing manifests from overriding the k6 version.
Maintenance and security updates
v1.6.0
k6 v1.6.0 is here 🎉! This release includes:
- Cloud commands now support configurable default Grafana Cloud stack.
- New
k6 depscommand for analyzing script dependencies. - Browser APIs enhancements with
frameLocator(),goBack(),goForward()methods. - Crypto module adds PBKDF2 support for password-based key derivation.
jslibgets a new TOTP library for time-based one-time password generation and validation.- New mcp-k6 MCP server for AI-assisted k6 script writing.
Breaking changes
There are no breaking changes in this release.
New features
Configurable default stack for Cloud commands #5420
Cloud commands now support configuring the default Grafana Cloud stack you want to use. The stack slug (or stack id) is used by the Cloud to determine which default project to use when not explicitly provided.
Previously, users had to specify the project id for every test run. With this change, you can configure a default stack during login, and k6 will use it to automatically resolve the appropriate default project. This is particularly useful for organizations with multiple Grafana Cloud stacks or when working across different teams and environments.
Users can also set up a specific stack for every test run, either using the new option stackID or the environment variable K6_CLOUD_STACK_ID.
Please note that, in k6 v2, this stack information will become mandatory to run a test.
# Login interactively and select default stack
k6 cloud login
# Login and set default stack with token
k6 cloud login --token $MY_TOKEN --stack my-stack-slug
# Run test using the configured default stack
k6 cloud run script.js
# Run test using a specific stack
K6_CLOUD_STACK_ID=12345 k6 cloud run script.js
# Stack id can also be set in the options
export const options = {
cloud: {
stackID: 123,
projectID: 789, // If the project does not belong to the stack, this will throw an error
},
};This simplifies the cloud testing workflow and prepares k6 for upcoming changes to the Grafana Cloud k6 authentication process, where the stack will eventually become mandatory.
k6 deps command and manifest support #5410, #5427
A new k6 deps command is now available to analyze and list all dependencies of a given script or archive. This is particularly useful for understanding which extensions are required to run a script, especially when using auto extension resolution.
The command identifies all imports in your script and lists dependencies that might be needed for building a new binary with auto extension resolution. Like auto extension resolution itself, this only accounts for imports, not dynamic require() calls.
# Analyze script dependencies
k6 deps script.js
# Output in JSON format for programmatic consumption
k6 deps --json script.js
# Analyze archived test dependencies
k6 deps archive.tarThis makes it easier to understand extension requirements, share scripts with clear dependency information, and integrate k6 into automated build pipelines.
In addition, k6 now supports a manifest that specifies default version constraints for dependencies when no version is defined in the script using pragmas. If a dependency is imported without an explicit version, it defaults to "*", and the manifest can be used to replace that with a concrete version constraint.
The manifest is set through an environment variable as JSON with keys being a dependency and values being constraints:
K6_DEPENDENCIES_MANIFEST='{"k6/x/faker": ">=v0.4.4"}' k6 run scripts.js
In this example, if the script only imports k6/x/faker and does not use a use k6 with k6/x/faker ... directive, it will set the version constraint to >=v0.4.4. It will not make any changes if k6/x/faker is not a dependency of the script at all.
Browser module: frameLocator() method #5487
The browser module now supports frameLocator() on Page, Frame, Locator, and FrameLocator objects. This method creates a locator for working with iframe elements without the need to explicitly switch contexts, making it much easier to interact with embedded content.
Frame locators are particularly valuable when testing applications with nested iframes, as they allow you to chain locators naturally while maintaining readability:
Click to expand example code
import { browser } from 'k6/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://example.com');
// Locate an iframe and interact with elements inside it
const frame = page.frameLocator('#payment-iframe');
await frame.locator('#card-number').fill('4242424242424242');
await frame.locator('#submit-button').click();
// Chain frame locators for nested iframes
const nestedFrame = page
.frameLocator('#outer-frame')
.frameLocator('#inner-frame');
await nestedFrame.locator('#nested-content').click();
} finally {
await page.close();
}
}This complements existing frame handling methods and provides a more intuitive API for working with iframe-heavy applications.
Browser module: goBack() and goForward() navigation methods #5494
The browser module now supports page.goBack() and page.goForward() methods for browser history navigation. These methods allow you to navigate the page's history, similar to clicking the browser's back/forward buttons.
Click to expand example code
import { browser } from 'k6/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://example.com');
await page.goto('https://example.com/page2');
// Navigate back to the previous page
await page.goBack();
// Navigate forward again
await page.goForward();
// Both methods support optional timeout and waitUntil parameters
await page.goBack({ waitUntil: 'networkidle' });
} finally {
await page.close();
}
}Browser module: Request event handlers #5481, #5486
The browser module now supports page.on('requestfailed') and page.on('requestfinished') event handlers, enabling better monitoring and debugging of network activity during browser tests.
The requestfailed event fires when a request fails (network errors, aborts, etc.), while requestfinished fires when a request completes successfully.
Click to expand example code
import { browser } from 'k6/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
// Monitor failed requests
page.on('requestfailed', (request) => {
console.log(`Request failed: ${request.url()}`);
});
// Monitor successful requests
page.on('requestfinished', (request) => {
console.log(`Request finished: ${request.url()}`);
});
await page.goto('https://example.com');
await page.close();
}These event handlers provide deeper insights into network behavior during browser testing and help identify issues that might not be immediately visible.
Crypto module: PBKDF2 support #5380
The crypto module now supports PBKDF2 for deriving cryptographic keys from passwords. PBKDF2 is widely used for password hashing and key derivation in security-sensitive applications, and this addition enables testing of systems that use PBKDF2 for authentication or encryption.
For usage examples, check out the one provided in the repository or refer to the documentation.
WebSockets module is now stable #5586
The websockets module has been promoted to stable status and is now available via the k6/websockets path.
The experimental k6/experimental/websockets module will be removed in a future release. Users should migrate to the stable k6/websockets module.
To migrate, simply update your import statement:
// Old (experimental)
import ws from 'k6/experimental/websockets';
// New (stable)
import ws from 'k6/websockets';No other changes are required because the API is the same.
Console logging: ArrayBuffer and TypedArray support #5496
console.log() now properly displays ArrayBuffer and TypedArray objects, making it easier to debug binary data handling in your test scripts. Previously, these types would not display useful information, making debugging difficult when working with binary protocols, file uploads, or WebS...