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.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:
nameis 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_namestreats all rows as data (no header).binduses first row as header.on_exhausted: stopstops that VU when the data source is exhausted.
Fields:
modeper_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.
strategysequential(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.
- assigns columns by position (example:
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: whentrue, Relampo follows HTTP redirects (301/302/307/308). Whenfalse, 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 whenretrieve_embedded_resourcesis 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_erroron_4xxon_5xxon_timeout
Supported values:
stopcontinue
Override order:
- request-level
error_policyoverrides scenario-level. - scenario-level
error_policyoverrides 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:
constantrampramp_up_downthroughputstages
Common behavior:
- All types accept
durationanditerations. - If
iterations > 0, each VU runs exactly that number of iterations. - If
iterations = 0anddurationis set, it loops until duration ends. - If neither
durationnoriterationsis 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
typeis treated asconstant. usersdefines concurrent VUs.
ramp
Use it like this:
load:
type: ramp
start_users: 1
end_users: 50
duration: 10m
iterations: 0
Behavior:
- If
usersis missing or<= 1, it usesend_usersas total VUs. start_usersandend_usersare 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, andramp_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_rpsdefines the target requests per second for the whole scenario.- Relampo spaces executions to stay close to that RPS target.
durationkeeps that target active for the configured time window.- If
iterations > 0, the scenario ends when iterations complete, even if duration remains. ramp_upandramp_downcontrol ramp-in and ramp-out toward/from the target.
stages
Use it like this:
load:
type: stages
duration: 10m
stages:
- type: constant
users: 5
duration: 2m
- type: ramp_up
start_users: 5
end_users: 20
duration: 3m
- type: ramp_down
start_users: 20
end_users: 0
duration: 5m
Behavior:
durationis required atloadlevel.stagesmust be a non-empty list.- Stage
typesupportsconstant,ramp_up, andramp_down. - Stage durations must cover the full
load.durationwithout gaps or overlap. - Only the last stage can use
open_ended: true; an open-ended stage omits its owndurationand uses the remainingload.duration. load.type: stagesdoes not support mixingusers,start_users,end_users,ramp_up,ramp_down,iterations,target_rps,target_unit,target_value,min_vus,max_vus, oraggressivenessat the sameloadlevel.
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:
policysupportsstandardandignore_cookies.clear_each_iteration: trueresets cookie jar on each iteration;falsekeeps 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_manageractive, successfulGETrequests (status < 400) are cached by full URL. - At request level,
cache_overridecan forceenabledordisabled, 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 and database:
data_source,sql - Containers and controllers:
group,controller,transaction,parallel,one_time,balanced - Flow control:
if,loop,retry - Validation:
assertions - Pauses:
think_time
Execution behavior:
stepsrun 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 likeheaders,body,assertions,extractors,timeout,files, orerror_policy.
2. Minimal request
steps:
- request:
method: GET
url: /health
3. What it inherits and overrides
- It inherits from
http_defaults(for examplebase_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: valueobject. - 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: valueobject. - 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-Typeis not set and body is serialized as JSON, Relampo setsapplication/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.trueor 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 asstatus,status_in,body_contains,body_not_contains,body_matches,response_time_ms,jsonpath,json,header,response_size.assertions(list): supportsstatus,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): supportsbody,header.<Name>,regex("..."),jsonpath(...).extractors(list): supportsheader,body,jsonpath(aliasjson),xpath,boundary,regex.
Extractor types (in extractors):
regex: extracts values with a regular expression frombody,headers, orstatus_linedepending onfrom.jsonpath/json: extracts values from JSON responses usingexpressionorpath.header: extracts the value of a specific response header.xpath: extracts values from XML/HTML responses using XPath.boundary: extracts text betweenleft_boundaryandright_boundary.
Behavior options:
fromsupportsbody,headers,status_line.capture_modesupportsfirst,all,index,random.defaultlets 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 optionalmime. - If
filesis present, request is sent asmultipart/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:
stopandcontinue. - 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_overridecontrols 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_overridecontrols 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
GETresponses (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:
whensupportsbeforeandafter(defaults toafterwhen omitted).scriptaccepts JavaScript.- Primary goal: transform values and generate dynamic variables for subsequent steps.
- Available context:
vars.get,vars.set,log. responseexists only inafter(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:
ifloopretry
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:
ifruns nestedstepsonly when the condition evaluates to true.looprepeats nestedstepsbased oncount(or untilbreak_onis true).retryreruns nestedstepsuntil 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
Common fields:
name: optional label.enabled: optional boolean. Whenfalse, the group is skipped.auth: optional auth override inherited by child steps.steps: child steps executed in order.
step.controller
step.controller is a generic container for nested steps. It behaves like a named block that can hold requests, assertions, think time, data sources, or other controllers.
Use it like this:
steps:
- controller:
name: "Checkout Flow"
steps:
- get: /cart
- post: /checkout
Common fields:
name: optional label.enabled: optional boolean. Whenfalse, the controller is skipped.auth: optional auth override inherited by child steps.steps: child steps executed in order.
step.transaction
step.transaction groups nested steps and records transaction-level metrics. Use it when a business action should be measured as one unit, such as login, checkout, or search.
Use it like this:
steps:
- transaction:
name: "Login"
steps:
- get: /login
- post:
url: /session
body:
user: "{{username}}"
password: "{{password}}"
Common fields:
name: transaction name shown in reports.enabled: optional boolean. Whenfalse, the transaction is skipped.auth: optional auth override inherited by child steps.steps: child steps executed in order.
step.parallel
step.parallel executes child steps concurrently inside the same VU flow. Each branch receives an isolated copy of the current variables, and changed variables are merged back after branches finish.
Use it like this:
steps:
- parallel:
name: "Load dashboard widgets"
steps:
- get: /api/widget-a
- get: /api/widget-b
- get: /api/widget-c
Use it for independent requests that should happen at the same logical time.
Common fields:
name: optional label.enabled: optional boolean. Whenfalse, the parallel block is skipped.auth: optional auth override inherited by child steps.steps: child steps executed concurrently.
step.one_time
step.one_time runs initialization steps once for the scenario execution and reuses generated variables for later VUs or iterations.
Use it like this:
steps:
- one_time:
name: "Authenticate once"
steps:
- post:
url: /login
body:
user: "{{admin_user}}"
password: "{{admin_password}}"
extractors:
- type: jsonpath
var: admin_token
path: $.token
- get:
url: /admin/me
headers:
Authorization: "Bearer {{admin_token}}"
Common fields:
name: optional label.description: optional description.enabled: optional boolean. Whenfalse, the one-time block is skipped.auth: optional auth override inherited by child steps.steps: initialization steps.
step.balanced
step.balanced routes each VU or iteration into one child branch based on percentage distribution.
Use it like this:
scenarios:
- name: "Traffic mix"
load:
type: constant
users: 10
iterations: 20
steps:
- balanced:
name: "Traffic Mix"
type: total
mode: iterations
steps:
- percentage: 70
transaction:
name: "Browse"
steps:
- get: /products
- percentage: 30
transaction:
name: "Checkout"
steps:
- get: /cart
- post: /checkout
Rules:
nameis required.typemust betotal.modemust beiterationsorvirtual_users.- Participating child steps must include
percentage. - Percentages must sum exactly to
100. - Child branches must contain at least one enabled HTTP request or SQL step.
mode: iterationsrequiresload.iterationsgreater than0, unless the scenario is a simple one-shot functional run.mode: virtual_usersis not supported with dynamic user loads such asstagesorintent.
Supported child step types inside balanced:
requestand HTTP short formssqlgroupcontrollertransactionifloopretry
step.data_source
step.data_source can load data at a specific point in the flow. It uses the same data source behavior as the root-level data_source block, but the binding happens when the step executes.
Use it like this:
steps:
- data_source:
type: csv
file: users.csv
mode: shared
variable_names: email,password
on_exhausted: recycle
- post:
url: /login
body:
email: "{{email}}"
password: "{{password}}"
Supported source types:
csvtxt
Supported modes:
per_vusharedper_worker
Supported strategies:
sequentialrandomunique
Notes:
variable_namescan be a comma-separated string or a YAML list.binduses a header row and maps source columns to variable names.- A step-level
data_sourcekeeps an independent cursor from other data source nodes. on_exhausted: stopends the current VU iteration gracefully when data is exhausted.
step.sql
step.sql executes a database query or command as part of a scenario.
Use it like this:
steps:
- sql:
name: "Check user count"
dialect: postgres
kind: query
connection:
host: localhost
port: 5432
database: app
user: "{{db_user}}"
password: "{{db_password}}"
ssl_mode: disable
query: "select count(*) from users"
Supported dialects:
postgresmysql
Supported kinds:
query: read operation.exec: write operation. Requiresallow_writes: true.
Required connection fields:
hostdatabaseuserpassword
Common optional fields:
nameparamstimeoutvalidate_connectivityon_errorerror_policyconnection.portconnection.ssl_modeconnection.max_open_connsconnection.max_idle_connsconnection.conn_max_lifetime
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 example200).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<800or>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 withduration(or short formthink_time: 2s).uniform: uniform random pause betweenminandmax.normal: normal random pause usingmeanandstd_dev(clamped bymin/maxwhen 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 formthink_time: 2s), that value takes precedence. - If
durationis missing, it usesmin/maxrange (uniformdistribution by default). - If
distribution: normal, it usesmeanandstd_dev, then clamps bymin/maxwhen provided. - If no valid time values are provided, no pause is applied.
Common fields:
duration: fixed pause.min/max: pause range.distribution:uniformornormal.mean/std_dev: used for normal distribution.
scenarios is required and must include at least one scenario.