---
title: "StarSling tops the GitHub Actions runner benchmarks"
description: "StarSling Runners finished first on the Rust and TypeScript builds in RunsOn's public GitHub Actions runner benchmark, across 9 providers and 89 runners."
date: 2026-10-05
url: https://starsling.dev/blog/gha-benchmarks
canonicalUrl: https://starsling.dev/blog/gha-benchmarks
---

# StarSling tops the GitHub Actions runner benchmarks

<figure>
  [Image: RunsOn's GitHub Actions runner benchmark, build time per provider at 8 to 12 vCPU. StarSling ranks first on the Rust build at 1m 54s and first on the TypeScript build at 1m 07s, ahead of Blacksmith, RunsOn, Warpbuild, Ubicloud, GitHub, and AWS CodeBuild.]
  <figcaption>RunsOn's GitHub Actions runner benchmark: x64, 8 to 12 vCPU, wait + job, p90, Sep 30 to Oct 4.</figcaption>
</figure>

[StarSling Runners](/products/runners) came in first on both the Rust and TypeScript builds in RunsOn's open [GitHub Actions runner benchmark](https://runs-on.com/benchmarks/runners/starsling/), which runs the same cold builds on 89 runners from 9 providers. The harness and every raw result are [public on GitHub](https://github.com/runs-on-demo/benchmark-public/tree/33ed4431b26868d810939bea9315081f31bd0c74).

## The results

The builds benchmark real open source projects: [opencode](https://github.com/anomalyco/opencode), the TypeScript coding agent with over 200,000 GitHub stars, and [spotify-player](https://github.com/aome510/spotify-player), a Rust terminal client with over 7,000.

The view above compares each provider's fastest 8 to 12 vCPU runner on x64. Build time is the p90 of the job (the slowest build out of every ten) plus the typical wait for a runner to pick it up. That is the number you feel as a developer: how long until the check goes green, on a bad run as well as a good one. Each runner built each project 4 to 5 times, from September 30 to October 4.

**Rust** (spotify-player: checkout, toolchain, `cargo fmt`, `cargo test`, `cargo clippy`)

| Provider | Build time (p90) | Cost per build |
|---|---|---|
| **StarSling** | **1m 54s** | **$0.0258** |
| Blacksmith | 1m 55s | $0.0271 |
| RunsOn | 2m 02s | $0.0443 |
| Warpbuild | 2m 14s | $0.0311 |
| Ubicloud | 2m 30s | $0.0164 |
| GitHub | 2m 36s | $0.0562 |
| AWS CodeBuild | 3m 48s | $0.0671 |

**TypeScript** (opencode: clone, `bun install`, typecheck)

| Provider | Build time (p90) | Cost per build |
|---|---|---|
| **StarSling** | **1m 07s** | **$0.0135** |
| Blacksmith | 1m 11s | $0.0153 |
| RunsOn | 1m 14s | $0.0137 |
| Ubicloud | 1m 33s | $0.0087 |
| GitHub | 1m 34s | $0.0326 |
| Warpbuild | 1m 34s | $0.0201 |
| AWS CodeBuild | 2m 34s | $0.0415 |

Against GitHub's own 8 vCPU runner, StarSling builds the Rust project 1.38x faster for 54% less per build, and the TypeScript project 1.41x faster for 59% less per build. It also costs less per build than the runner-up, Blacksmith, on both.

Compile, test, lint and typecheck jobs are CPU-bound, and StarSling Runners run them on 5th Gen AMD EPYC cores. The per-phase bars in the benchmark show where the time goes: `cargo test` and `clippy` for Rust, `bun install` and typecheck for TypeScript.

If your team ships a Rust service or a TypeScript monorepo and waits on those checks every PR, StarSling is faster than the alternatives.

## Day one is the floor

Moving onto StarSling Runners is a one-line change to `runs-on`. The benchmark measures exactly that: the same unchanged build on different hardware. That is the speed you get on day one.

Then StarSling's agents get to work. They watch your CI's logs, timing and telemetry, form hypotheses about what is slow, test each fix against a control on the same runners, and open a PR with the before and after. We wrote about how that loop works in [The agent loop that shipped a 5.4x speedup](/blog/agent-loop).

Some recent PRs our agents shipped that customers merged:

- **Mastra, [clickhouse store tests 44% faster](https://github.com/mastra-ai/mastra/pull/25613)** (merged September 30). Same commit, five runs per arm: the job went from a median of 530s to 298s, running the same tests.
- **Better Auth, [up to 5.4x faster adapter tests](https://github.com/better-auth/better-auth/pull/10762).** 50 runs per arm: `mongo-adapter` went from 24.3s to 4.5s, and `kysely-prisma-adapter` from 145.6s to 45.7s.
- **Better Auth, [test job 1.21x faster](https://github.com/better-auth/better-auth/pull/10879).** About 62 seconds off every run of the `test` job on both Node versions.
- **Mastra, [88% less checkout data](https://github.com/mastra-ai/mastra/pull/21298).** Full-history checkouts dropped from 1,572 MB to 189 MB.

That work stacks on top of the hardware, and it compounds. Our [pricing page](/pricing#savings) shows both views: on day one, the runner swap cuts runtime by 41%. Once the agents have worked on the suite, runtime is cut by up to 91%.

## Test it yourself

Install the [StarSling GitHub App](https://github.com/apps/starslingdev), then give your agent this prompt to move your workflows over in one PR:

<details class="prompt-row">
<summary>Migration prompt</summary>

````text
# Migrate GitHub Actions to StarSling Runners

Migrate the user's workflows from GitHub-hosted runners to StarSling Runners.

**Prerequisites:** `gh` CLI authenticated, [StarSling GitHub App](https://github.com/apps/starslingdev) installed on the repo's org.

## Configuration

**Target:** `starsling-ubuntu-24.04` | **Branch:** `migrate-starsling-ubuntu-2404`

**Source runners to replace:** `ubuntu-latest`, `ubuntu-24.04`

Replace all UPPERCASE placeholders (`OWNER`, `REPO`, `BRANCH_NAME`, `HEAD_OID`, `BASE64_CONTENT`, `FILE_NAME`, `N`, `DEFAULT_BRANCH`) with actual values from previous steps.

## Procedure

### Step 1: Confirm GitHub App Installation

Ask the user: "Have you installed the [StarSling GitHub App](https://github.com/apps/starslingdev) on your org? It's required for runners to pick up jobs after merge. If not, please install it first and let me know when you're ready."

**Do not run any commands or proceed to Step 2 until the user explicitly confirms the app is installed.**

### Step 2: Verify CLI Auth

Verify `gh auth status` succeeds. If not, direct the user to install from https://cli.github.com/ and run `gh auth login`.

### Step 3: Get Repository

Ask the user for the repository (`owner/repo`). Then run `gh api repos/OWNER/REPO --jq '.owner.type'`. If the result is `User` (not `Organization`), stop and explain: "StarSling Runners only work with GitHub organization repositories. You can create a free organization at https://github.com/account/organizations/new."

### Step 4: Discover Workflows

Fetch all workflow files in one API call:

```bash
cat <<'QUERY' | gh api graphql --input -
{
  "query": "query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { id nameWithOwner defaultBranchRef { name target { oid } } object(expression: \"HEAD:.github/workflows\") { ... on Tree { entries { name object { ... on Blob { text } } } } } } }",
  "variables": { "owner": "OWNER", "repo": "REPO" }
}
QUERY
```

Note: `HEAD` in the GraphQL expression is a Git ref, not a placeholder - do not replace it.

If the response is truncated or errors due to size, fall back to the REST API: fetch the default branch and HEAD SHA via `gh api repos/OWNER/REPO --jq '.default_branch'` and `gh api repos/OWNER/REPO/git/ref/heads/DEFAULT_BRANCH --jq '.object.sha'`, then fetch workflow file names via `gh api repos/OWNER/REPO/contents/.github/workflows` and each file individually via `gh api repos/OWNER/REPO/contents/.github/workflows/FILE_NAME`. The response `content` field is base64-encoded - decode it before scanning for `runs-on:` values.

Save `defaultBranchRef.name` (or REST default branch), `.target.oid` / HEAD SHA, and workflow `entries[]`. Scan each `.yml`/`.yaml` file for:
- direct `runs-on:` string values matching supported runners, and
- matrix-driven patterns (e.g., `runs-on: ${{ matrix.os }}`) where the matrix values include supported runner labels.

Show the user a summary including:
- Workflows that will be migrated and which runners are being replaced
- Any Ubuntu runners NOT in the supported list (e.g., `ubuntu-22.04`, `ubuntu-20.04`, larger runners like `ubuntu-latest-16-cores`) listed as "Not migrated - unsupported runner label"

If no direct or matrix-backed supported runners match, stop.

### Step 5: Preview Changes

**Never create a PR without the user confirming changes first.**

Show what will change per workflow:
- Replace supported runners in `runs-on:` string values with `starsling-ubuntu-24.04` (preserve quotes/comments)
- Replace matching `runner:`/`os:` values in `matrix.include` sections
- Skip commented lines

**Complex `runs-on` patterns:**
- **Array syntax** (e.g., `runs-on: [self-hosted, linux, ubuntu-latest]`): Only replace the matching label within the array; do not collapse to a single string
- **Group/labels syntax** (e.g., `runs-on: { group: ..., labels: [...] }`): Skip and flag for manual review
- **Expressions** (`${{ matrix.os }}`, ternary/conditional): If `runs-on` uses `${{ matrix.os }}` and the matrix values are hardcoded runner labels, replace the labels in the matrix definition and flag the workflow for manual verification. If the matrix values come from other expressions, skip entirely and flag for manual review

**YAML fidelity:** Change ONLY the `runs-on` and matrix values. Preserve the original file exactly: same indentation, key ordering, comments, blank lines, and trailing newline. The PR diff should show only the runner label changes.

### Step 6: Create PR

**Branch:** Before creating, check for existing branches: `gh api repos/OWNER/REPO/git/matching-refs/heads/migrate-starsling-ubuntu-2404 --jq '.[].ref'`. If any exist, find the highest numeric suffix and increment by 1 (if none have a suffix, use `-2`). Then create: `gh api repos/OWNER/REPO/git/refs -f ref=refs/heads/BRANCH_NAME -f sha=HEAD_OID`.

**Atomic commit** - all files in one commit, contents base64-encoded:

```bash
cat <<'MUTATION' | gh api graphql --input -
{
  "query": "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid url } } }",
  "variables": {
    "input": {
      "branch": {
        "repositoryNameWithOwner": "OWNER/REPO",
        "branchName": "BRANCH_NAME"
      },
      "message": {
        "headline": "Migrate N CI workflows to StarSling Runners",
        "body": "Replaced runners per file:\n- file1.yml: ubuntu-latest → starsling-ubuntu-24.04\n- file2.yml: ubuntu-24.04 → starsling-ubuntu-24.04"
      },
      "expectedHeadOid": "HEAD_OID",
      "fileChanges": {
        "additions": [
          { "path": ".github/workflows/FILE_NAME", "contents": "BASE64_CONTENT" }
        ]
      }
    }
  }
}
MUTATION
```

Verify the response contains a valid `commit.oid`. If the mutation returned an `expectedHeadOid` mismatch, re-fetch the HEAD SHA (Step 4) and retry the commit once. For any other errors, report and stop - do not create a PR against a failed commit.

**Commit message:** The headline should read `Migrate N CI workflows to StarSling Runners` where N is the count of modified workflow files (`.yml` + `.yaml`). List each file and the runner label(s) replaced in the body, as shown in the example above.

**PR** via `gh pr create --repo OWNER/REPO --head BRANCH_NAME --base DEFAULT_BRANCH --title "Migrate N CI workflows to StarSling Runners" --body "..."` with this body structure:

```
## Summary
Migrates CI workflows from GitHub-hosted runners to [StarSling Runners](https://docs.starsling.dev) for faster builds and AI-powered optimizations and fixes.

## Changes
- \`file1.yml\`: \`ubuntu-latest\` → \`starsling-ubuntu-24.04\`
- \`file2.yml\`: \`ubuntu-24.04\` → \`starsling-ubuntu-24.04\`

## Not Migrated
- \`file3.yml\`: Uses \`${{ matrix.os }}\` - requires manual review
(or "All workflows migrated successfully.")

## After Merging
Workflows will automatically run on StarSling Runners. Ensure the [StarSling GitHub App](https://github.com/apps/starslingdev) is installed with access to this repo.
```

### Step 7: Monitor (Optional)

Ask the user if they'd like help monitoring after merge. If yes, explain they can return after merging and you'll check with:

```bash
gh run list --repo OWNER/REPO --branch DEFAULT_BRANCH --limit 5 --json status,conclusion,name,createdAt
```

Look for runs created after the merge. If any show `queued` for more than 2 minutes, suggest checking the GitHub App installation and repo access at https://github.com/apps/starslingdev.

## Error Handling

| Error | Solution |
|-------|----------|
| `gh` not found or not logged in | Install from https://cli.github.com/, run `gh auth login` |
| 403 / insufficient permissions | User needs write access to the repository - check collaborator status or org role |
| App not installed | Install from https://github.com/apps/starslingdev |
| Personal repo (owner type `User`) | StarSling requires an org repo - create one at https://github.com/account/organizations/new |
| Repository not found | Check repo name and permissions |
| No workflows found | Ensure `.github/workflows/` exists |
| Runner not available after merge | Verify app has repo access at https://github.com/apps/starslingdev |
| Rate limit exceeded | Wait a few minutes and retry |
| 422 on branch creation | Branch exists - append a number suffix |
| `expectedHeadOid` mismatch | Re-fetch HEAD SHA and retry the commit |
| `${{ matrix.os }}` / complex `runs-on` | Require manual review - determined at runtime |
| GraphQL response truncated | Fall back to REST API for individual file fetches |
````

<p class="prompt-source">Source: <a href="/products/runners">StarSling Runners</a></p>

</details>

Compare your own job times before and after the merge. For more on where runners fit among the other ways to speed up CI, see [Level 1: swap runners](/blog/levels-of-ci#level-1-swap-runners) and [Level 2: optimize your tests and workflows](/blog/levels-of-ci#level-2-optimize-your-tests-and-workflows) in Levels of CI.

<aside class="further-reading" aria-label="Further reading">
<p>Further reading</p>
<a href="/compare"><span>Compare GitHub Actions runners</span> <span>Comparison</span></a>
<a href="/fast-github-actions"><span>Fast GitHub Actions</span> <span>Guide</span></a>
<a href="/best-practices/github-actions/right-size-runners"><span>Right-size runners</span> <span>Guide</span></a>
<a href="/customers/mastra"><span>How Mastra cut CI time</span> <span>Customer</span></a>
</aside>
