Skip to content

CI-gated performance test

Problem: A deployment can bring in a performance regression. You want your CI pipeline to run a performance test on every release candidate, report the key metrics, and fail the build when the result is worse than your baseline. All of that has to happen before the code ships to production.

Test type: Baseline regression test triggered from CI.

  • A MaxoPerf account with a test that has a clean baseline run.
  • A MaxoPerf API token stored as a secret in your CI provider (GitHub Actions, GitLab CI, CircleCI, etc.).
  • Failure criteria configured on the test. The criteria decide whether the run ends with status finished (pass) or failed (fail).
  • The test ID from the MaxoPerf console (visible in the URL when the test is open: /tests/tst-xxxxxxxx).

See Run tests from CI for the how-to reference.

Before you wire up CI, tell the test when to fail. In the MaxoPerf console:

  1. Open the test, switch to Configuration, and scroll to Failure criteria.
  2. Add your thresholds, for example p95 latency > 300ms and error rate > 0.5 %.
  3. Save.

The CI pipeline reads the run status field to decide pass or fail. CI needs no custom logic.

In your CI provider, add a secret named MAXOPERF_TOKEN with the value of your Maxoperf API token.

GitHub Actions example:

.github/workflows/perf-gate.yml
name: Performance gate
on:
push:
branches: [main, 'release/**']
jobs:
perf:
runs-on: ubuntu-latest
steps:
- name: Trigger MaxoPerf run
id: start
run: |
RUN=$(curl -sf -X POST \
-H "Authorization: Bearer ${{ secrets.MAXOPERF_TOKEN }}" \
-H "Content-Type: application/json" \
https://api.maxoperf.com/v1/runs \
-d '{"test_id": "tst-1234abcd56"}')
echo "run_id=$(echo $RUN | jq -r .id)" >> $GITHUB_OUTPUT
- name: Wait for run to complete
run: |
for i in $(seq 1 60); do
STATUS=$(curl -sf \
-H "Authorization: Bearer ${{ secrets.MAXOPERF_TOKEN }}" \
https://api.maxoperf.com/v1/runs/${{ steps.start.outputs.run_id }} \
| jq -r .status)
echo "Run status: $STATUS"
if [ "$STATUS" = "finished" ]; then
echo "Run passed failure criteria."
exit 0
elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "cancelled" ]; then
echo "Run did not pass failure criteria — status: $STATUS"
exit 1
fi
sleep 10
done
echo "Timed out waiting for run."
exit 1

Replace tst-1234abcd56 with your real test ID.

GitLab CI example:

perf-gate:
stage: test
script:
- |
RUN_ID=$(curl -sf -X POST \
-H "Authorization: Bearer $MAXOPERF_TOKEN" \
-H "Content-Type: application/json" \
https://api.maxoperf.com/v1/runs \
-d "{\"test_id\": \"tst-1234abcd56\"}" | jq -r .id)
echo "Started run: $RUN_ID"
for i in $(seq 1 60); do
STATUS=$(curl -sf \
-H "Authorization: Bearer $MAXOPERF_TOKEN" \
https://api.maxoperf.com/v1/runs/$RUN_ID | jq -r .status)
[ "$STATUS" = "finished" ] && exit 0
[ "$STATUS" = "failed" ] && exit 1
sleep 10
done
exit 1
  1. Open the Runs list in the MaxoPerf console after the CI step fires.
  2. The run started by CI appears with the test name and a timestamp matching the pipeline execution.
  3. Click the run to see the full result: per-label latency, error rate, and the failure-criteria verdict.
  • The CI step exits 0 when the run status is finished and 1 when it is failed.
  • The run appears in the MaxoPerf console with a source tag identifying the CI trigger.
  • Failure criteria violations show on the run overview with the exact threshold that was breached.
  • Override load profile per branch: pass "overrides": {"concurrency": 5} in the trigger payload for feature-branch runs to use a lighter load than the main-branch gate.
  • Tag the run with commit SHA: add "tags": {"commit": "$GITHUB_SHA"} to the trigger body for searchable run history.
  • Webhook instead of polling: configure a MaxoPerf webhook that POSTs to your CI pipeline’s callback URL when the run completes. You no longer need the polling loop.
  • Parallel CI jobs: trigger a matrix of tests (smoke, load, API) in parallel CI jobs. Each job polls its own run_id.