Environment variables are a surprisingly blunt instrument for configuring k6 tests.

Let’s see it in action. Imagine a k6 script that needs to hit a specific API endpoint and use an API key for authentication.

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 10,
  duration: '30s',
};

const API_URL = __ENV.API_URL || 'http://localhost:8080/api';
const API_KEY = __ENV.API_KEY || 'default-secret-key';

export default function () {
  const params = {
    headers: {
      'X-API-Key': API_KEY,
    },
  };

  const res = http.get(`${API_URL}/users`, params);

  check(res, {
    'status is 200': (r) => r.status === 200,
  });

  sleep(1);
}

In this script, __ENV.API_URL and __ENV.API_KEY are how k6 exposes environment variables. If API_URL isn’t set in the environment, it defaults to http://localhost:8080/api. Same for API_KEY.

To run this with custom values, you’d typically set the environment variables before running k6.

On Linux/macOS:

export API_URL="https://staging.example.com/api"
export API_KEY="staging-api-token-123"
k6 run your_script.js

On Windows (Command Prompt):

set API_URL=https://staging.example.com/api
set API_KEY=staging-api-token-123
k6 run your_script.js

On Windows (PowerShell):

$env:API_URL="https://staging.example.com/api"
$env:API_KEY="staging-api-token-123"
k6 run your_script.js

The problem k6 environment variables solve is decoupling configuration from code. Instead of hardcoding URLs, API keys, or other parameters directly into your JavaScript test files, you can inject them at runtime. This is crucial for several reasons:

  • Security: Sensitive credentials like API keys should never be committed to version control. Environment variables allow you to keep them out of your codebase and manage them securely in your deployment environment.
  • Flexibility: You can easily run the same test script against different environments (development, staging, production) simply by changing the environment variables, without modifying the script itself.
  • CI/CD Integration: CI/CD pipelines commonly use environment variables to pass configuration values to test runners. k6 integrates seamlessly with this pattern.

Internally, k6 reads the environment variables before it starts executing your script. The __ENV object is a global object provided by the k6 runtime that maps environment variable names to their string values. When your script accesses __ENV.MY_VARIABLE, k6 looks up the corresponding environment variable in the operating system’s environment.

The || operator in __ENV.API_URL || 'http://localhost:8080/api' is a standard JavaScript way to provide a default value. If __ENV.API_URL is undefined (meaning the environment variable wasn’t set), it falls back to the string on the right.

Consider a scenario where you need to configure multiple VUs and their sleep durations differently. You can do this with environment variables, but it quickly becomes cumbersome.

// advanced_config.js
const VUS_PER_STAGE = parseInt(__ENV.VUS_PER_STAGE || '10', 10);
const DURATION_PER_STAGE = __ENV.DURATION_PER_STAGE || '30s';
const TARGET_URL = __ENV.TARGET_URL || 'http://example.com';

export const options = {
  stages: [
    { duration: DURATION_PER_STAGE, target: VUS_PER_STAGE },
    // ... more stages if needed
  ],
  ext: {
    loadimpact: {
      // Example of another place env vars can be used
      projectID: __ENV.K6_PROJECT_ID,
    },
  },
};

export default function () {
  http.get(TARGET_URL);
  sleep(1);
}

Running this might look like:

export VUS_PER_STAGE="50"
export DURATION_PER_STAGE="1m"
export TARGET_URL="https://api.prod.example.com"
k6 run advanced_config.js

The parseInt(__ENV.VUS_PER_STAGE || '10', 10) pattern is important. Environment variables are always strings. If you expect a number (like for VUs), you must explicitly parse it using parseInt or parseFloat, providing a radix (like 10 for decimal) for parseInt. For durations, k6’s runtime handles the string parsing itself, so you can pass strings like '1m' or '30s'.

While environment variables are powerful for passing simple string or numeric values, they become unwieldy for complex configurations like arrays or nested objects. For those cases, passing a JSON string via an environment variable and then parsing it in your script is a common workaround, though it adds complexity.

The next hurdle you’ll encounter is managing environment variables across different test runs and environments, often leading to the need for external configuration management tools.

Want structured learning?

Take the full K6 course →