Open source

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-controlled executor has been removed — scripts using executor: externally-controlled will no longer run.
  • Cloud run non-threshold aborts (aborted by user, system, timeout, etc.) now return exit code 97 instead of 0.
  • options.ext.loadimpact is no longer supported — use options.cloud.
  • k6/experimental/redis module has been removed.
  • The k6 cloud script.js positional form has been fully removed — use k6 cloud run script.js.
  • A stack is now required for all k6 cloud commands — 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 --address to enable it.
  • (new since v2.0.0-rc1) New k6 cloud project list command 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-secrets to 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:

Go
// 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 pause
  • k6 resume
  • k6 scale
  • k6 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 cloudk6 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:

JavaScript
// 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

Previously, most cloud abort scenarios returned an exit code of 0, making it impossible for CI pipelines to distinguish a failed run from a successful one. These scenarios now return exit code 97.

ScenarioBeforeAfter
Finished00
Aborted by threshold9999
Aborted by system, limit, script error, user, or timeout097

Migration: Review any CI pipeline logic that treats exit code 0 as success for cloud runs. Cloud runs that abort for reasons other than threshold violations will now return exit code 97.

Browser: FID metric removed, web vitals updated to v5.1.0 #5661

The browser_web_vital_fid metric has been removed. FID (First Input Delay) was deprecated as a Core Web Vital and replaced by INP (Interaction to Next Paint). The embedded web-vitals library has been updated from v3 to v5.1.0, which reflects this change.

Migration: Update any dashboards, alerts, or threshold rules that reference browser_web_vital_fid to use browser_web_vital_inp instead.

Dropped easyjson in favor of stdlib encoding/json (new since v2.0.0-rc1) #5853

k6 no longer uses easyjson for JSON serialization. Several types in the public Go API previously had easyjson-generated MarshalJSON/UnmarshalJSON methods; those are now gone, replaced by stdlib encoding/json. This change was intentionally held until v2 to avoid a breaking change mid-cycle.

Migration: Extension authors who relied on easyjson-generated methods on k6 types should remove those dependencies. Standard encoding/json marshaling applies to all affected types.

HTTP API server no longer starts by default (new since v2.0.0-rc1) #5876

The k6 HTTP API server no longer starts automatically. Previously, k6 always listened on localhost:6565 unless opted out. The server now only starts when an address is explicitly provided via the --address flag or the K6_ADDRESS environment variable.

Migration: If you use the k6 REST API, pass --address=localhost:6565 (or any address) to re-enable the server.

New features

Cloud secret source #5738

A new built-in secret source, cloud, allows k6 scripts to retrieve secrets stored in Grafana Cloud directly during local execution. Pass --secret-source=cloud when running k6 cloud run --local-execution to make cloud-managed secrets available to your script via secrets.get(). Thanks, @vortegatorres!

shell
k6 cloud run --local-execution --secret-source=cloud script.js

Cloud secret source enabled by default for local execution (new since v2.0.0-rc1) #5875

--secret-source=cloud is no longer required. When running k6 cloud run --local-execution, the cloud secret source is now automatically available and secrets.get() works out of the box.

To opt out, pass --no-cloud-secrets:

shell
k6 cloud run --local-execution --no-cloud-secrets script.js

k6 cloud project list command (new since v2.0.0-rc1) #5650

A new k6 cloud project command group has been added, with a k6 cloud project list subcommand for listing Grafana Cloud k6 projects. The output supports both human-readable table format and JSON (--format=json).

shell
k6 cloud project list
k6 cloud project list --format=json

Extension tab-completion provisioning #5761

When using shell tab-completion, pressing TAB after a fully-typed extension name (e.g. k6 x docs <TAB>) will now automatically provision a custom binary with that extension and delegate the completion request to it. This means extension-specific flags and subcommands are available via tab-completion without any manual setup.

Pre-manifest extension dependencies captured in archive metadata #5819

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 Warns when http.get() or http.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!
  • #5779 Adds a k6 x docs hint to the main k6 help output for built-in documentation discovery. Thanks, @Reranko05!
  • #5788 Adds a k6 x explore hint to the main k6 help output for extension discovery.
  • (new since v2.0.0-rc1) #5869 Improves cloud authentication error messages — instead of a single generic error, k6 now reports specifically whether a token or a stack ID is missing.
  • (new since v2.0.0-rc1) #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.
  • (new since v2.0.0-rc1) Exposes the host k6 version to provisioned subcommands via K6_PROVISION_HOST_VERSION, so extension subcommands can correctly identify the caller’s k6 version when showing docs or feature information.
  • (new since v2.0.0-rc1) Tab completion no longer blocks on a build when the requested extension binary is not in the local cache — completions bail out instantly on a cache miss instead of waiting 10–30 seconds.

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+sha or v0.0.0-timestamp-sha), even when the running binary already satisfied the constraint.
  • #5769 Fixes several k6 cloud run TUI issues: ghost duplicate progress bars appearing after the run finishes, a timer that stalled and then jumped to 100%, and a stale timer racing with the status line.
  • #5814 Fixes k6 cloud run --local-execution ignoring K6_CLOUD_PUSH_REF_ID and unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05!
  • #5716 Fixes WebSocket bufferedAmount not being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng!
  • #5630 Fixes duplicate redirect request metric emissions in the browser module, where each redirect was incorrectly emitting metrics for all prior redirects in the chain.
  • #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 handleExitEvent where Done() was signalled before the subscription was removed, causing tests to hang until the timeout.
  • #5574 Fixes position parser for base pointer options. Thanks, @chrismooreproductions!
  • (new since v2.0.0-rc1) #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.
  • (new since v2.0.0-rc1) #5923 Fixes a nil pointer panic in ElementHandle.DefaultTimeout (and any method that calls it, such as GetAttribute) when invoked on a nil or partially-initialized handle. Thanks, @SAY-5!
  • (new since v2.0.0-rc1) #5620 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!

Maintenance and internal improvements

  • #5847 Moves TaskQueue from the external github.com/mstoykov/k6-taskqueue-lib into internal/js/taskqueue, eliminating an awkward reverse dependency where the library’s own tests depended on k6.
  • #5775 Moves xk6-dashboard source code into internal/dashboard, making the web dashboard a built-in part of the k6 binary without requiring a separate extension. The dashboard is available via k6 run --out=web-dashboard.
  • #5703, #5818, #5817 Uses Go’s synctest package within lib/executor, eventloop, timer, PeriodicFlusher, output/cloud, and output/cloud/expv2 tests for virtualized time, improving test determinism and CI speed.
  • #5778 Bumps Ubuntu CI runners to 24.04 and always installs Chromium for browser tests.
  • #5826, #5827 Updates go.opentelemetry.io/otel/exporters/otlp/otlptracehttp and otlpmetrichttp to v1.43.0 [security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers.
  • #5669, #5911 Updates go.opentelemetry.io/otel packages.
  • #5740, #5796, #5910 Updates github.com/grafana/sobek digest.
  • #5783, #5838, #5862 Updates github.com/grafana/k6-cloud-openapi-client-go.
  • #5616, #5805 Updates github.com/evanw/esbuild to v0.28.0.
  • #5667 Updates golangci-lint.
  • #5799 Updates buf.build/gen/go/prometheus protobuf.
  • #5800 Updates github.com/andybalholm/brotli to v1.2.1.
  • #5804 Updates examples/ dependencies.
  • #5806 Updates github.com/fatih/color to v1.19.0.
  • #5807 Updates github.com/puerkitobio/goquery to v1.12.0.
  • #5840, #5931 Updates github.com/mattn/go-isatty to v0.0.22.
  • #5857, #5950 Updates Go to v1.25.10.
  • #5863, #5880, #5908 Updates github.com/grafana/k6provider to v0.5.0.
  • #5670 Updates GitHub artifact actions (major).
  • #5693 Updates crazy-max/ghaction-chocolatey to v4.
  • #5706 Updates actions/setup-go to 4a36011.
  • #5707 Updates anchore/sbom-action to v0.24.0.
  • #5718, #5861 Updates github/codeql-action to v4.35.2.
  • #5720, #5802 Updates docker/login-action to v4.1.0.
  • #5771, #5798 Updates grafana/shared-workflows actions.
  • #5836 Updates actions/upload-artifact.
  • #5809, #5839, #5951 Updates the Docker base image tags (Debian to v13, Go to v1.26.3).
  • #5872 Fixes v6 cloud client losing the request body when retrying after a Connection: close response.
  • #5871 Returns an accurate error from the v6 cloud client when a test name lookup returns no results.
  • #5751 Updates github.com/klauspost/compress to v1.18.5.
  • #5841 Updates golang.org/x packages.
  • #5860 Updates Alpine Docker image to v3.23.4.
  • (new since v2.0.0-rc1) #5864 Configures the v1 release branch to receive security updates via Renovate.
  • (new since v2.0.0-rc1) #5891 Sets K6ModPath to the v2 module path in the k6provider config so that k6 v2 correctly signals to k6build which module to resolve against (go.k6.io/k6/v2).
  • (new since v2.0.0-rc1) #5947 Reworks the release workflow so pre-releases and older-major maintenance releases no longer claim Docker :latest or the GitHub “Latest release” marker. Adds a floating :vN Docker tag (e.g. grafana/k6:v1) for users who want to pin to a major line.
  • (new since v2.0.0-rc1) #5945 Updates golang.org/x/net to v0.53.0 [security] (CVE-2026-33814).
  • (new since v2.0.0-rc1) #5932 Updates github.com/Masterminds/semver/v3 to v3.5.0.
  • (new since v2.0.0-rc1) #5966 Bumps github.com/Azure/go-ntlmssp to v0.1.1.
  • (new since v2.0.0-rc1) #5965 Bumps the Go version used by the xk6 test workflows to appease security scanners.

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! 🙏