Skip to main content

YAML Specification

Root sections

Every valid Relampo YAML script must include these sections at the same top level (root):

  • test (required): test metadata.
  • variables (optional): reusable global variables.
  • data_source (optional): external data input for the test.
  • http_defaults (optional): default HTTP configuration.
  • metrics (optional): custom metrics configuration.
  • error_policy (optional): error handling behavior rules.
  • scenarios (required): executable scenarios.

Main structure

test:
name: "My test"
description: "Optional"
version: "1.0"
variables:
base_url: "https://api.example.com"
username: "demo"
data_source:
type: csv
file: "users.csv"
mode: per_vu
http_defaults:
base_url: "{{base_url}}"
headers:
Accept: "application/json"
timeout: "30s"
follow_redirects: true
error_policy:
on_error: stop
on_4xx: continue
on_5xx: stop
scenarios:
- name: "Main"
load:
type: constant
users: 10
duration: 5m
ramp_up: 10s
iterations: 0
steps:
- get: /health

test

test defines script metadata.

Use it like this:

test:
name: "My test"
description: "Optional"
version: "1.0"

Common fields:

  • name (required): test name.
  • description (optional): test description.
  • version (optional): script version.

Notes:

  • name is required to identify the test.

Variables

Variables let you reuse values across the script.

Use this syntax:

{{var}}

Example:

variables:
base_url: "https://api.example.com"

http_defaults:
base_url: "{{base_url}}"

Supported usage:

  • URL
  • headers
  • query params
  • body string
  • body object (string fields)

Notes:

  • If a variable does not exist, it is replaced with an empty string.

Data source

Data sources let you load external data to use in scenarios.

Types:

  • csv
  • txt

Supported configuration:

data_source:
type: csv
file: "/path/users.csv"
mode: per_vu # per_vu | shared | per_worker
strategy: sequential # sequential | random | unique
variable_names: "email,password"
# or bind in header-based mode
bind:
email_col: user_email
pass_col: user_password
on_exhausted: recycle # recycle | stop

Notes:

  • variable_names treats all rows as data (no header).
  • bind uses first row as header.
  • on_exhausted: stop stops that VU when the data source is exhausted.

Fields:

  • mode
    • per_vu (default): each VU keeps its own cursor.
    • shared: all VUs share a global cursor for the same file within a worker.
    • per_worker: picks row by VU index (vu_idx), making row assignment stable per VU.
  • strategy
    • sequential (default): reads next row and advances cursor.
    • random: picks a random row every iteration (no sequential cursor advance).
    • unique: uses a shuffled order without repeating rows until dataset exhaustion.
  • variable_names
    • assigns columns by position (example: email,password).
    • when present, all rows are treated as data (no header).
    • missing columns are filled with empty string.

http_defaults

http_defaults is used to define global, reusable HTTP configuration for a scenario.

It is used to:

  • Avoid repeating base_url, headers, and timeouts on every request.
  • Set default redirect behavior.
  • Control whether embedded resources are fetched and which types are allowed.
  • Exclude embedded resources by pattern (for example analytics or hotjar).

Use it like this:

http_defaults:
base_url: "https://api.example.com"
headers:
User-Agent: "Relampo/1.0"
timeout: "30s"
follow_redirects: true
retrieve_embedded_resources: false
embedded_resource_types: ["js", "css", "img", "font"]
embedded_resource_blacklist: ["analytics", "hotjar"]

Field behavior:

  • timeout: "30s": maximum wait time per request. If exceeded, the request fails with timeout.
  • follow_redirects: true: when true, Relampo follows HTTP redirects (301/302/307/308). When false, it returns the redirect response without following it.
  • retrieve_embedded_resources: false: defines whether Relampo runs only the main request or also fetches embedded resources (js, css, images, fonts). false = main request only; true = main + embedded resources.
  • embedded_resource_types: ["js", "css", "img", "font"]: allowlist of embedded resource types fetched when retrieve_embedded_resources is enabled.
  • embedded_resource_blacklist: ["analytics", "hotjar"]: list of substrings used to skip embedded URLs (case-insensitive match).

error_policy

error_policy defines behavior when request execution hits errors.

It can be defined at root, scenario, or request level.

Use it like this:

scenarios:
- name: "Main"
error_policy:
on_error: stop
on_4xx: continue
on_5xx: stop
on_timeout: stop
steps:
- get: /health
- request:
method: GET
url: /profile
error_policy:
on_4xx: stop

Supported fields:

  • on_error
  • on_4xx
  • on_5xx
  • on_timeout

Supported values:

  • stop
  • continue

Override order:

  • request-level error_policy overrides scenario-level.
  • scenario-level error_policy overrides root-level.

scenarios

scenarios defines executable flows in the test.

Use it like this:

scenarios:
- name: "Main"
load:
type: constant
users: 10
duration: 5m
cookies:
clear_each_iteration: true
policy: standard
cache_manager:
clear_each_iteration: true
max_elements: 1000
steps:
- get: /health

Inside each scenario you can define:

  • load: scenario load pattern.
  • cookies: VU cookie behavior.
  • cache_manager: HTTP cache behavior for requests.
  • error_policy: scenario-level error behavior.
  • steps: action flow executed by the scenario.

scenario.load

scenario.load defines how load is executed for that scenario.

Supported load types:

  • constant
  • ramp
  • ramp_up_down
  • throughput

Common behavior (applies to all 4 types):

  • All types accept duration and iterations.
  • If iterations > 0, each VU runs exactly that number of iterations.
  • If iterations = 0 and duration is set, it loops until duration ends.
  • If neither duration nor iterations is set, it runs once per VU.

constant

Use it like this:

load:
type: constant
users: 10
duration: 5m
ramp_up: 10s
ramp_down: 10s
iterations: 0

Behavior:

  • Empty type is treated as constant.
  • users defines concurrent VUs.

ramp

Use it like this:

load:
type: ramp
start_users: 1
end_users: 50
duration: 10m
iterations: 0

Behavior:

  • If users is missing or <= 1, it uses end_users as total VUs.
  • start_users and end_users are used to describe the linear load profile.

ramp_up_down

Use it like this:

load:
type: ramp_up_down
users: 50
duration: 10m
ramp_up: 2m
ramp_down: 1m
iterations: 0

Behavior:

  • This pattern uses users, duration, ramp_up, and ramp_down.
  • It represents ramp up, steady phase, and ramp down.

throughput

Use it like this:

load:
type: throughput
target_rps: 20
duration: 10m
ramp_up: 1m
ramp_down: 1m
iterations: 0

Behavior:

  • target_rps defines the target requests per second for the whole scenario.
  • Relampo spaces executions to stay close to that RPS target.
  • duration keeps that target active for the configured time window.
  • If iterations > 0, the scenario ends when iterations complete, even if duration remains.
  • ramp_up and ramp_down control ramp-in and ramp-out toward/from the target.

scenario.cookies

scenario.cookies controls how the scenario handles cookies.

Use it like this:

scenarios:
- name: "Main"
cookies:
policy: standard
clear_each_iteration: false
cookies:
- name: session_id
value: abc123
domain: api.example.com
path: /

Behavior:

  • policy supports standard and ignore_cookies.
  • clear_each_iteration: true resets cookie jar on each iteration; false keeps it.
  • cookies[] lets you preload initial cookies (name, value, domain, path).

scenario.cache_manager

scenario.cache_manager enables HTTP response caching in the scenario.

Use it like this:

scenarios:
- name: "Main"
cache_manager: {}
  • With cache_manager active, successful GET requests (status < 400) are cached by full URL.
  • At request level, cache_override can force enabled or disabled, and it overrides scenario-level behavior.

scenario.steps

scenario.steps defines the actions each virtual user executes inside the scenario.

Currently supported step types:

  • HTTP requests: request, get, post, put, delete, patch, head, options
  • Data access: sql
  • Flow control: if, loop, retry, one_time, parallel, balanced
  • Organization and measurement: group, transaction
  • Validation: assertions
  • Pauses: think_time

Execution behavior:

  • steps run in order, top to bottom.
  • If a step contains nested steps, that block runs first, then flow continues.

step.request

step.request defines an HTTP call inside the flow.

1. When to use short vs long form

  • Use short form (get, post, put, delete, patch, head, options) when you only need a simple call.
  • Use long form (request) when you need details like headers, body, assertions, extractors, timeout, files, or error_policy.

2. Minimal request

steps:
- request:
method: GET
url: /health

3. What it inherits and overrides

  • It inherits from http_defaults (for example base_url, headers, and timeout).
  • Values declared inside the request override global defaults.

4. step.request subsections

The following blocks are part of step.request and are documented as separate quick-reference sections.

step.request.headers

headers defines request-specific HTTP headers.

Use it like this:

- request:
method: GET
url: /me
headers:
Authorization: "Bearer {{token}}"
Accept: "application/json"

Behavior:

  • It is a key: value object.
  • Values are interpolated with variables.
  • If a header also exists in http_defaults.headers, request-level value overrides it.

step.request.query_params

query_params adds parameters to the final request query string.

Use it like this:

- request:
method: GET
url: /search
query_params:
q: "relampo"
page: "1"

Behavior:

  • It is a key: value object.
  • Values are interpolated with variables.
  • They are applied on top of the resolved request URL.

step.request.body

body defines the request HTTP payload.

Use it like this:

- request:
method: POST
url: /login
body:
user: "{{username}}"
pass: "{{password}}"

Supported types and behavior:

  • string: sent as literal string (interpolated).
  • object: serialized as JSON; string fields are interpolated.
  • Other types: JSON marshaling is attempted.
  • If Content-Type is not set and body is serialized as JSON, Relampo sets application/json.

step.request.timeout

timeout sets a request-specific timeout.

Use it like this:

- request:
method: GET
url: /reports
timeout: "45s"

Behavior:

  • Uses duration format (500ms, 5s, 1m).
  • If invalid, it falls back to client/global timeout.

step.request.enabled

enabled lets you turn a request on/off.

Use it like this:

- request:
method: GET
url: /experimental
enabled: false

Behavior:

  • false: request is skipped.
  • true or missing: request executes normally.

step.request.assertions

Inside step.request, validations can be defined in two formats: assert and assertions.

Use them like this:

- request:
method: GET
url: /health
assert:
status: 200
body_contains: "ok"
- request:
method: GET
url: /health
assertions:
- type: status
value: 200
- type: contains
value: "ok"

Supported types and behavior:

  • assert (object format): direct keys such as status, status_in, body_contains, body_not_contains, body_matches, response_time_ms, jsonpath, json, header, response_size.
  • assertions (list): supports status, contains, not_contains, regex, jsonpath, json, header, response_time, response_time_max, response_size.

Assertion types (in assertions):

  • status: validates expected HTTP status code.
  • contains / not_contains: validates presence or absence of text in response body.
  • regex: validates response body with a regular expression.
  • jsonpath / json: validates a JSON value by path.
  • header: validates a response header value.
  • response_time / response_time_max: validates response time.
  • response_size: validates response size.

step.request.extractors

Inside step.request, extractors can be defined in two formats: extract and extractors.

Use them like this:

- request:
method: POST
url: /login
extract:
token: "jsonpath($.token)"
req_id: "header.X-Request-Id"
- request:
method: POST
url: /login
extractors:
- type: jsonpath
var: token
expression: $.token
default: ""
- type: regex
var: order_id
pattern: "orderId=(\\w+)"
capture_mode: first

Supported types and behavior:

  • extract (simple object format): supports body, header.<Name>, regex("..."), jsonpath(...).
  • extractors (list): supports header, body, jsonpath (alias json), xpath, boundary, regex.

Extractor types (in extractors):

  • regex: extracts values with a regular expression from body, headers, or status_line depending on from.
  • jsonpath / json: extracts values from JSON responses using expression or path.
  • header: extracts the value of a specific response header.
  • xpath: extracts values from XML/HTML responses using XPath.
  • boundary: extracts text between left_boundary and right_boundary.

Behavior options:

  • from supports body, headers, status_line.
  • capture_mode supports first, all, index, random.
  • default lets you assign a fallback value when no match is found.

step.request.files

files enables multipart uploads.

Use it like this:

- request:
method: POST
url: /upload
body:
category: invoices
files:
- field: file
path: /tmp/invoice.pdf
mime: application/pdf

Supported types and behavior:

  • Each file entry uses field, path, and optional mime.
  • If files is present, request is sent as multipart/form-data.

step.request.error_policy

error_policy sets error behavior only for that request.

Use it like this:

- request:
method: GET
url: /profile
error_policy:
on_error: stop
on_4xx: continue
on_5xx: stop
on_timeout: stop

Supported values and behavior:

  • Supported values: stop and continue.
  • Can define on_error, on_4xx, on_5xx, on_timeout.
  • At request level, it overrides scenario.error_policy.

step.request.cache_override

cache_override lets a request override scenario-level cache behavior.

Use it like this:

- request:
method: GET
url: /catalog
cache_override: enabled

Supported values:

  • Supported modes: inherit, enabled, disabled.
  • cache_override controls request-level HTTP cache behavior.

step.request.cookie_override

cookie_override lets a request override scenario-level cookie behavior.

Use it like this:

- request:
method: GET
url: /catalog
cookie_override: disabled

Supported values:

  • Supported modes: inherit, enabled, disabled.
  • cookie_override controls whether the request uses cookie jar.

step.request.redirects

follow_redirects and redirect_automatically control redirects at request level.

Use it like this:

- request:
method: GET
url: /old-path
follow_redirects: false

Behavior:

  • Both are booleans.
  • Precedence: redirect_automatically > follow_redirects > http_defaults.follow_redirects.
  • If effective value is false, Relampo does not follow redirects and returns the 3xx response.

step.request.retrieve_embedded_resources

retrieve_embedded_resources enables/disables embedded resource fetching for that request.

Use it like this:

- request:
method: GET
url: /home
retrieve_embedded_resources: true

Behavior:

  • Boolean at request level.
  • Evaluated only for successful GET responses (status < 400) with HTML/CSS content.
  • Uses resource types and blacklist from http_defaults.

step.request.spark

spark runs JavaScript scripts before or after request execution.

Use it like this:

- request:
method: GET
url: /profile
spark:
- when: before
script: |
vars.set("trace_id", "abc-123")
- when: after
script: |
if (response.status === 200) {
vars.set("ok", true)
}

Supported values and behavior:

  • when supports before and after (defaults to after when omitted).
  • script accepts JavaScript.
  • Primary goal: transform values and generate dynamic variables for subsequent steps.
  • Available context: vars.get, vars.set, log.
  • response exists only in after (status, body, latency_ms).

Example (split a value and store a variable):

- request:
method: POST
url: /login
spark:
- when: after
script: |
var payload = JSON.parse(response.body || "{}");
var session = String(payload.session_id || "");
var parts = session.split("-");
vars.set("session_prefix", parts[0] || "");

Example (normalize response data into a reusable variable):

- request:
method: GET
url: /profile
spark:
- when: after
script: |
var payload = JSON.parse(response.body || "{}");
var user = payload.user || {};
var email = String(user.email || "");
if (email) {
vars.set("email_lower", email.toLowerCase());
}

step.control_flow

step.control_flow controls branching, repetition, and retries inside steps.

Currently supported control flow steps:

  • if
  • loop
  • retry

Example:

steps:
- if: "{{token}} != ''"
steps:
- get: /profile
- loop:
count: 3
break_on: "{{done}} == true"
steps:
- get: /poll
- retry:
attempts: 3
backoff: exponential
initial_delay: 1s
max_delay: 10s
multiplier: 2
steps:
- get: /health

Behavior:

  • if runs nested steps only when the condition evaluates to true.
  • loop repeats nested steps based on count (or until break_on is true).
  • retry reruns nested steps until success or attempts are exhausted.

Common fields:

  • if: condition + steps.
  • loop: count, break_on, steps.
  • retry: attempts, backoff, initial_delay, max_delay, multiplier, steps.

step.group

step.group groups related steps under a named block.

Use it like this:

steps:
- group:
name: "Login Flow"
steps:
- post: /login
- get: /profile

step.assertions

step.assertions validates the most recent response inside the flow.

Example:

steps:
- get: /health
- assertions:
- type: status
value: 200
- type: contains
value: "ok"
- type: response_time
value: "<800"

Behavior:

  • It always evaluates against the immediately previous response.
  • If there is no previous response, the step fails.

Supported assertion types:

  • status: validates that the HTTP status code exactly matches the expected value (for example 200).
  • contains / not_contains: validates whether response body includes or excludes text.
  • regex: validates response body with a regular expression.
  • jsonpath / json: validates existence or value at a JSON path (for example $.data.id).
  • header: validates that a header exists or has a specific value.
  • response_time / response_time_max: validates response time with comparisons like <800 or >200.
  • response_size: validates response body size.

step.think_time

step.think_time adds a pause between actions to simulate real user timing.

Supported types:

  • fixed: fixed pause with duration (or short form think_time: 2s).
  • uniform: uniform random pause between min and max.
  • normal: normal random pause using mean and std_dev (clamped by min/max when provided).
steps:
- get: /health
- think_time: 2s
steps:
- think_time:
min: 1s
max: 3s
distribution: uniform
steps:
- think_time:
mean: 2s
std_dev: 500ms
min: 1s
max: 4s
distribution: normal

Behavior:

  • If you use duration (or short form think_time: 2s), that value takes precedence.
  • If duration is missing, it uses min/max range (uniform distribution by default).
  • If distribution: normal, it uses mean and std_dev, then clamps by min/max when provided.
  • If no valid time values are provided, no pause is applied.

Common fields:

  • duration: fixed pause.
  • min / max: pause range.
  • distribution: uniform or normal.
  • mean / std_dev: used for normal distribution.

scenarios is required and must include at least one scenario.

step.sql

sql runs a database query or explicitly authorized write.

steps:
- sql:
name: Lookup user
dialect: postgres
kind: query
connection:
host: db.internal
port: 5432
database: relampo
user: "{{db_user}}"
password: "{{db_password}}"
ssl_mode: disable
query: "SELECT id, email FROM users WHERE email = $1"
params:
- "{{email}}"

Supported dialects are PostgreSQL and MySQL. Use kind: query for reads; it reports rows_returned. kind: exec performs a non-read operation and reports rows_affected, but requires allow_writes: true. Parameters are interpolated before execution, and credentials are redacted from events and verbose output.

step.transaction

transaction groups a complete business flow and reports it as one transactional unit in addition to its child operations.

steps:
- transaction:
name: Checkout
steps:
- post: /cart
- post: /address
- post: /payment
- post: /confirm

It must contain at least one child step. The supported key is only transaction; group and controller remain generic grouping blocks and do not count as TPS transactions.

step.one_time

one_time runs initialization steps once for each VU, before that VU continues with dependent steps.

steps:
- one_time:
name: Shared bootstrap
steps:
- request:
method: POST
url: /auth/login
extractors:
- type: jsonpath
var: session_token
expression: $.token
- get: "/profile?token={{session_token}}"

It requires at least one child step. Variables extracted or created inside the controller remain available to later steps for the same VU. If initialization fails, the scenario stops before dependent steps run.

step.parallel

parallel starts each direct child step at the same time and waits for every child to finish.

steps:
- parallel:
name: Parallel reads
steps:
- get: /dashboard/summary
- get: /dashboard/activity

The controller requires at least one child. By default, one child failing does not stop the others. With error_policy.on_error: stop, the first failure cancels pending children. Variables written by children are merged in YAML order, so the later child wins when both write the same variable.

step.balanced

balanced distributes the selected load among weighted child steps. The supported type is total.

scenarios:
- name: Traffic mix
load:
users: 1
iterations: 10
steps:
- balanced:
name: Traffic mix
type: total
mode: iterations
steps:
- get: /landing
percentage: 60
- request:
name: Submit lead
method: POST
url: /leads
percentage: 40

name is required, mode is iterations or virtual_users, and every child must declare a percentage greater than zero. For type: total, all percentages must add up to exactly 100. mode: iterations requires load.iterations > 0; virtual_users is unavailable for dynamic loads such as stages or intent. Assignment is deterministic, with ties resolved in YAML order.