SDODS
Guides

Load testing

Run k6 load profiles against an API environment that has opted in.

What you'll learn

How to describe a load profile, opt an environment in, run it with k6, and read the result. SDODS generates and runs the k6 script; it is not a load framework of its own.

Load is destructive. Point it only at an environment you own and that is sized for it, never production. Load against an environment that caps writes (a free-tier plan, a rate limit) measures the cap rather than the system, so get the functional suite green first.

1. Opt the environment in

projects/demo-shop/envs/perf.yaml
ui: { baseUrl: https://perf.shop.example.com }
api:
  baseUrl: https://api.perf.shop.example.com
  headers: { Accept: application/json }
  auth: { type: bearer, token: '${PERF_API_TOKEN}' }
load:
  allowed: true # without this, sdods load refuses to run
  maxVus: 50 # refuse profiles that go higher
  allowWrites: false # refuse POST, PUT, PATCH and DELETE
  allowBaseUrlOverride: false # refuse SDODS_API_BASE_URL pointing at another host

Add the environment to envs.available in sdods.project.yaml like any other.

The opt-in belongs to this file, so the load goes to its api.baseUrl. When SDODS_API_BASE_URL is set to a different URL (a leftover from a functional run, say), sdods load refuses to run and --dry-run reports it. Set allowBaseUrlOverride: true only for an environment whose URL is meant to change per run, such as a preview deployment.

2. Write a profile

projects/demo-shop/load/browse.yaml
description: Browse the catalogue
thinkTimeSeconds: 1
stages:
  - { duration: 30s, target: 10 }
  - { duration: 2m, target: 25 }
  - { duration: 30s, target: 0 }
thresholds:
  http_req_duration: ['p(95)<500']
  http_req_failed: ['rate<0.01']
  checks: ['rate>0.99']
requests:
  - name: list products
    path: /products?limit=${PAGE_SIZE:-20}
    expect: { status: 200, bodyContains: title, maxDurationMs: 800 }
  - name: product detail
    path: /products/1
FieldMeaning
descriptionfree text
authenv (default) sends the environment's api.auth and api.headers; none sends neither
thinkTimeSecondspause after each iteration of one virtual user (default 1)
stagesk6 ramping stages: { duration, target }
vus + duration/iterationsa constant load instead of stages (use one or the other)
thresholdsk6 thresholds by metric; a failing threshold fails the run
requests[].namelabel in k6 output and the name tag (default METHOD path)
requests[].methodGET (default), HEAD, OPTIONS, POST, PUT, PATCH, DELETE
requests[].pathrelative to api.baseUrl; must start with / (absolute URLs are refused, so load stays on the opted-in env)
requests[].headersextra headers
requests[].bodya string is sent as-is; an object or array is sent as JSON with Content-Type: application/json
requests[].expectk6 checks: status (number or list, default 200), bodyContains, maxDurationMs

Each iteration of a virtual user sends the requests in order. ${VAR} works in paths, headers and bodies and is read from the environment when k6 runs; ${VAR:-default} falls back to the default. Secret-looking keys must use ${VAR}, as everywhere else in SDODS.

3. Check the script, then run it

sdods load -p demo-shop -e perf browse --dry-run

The dry run writes script.js without needing k6 and reports whether a real run would be refused. Read it: it is plain k6 JavaScript, and it contains __ENV.PERF_API_TOKEN, never the token.

sdods load -p demo-shop -e perf browse

The command prints the target URL and the peak virtual users, then runs k6. Install k6 from grafana.com/docs/k6/latest/set-up/install-k6, or use Docker:

sdods load -p demo-shop -e perf browse --runner docker

Inside the container localhost is the container itself; point a local environment at host.docker.internal instead.

k6 reads environment variables such as K6_VUS, K6_STAGES, K6_DURATION, K6_ITERATIONS and K6_SCENARIOS as options that override the script, which would bypass the profile and load.maxVus. sdods load passes no K6_* variable to k6 (outputs such as K6_OUT included) and prints a warning naming the ones it left out.

4. Read the result

Everything lands in .sdods/runs/<id>/load/<profile>/ (or --out):

FileContent
script.jsthe generated k6 script
summary.jsonk6's --summary-export output: every metric and threshold
load.jsonwhat SDODS ran: project, env, target, peak VUs, k6 exit code, headline numbers

The exit code is 0 when every threshold passed and 1 when k6 reports failed thresholds (its exit code 99) or fails otherwise, so a CI job can gate on it. Load runs are not recorded in the results database or shown in the web UI yet.

Using an AI coding agent? Install SDODS:

Install the SDODS plugin in Claude Code: skills, subagents, commands and the MCP server.

  1. /plugin marketplace add YarlisAISolutions/sdods-skills
  2. /plugin install sdods@sdods
Learn more about using SDODS with AI tools

On this page