GitHub Action reference

August 11, 2026 · View on GitHub

Run cryload as a GitHub Action with pass/fail thresholds and JSON results as step outputs:

- name: Latency SLA
  uses: sdogruyol/cryload@v5
  with:
    url: http://127.0.0.1:3000/api
    requests: 500
    connections: 50
    max_p99: "250"

The action installs the prebuilt release binary and runs it directly on the runner — no container. It works on Linux, macOS, and Windows runners and can reach services listening on the runner's localhost.

macOS runners: Local Network Privacy on macOS 15+ blocks unsigned binaries from reaching loopback addresses, so localhost targets may time out there. Prefer Linux runners for localhost benchmarks.

Windows runners: requires cryload v5.2.0 or newer — earlier Windows binaries were dynamically linked against the Crystal toolchain's DLLs and fail to start on clean runners.

Versioning

The installed binary version follows the ref the action is pinned to:

uses: refInstalled binary
sdogruyol/cryload@v5.2.0That exact release (reproducible)
sdogruyol/cryload@v5Latest release
branch / commit SHALatest release

Set the version input to override, e.g. version: v5.2.0.

Inputs

All inputs map 1:1 to CLI flags — see cryload --help for full semantics.

InputCLI flagDefaultDescription
urlpositional(required)Target URL to load test
requests-nNumber of requests
duration-dTest duration in seconds (instead of requests)
connections-cConcurrent connections (CLI default: 10)
method-mHTTP method
body-bRequest body
headers-HOne Key: Value per line
cookies--cookieOne name=value per line
basic_auth-auser:password
timeout--timeoutConnect/read timeout in seconds
rate-qRate limit in requests/sec
warmup--warmupWarmup seconds before the benchmark
follow_redirects-LfalseFollow redirects
disable_keepalive--disable-keepalivefalseNew connection per request
insecure--insecurefalseAccept invalid TLS certificates
success_status--success-statusSuccess codes/ranges, e.g. 200-299,301
user_agent--user-agentUser-Agent header
host_header--host-headerHost header override
proxy--proxyHTTP(S) proxy URL
fail_on_error--fail-on-errorfalseFail on any HTTP/transport error
fail_on_transport_error--fail-on-transport-errorfalseFail on any transport error
max_fail_rate--max-fail-rateFail when failure rate exceeds this %
max_p99--max-p99Fail when p99 exceeds this many ms
output_format--output-formatjsontext, json, csv, quiet
versionRelease tag to install (overrides the action ref)

File-based flags (--body-file, --body-stdin, --urls-file, --random-path) are not exposed as inputs. The action puts cryload on PATH, so follow-up run: steps can use the full CLI directly.

Outputs

Structured outputs are populated when output_format is json (the default) — field names match the JSON output reference.

OutputSourceDescription
jsonfull reportCompact single-line JSON document
requests.summary.requestsTotal attempts
requests_per_second.summary.requests_per_secondThroughput
failure_rate_percent.summary.failure_rate_percentFailure rate
p50.latency_ms.p50Median latency (ms)
p99.latency_ms.p99p99 latency (ms)
exit_codecryload's exit code (1 on threshold breach)

When a threshold breaks, the step fails with cryload's exit code — but outputs are written first, so if: always() steps can still consume them.

Full example

jobs:
  load-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Start app
        run: |
          docker compose up -d
          curl --retry 10 --retry-connrefused -sf http://127.0.0.1:3000/health

      - name: Benchmark
        id: bench
        uses: sdogruyol/cryload@v5
        with:
          url: http://127.0.0.1:3000/api
          duration: 30
          connections: 50
          warmup: 5
          headers: |
            Authorization: Bearer ${{ secrets.API_TOKEN }}
            Accept: application/json
          max_p99: "250"
          max_fail_rate: "1"

      # Outputs survive a threshold failure; pass them via env, never
      # inline them into the script.
      - name: Report results
        if: always()
        env:
          REPORT: ${{ steps.bench.outputs.json }}
          P99: ${{ steps.bench.outputs.p99 }}
          RPS: ${{ steps.bench.outputs.requests_per_second }}
        run: |
          echo "p99: ${P99} ms, throughput: ${RPS} req/s"
          printf '%s' "$REPORT" | jq .