Runners
5 min read

GitHub Actions vs BuildPulse runners, benchmarked

Real open-source workloads, pinned commits, public runs you can fork and rerun: the same jobs finish 1.3 to 2.1x sooner and cost 50 to 68 percent less. Every number links to a run.

BuildPulse Team

October 23, 2026

Every managed runner's homepage says faster. Saying it is easy. We moved our own repos to our own runners and then measured it in public, on real workloads, with a repo you can fork and rerun. Claims are marketing; runs are receipts. Here are the receipts.

In October we moved the benchmark to three heavier workloads that look like real CI: a Rust test and lint run, a TypeScript monorepo install and typecheck, and a production Docker image. Each one is a pinned commit of a real open-source project, runs five times per runner on identical jobs, and we report the median.

Everything below comes from github.com/buildpulse/runner-benchmarks, measured on October 23, 2026. If you get different numbers, let us know by opening an issue.

Speed, measured

Both runners at 4 vCPU. Median seconds of the timed steps, and each number links to its run:

WorkloadGitHub ubuntu-latestBuildPulse x64-4xFasterTime saved
Rust test and lint (spotify-player, cargo test + clippy)231 s112 s2.1x52%
TypeScript install and typecheck (opencode, bun install + turbo typecheck)123 s64 s1.9x48%
Docker image build (PostHog, no cache)787 s588 s1.3x25%

The pattern holds from August: the more of the job is raw compute, the bigger the gap. Rust and TypeScript are close to twice as fast. A Docker build spends more of its time writing layers to disk, so the gain is smaller. The Docker build runs on our docker-builder 4x runner, the label we recommend for image builds, and both sides build with buildx and no cache.

The first round, in August, used hugo and vuejs/core (a Docker build, a Node install and test run, and a Go compile). Those workflows are still in the repo, and our CircleCI and Semaphore comparisons run them.

A newer AMD EPYC generation is most of the story. That claim is measurable too, so we measured it.

The hardware, measured

Same sysbench and fio commands on both runners, straight from the hardware workflow:

ProbeGitHub ubuntu-latestBuildPulse x64-4xDifference
CPUAMD EPYC 7763 (Zen 3, 2021)AMD EPYC 9R45 (Zen 5, 2025)2 generations
sysbench CPU (events/s, all cores)8,18622,178+171%
sysbench memory throughput12.0 GB/s27.8 GB/s+133%

Zen 5 is the newest server generation AMD makes; GitHub's fleet is two generations behind it. The full probe set, including disk, is in the public workflow logs; run it yourself on any runner.

The cache your next build actually hits

Cold-vs-warm is where runner infrastructure differences stop being subtle. The docker-cache workflow builds hugo's Dockerfile twice, and the warm build runs on a fresh machine, so any difference between cold and warm comes from the cache infrastructure and nothing else. The GitHub side uses GitHub's own recommended caching for docker builds. On BuildPulse there is nothing to set up: docker-builder machines keep each customer's layer cache between builds, so your second build is fast without a single line of cache configuration in the workflow.

Cold buildWarm rebuild, fresh machine
GitHub + gha cache276 s3 s
BuildPulse docker-builder145 s1 s

Cold builds finish in about half the time. Warm rebuilds of this image are near-instant on both: hugo's layer cache is small enough that GitHub's remote cache restores it fine. The differences that matter show up in what surrounds that number.

  • Zero configuration. No cache-from, no cache-to, no scope keys in your workflow.
  • No 10 GB per-repo cache quota, no eviction fighting other caches in the repo.
  • Package-manager caches inside the build (RUN --mount=type=cache) survive between builds, because the cache is the build machine's own storage. Remote cache backends drop these.

Cost per build

Our per-minute rate is 33 percent below GitHub's at 4 vCPU ($0.008 against $0.012). You also buy fewer minutes, because the same job finishes sooner. Together, job time multiplied by each side's per-minute rate:

WorkloadGitHubBuildPulseSaved
Rust test and lint$0.0462$0.014968%
TypeScript install and typecheck$0.0246$0.008565%
Docker image build$0.1574$0.078450%

In dollars at 4 vCPU, every $100 of these builds on GitHub Actions costs about this much here: Rust $32, TypeScript $35, Docker $50. The full rate table is on buildpulse.io/pricing.

Why BuildPulse

The same jobs finish in 25 to 52 percent less time, on CPUs two generations newer, with a docker cache that works without configuration. In practice: a $1,000 a month GitHub Actions bill becomes roughly $320 to $500 for the same workloads, or the same budget runs 2 to 3 times the CI. You do not have to take our word for any of this: every number above links to a public run, and the harness is a repo you can fork and point at your own workloads.

Then there is the part no benchmark table shows, and the reason we built runners at all: every job on BuildPulse runners feeds the same platform's flaky-test detection. A red build tells you whether the failure is your code or a test with a known failure history before an engineer spends twenty minutes finding out manually. Faster minutes are table stakes; knowing which failures matter is the product.

.github/workflows/ci.yml

 jobs:   test:-    runs-on: ubuntu-latest+    runs-on: bp-ubuntu-latest-x64-4x

That is the entire change for most workflows. A few workflows also need a setup action for tools our deliberately minimal image does not preinstall (in our own migration of thirteen repos, exactly three: composer, sbt, and Chrome). The full step-by-step guide is its own post.

Move one job and compare the bill

Change one runs-on line, run it next to your current runner for a day, and keep whichever is faster.

3,000 free minutes every month · No credit card required