Key takeaways
- Install
@loadfocus/loadfocus-api-clientin a Bitbucket Pipelines step, configure it with--apikeyand--teamid, and runloadfocus-api jmeter run-testwith--thresholds. The client exits 1 when a threshold is missed or the run errors, so the step fails and later steps do not run. - Keep the API key and team ID in secured repository variables, never in
bitbucket-pipelines.yml. - Test the gate once with an impossible threshold. A pipeline that has never been seen to fail is not a gate.
How do I run a LoadFocus load test from Bitbucket Pipelines?
Add a step to bitbucket-pipelines.yml that installs the LoadFocus API Client with npm, saves your credentials with loadfocus-api config set --apikey ... --teamid ..., and runs loadfocus-api jmeter run-test --name <saved test> --thresholds "p95<=250,errors==0". The command starts a JMeter test you already saved in LoadFocus, waits for it, grades the result and returns a non-zero exit code on failure, which Bitbucket treats as a failed step.
There is no LoadFocus page for Bitbucket in the docs: the CI/CD guides cover Azure DevOps, GitHub Actions, CircleCI, GitLab CI/CD and Jenkins (the Bitbucket guide URL returns 404, checked 2026-10-23), although the load testing page lists Bitbucket Pipelines among the CI systems LoadFocus works with. So this setup is the same client as in the other guides, applied to Bitbucket’s syntax. I found no LoadFocus pipe; I could not browse Bitbucket’s full pipe directory, so check it yourself before assuming either way. Nothing below needs a pipe.
What you need first
- A JMeter test saved in your LoadFocus account.
--namemust match that test’s name; the client starts it, it does not create it (the LoadFocus CI guide’s troubleshooting says to verify the test name exists in your account). - Your LoadFocus API key and team ID.
- Two repository variables,
LOADFOCUS_API_KEYandLOADFOCUS_TEAM_ID, added under Repository settings, Pipelines, Repository variables, with “Secured” ticked. Bitbucket documents that secured variables are hidden in logs and masked, and that they can only be replaced or deleted, not read back.
A Bitbucket Pipelines config that fails on a missed threshold
image: node:24
pipelines:
default:
- step:
name: Build and test
script:
- npm ci
- npm test
- step:
name: Performance test
max-time: 45
script:
- npm install -g @loadfocus/loadfocus-api-client
- loadfocus-api config set --apikey "$LOADFOCUS_API_KEY" --teamid "$LOADFOCUS_TEAM_ID"
- |
loadfocus-api jmeter run-test \
--name "My_Saved_JMeter_Test" \
--thresholds "avgresponse<=200,errors==0,p95<=250" \
--wait-timeout 2400 \
--format json | tee performance_results.txt
artifacts:
- performance_results.txt
- step:
name: Deploy
deployment: production
trigger: manual
script:
- echo "Deploying application..."
I could not run this pipeline: it needs a Bitbucket repository and a LoadFocus account with a saved test. I checked the keys against Bitbucket’s configuration reference (read 2026-10-23: image, pipelines, default, step, max-time of 1 to 720 minutes with a 120 minute default, artifacts, deployment) and ran the LoadFocus client itself in a scratch folder with fake credentials. node:24 was a current tag on Docker Hub on 2026-10-22; pin the version you test with.
What changed from the older version of this post, and why:
- Artifacts use the built-in
artifacts:key. The old config uploaded the file with the pipeatlassian/bitbucket-upload-file:0.3.2and a Bitbucket app password. That pipe is now at 0.7.5, and its README marks the app-password variables deprecated in favour of an Atlassian API token or an access token. A file kept for the rest of the pipeline needs no pipe at all. - Credentials use the flag form. The old config, like the LoadFocus guides and the client README, printed
loadfocus-api config set apiKey $KEY. In version 1.1.7 (published 2025-04-03, still the latest on npm on 2026-10-23)config setreads only--apikey,--teamidand--url. I ran the positional form: it reported success with both values empty, and a followingrun-testprinted “Missing required configuration” and exited 0. In a pipeline that is a green step with no test. --wait-timeout, not--waitTimeout. The camelCase flag is rejected (“unknown option … Did you mean —wait-timeout?”, exit 1). The value is in seconds, default 1800, so keep it belowmax-time.teeinstead of>, because the client prints progress lines before the JSON on standard output. The file is a log, not valid JSON.- The “skip deployment” script is gone. It wrote to
$BITBUCKET_ENV_VARS, a variable I found nowhere in Bitbucket’s documentation, then calledexit 0, so a failed performance test ended as a passing step. Letting the failing step fail is simpler and works: the deploy step is behind it.
Traps that make a gate pass by accident
- A malformed threshold is only a warning. Entries must look like
p95<=250(metric, one of< <= > >= = == !=, number). Anything else prints “Invalid threshold format” and is skipped. If all are skipped, the client falls back toavgresponse <= 100. errorsis a count, labelled “Error Count” by the client, soerrors==0allows no failed samples. Thresholds are also checked per label (per request name), not only overall.- Metrics the client knows:
avgresponse,p95,p99,min,max,errors,hitspersec. Times are milliseconds. - Exit codes: the source sets exit code 1 on a failed threshold and on any error (a 401 from a fake key gave exit 1). The one path that exits 0 is the missing-configuration case above. I read the threshold-failure exit in the source; I did not observe it against a live LoadFocus run.
- Steps do not share environment changes. If you want a later step to know the result, fail the step or save a file under
artifacts:; do not rely on exporting a variable.
Test the gate once, on purpose
- On a branch, set
--thresholds "avgresponse<=1". - Push and confirm the Performance test step goes red and Deploy never becomes available.
- Restore the threshold. Then remove the secured variable on a throwaway branch run and confirm the step still fails with an error rather than passing.
The LoadFocus docs do not describe this check. Bitbucket’s reference page I read does not spell out that a failed step stops the pipeline, so the first run is also how you confirm that behaviour on your plan.
Branch-specific thresholds
Give each branch its own step, with its own --thresholds, under pipelines: branches:. Steps in Bitbucket’s variables: form apply to custom pipelines, which this post does not use, so the simplest reliable way is a different --thresholds string in each branch’s script. Keep the strictest limits on the branch that deploys. Choose numbers from a baseline: run the test a few times against a healthy build and set limits a little above what you see; the 200 ms and 250 ms here are placeholders.
pipelines:
branches:
develop:
- step:
name: Performance test (develop)
script:
- npm install -g @loadfocus/loadfocus-api-client
- loadfocus-api config set --apikey "$LOADFOCUS_API_KEY" --teamid "$LOADFOCUS_TEAM_ID"
- loadfocus-api jmeter run-test --name "My_Saved_JMeter_Test" --thresholds "avgresponse<=300,p95<=500"
main:
- step:
name: Performance test (main)
script:
- npm install -g @loadfocus/loadfocus-api-client
- loadfocus-api config set --apikey "$LOADFOCUS_API_KEY" --teamid "$LOADFOCUS_TEAM_ID"
- loadfocus-api jmeter run-test --name "My_Saved_JMeter_Test" --thresholds "avgresponse<=200,errors==0,p95<=250"
Alternatives and related setups
LoadFocus can also store pass/fail thresholds on the test itself and expose a verdict endpoint for a finished run, so a pipeline can gate on it with a plain HTTP call (Pass/Fail Thresholds in the docs). For other CI systems see the same setup for CircleCI, the DevOps toolchain guide and the 2026 comparison of CI/CD load testing tools. Long soak tests belong on a schedule, not in the pipeline: see scheduled load tests.
Frequently Asked Questions
How do I store credentials securely in Bitbucket Pipelines?
Add LOADFOCUS_API_KEY and LOADFOCUS_TEAM_ID under Repository settings, Pipelines, Repository variables, and tick “Secured”. Reference them as $LOADFOCUS_API_KEY in the YAML. A key committed to the repository stays in its history, including in clones and forks.
Can I run the performance test only on some branches?
Yes. Put the step under pipelines: branches: for the branches that matter, as in the example above, or use a custom pipeline you trigger yourself.
How do I handle a long-running test?
Raise the step’s max-time (minutes, 1 to 720, default 120) and set the client’s --wait-timeout in seconds below it. If a test needs more than a few minutes, consider running it on a schedule instead of on every push.
Why did my step pass without running a test?
Most likely the client had no credentials: the positional config set apiKey form leaves them empty, and the client then exits 0. Use --apikey and --teamid, and run the deliberate-failure test.
Does LoadFocus have a Bitbucket pipe?
I found none on LoadFocus’s pages, and the docs have no Bitbucket guide. The setup above calls the npm client directly.
Sources
- LoadFocus CI/CD integration guides, loadfocus.com/docs/guides/jmeter-load-testing/jmeter-integration-ci-cd (read 2026-10-23; no Bitbucket page, URL returns 404)
- LoadFocus Pass/Fail Thresholds, loadfocus.com/docs/guides/load-testing/pass-fail-thresholds (2026-10-23)
- loadfocus.com/load-testing, CI/CD FAQ (2026-10-23)
@loadfocus/loadfocus-api-client1.1.7 on npm: README, docs and source, installed and run in a scratch folder (2026-10-23)- Bitbucket Pipelines configuration reference and “Variables and secrets”, support.atlassian.com/bitbucket-cloud (read 2026-10-23)
atlassian/bitbucket-upload-fileREADME, tags (latest 0.7.5, 2025-07-23), bitbucket.org (2026-10-23)