---
title: "GitHub Actions runners for C++ and Bazel builds"
description: "Choose a GitHub Actions runner for C++ and Bazel using build bottlenecks, reproducible cold-build benchmarks, and a practical comparison method."
url: https://9apes.com/blog/github-actions-runners-cpp-bazel-builds/
---

# GitHub Actions runners for C++ and Bazel builds

For C++ and Bazel builds in GitHub Actions, choose a runner by measuring the work that remains after caching: compilation, linking, memory pressure, and disk activity. Start with matched vCPU counts, keep the build inputs fixed, and compare repeated runs. A larger runner is useful when your build can use its extra resources; a remote cache or remote execution service addresses a different part of the problem.

9apes provides managed GitHub Actions runners. Its published C++ benchmarks offer a starting point for a pilot, with enough provenance to inspect the workload. They do not predict the result for every C++ repository.

## Which runner resources matter for C++ builds?

Look at the slow phases before selecting a machine. A single long link step and hundreds of independent compile actions can need different changes.

| Bottleneck | What to inspect | What to compare |
| --- | --- | --- |
| Single-core work | Long serial phases while other CPUs are idle | Duration of the same phase on each runner |
| Parallelism | Whether independent compile actions keep CPUs busy | Matched sizes first, then additional vCPU |
| RAM | Peak memory, swapping, or processes killed during compilation | A configuration with sufficient memory for the same workload |
| Disk | Time reading sources, writing objects, and materializing outputs | Build duration alongside disk activity and available space |
| Cache | Which expensive actions are reused and the transfer time | Cold, unchanged, and source-edit builds separately |

Treat this table as a diagnostic checklist, not evidence that any one resource caused the published differences. Runner offerings can differ in several ways at once. Keep compiler versions, target selection, optimization settings, and dependency inputs consistent when testing them.

## Published cold-build comparisons

The [9apes benchmark results](/benchmarks/) include these Linux x86_64 C++ workloads. Values are seconds for the timed build step; lower is better.

| Workload | Build system | vCPU per provider | GitHub-hosted | 9apes |
| --- | --- | ---: | ---: | ---: |
| `google/xls` | Bazel | 8 | 4,945 | 1,486 |
| `facebook/folly` | getdeps | 2 | 1,264 | 471 |
| `duckdb/duckdb` | CMake + Ninja release | 2 | 1,554 | 739 |

These are medians of three repetitions measured on August 27, 2026 (UTC). Dependency caching was disabled. Both providers used the same vCPU count within each row; sizes differ between projects. The [original benchmark workflow](https://github.com/9apes/benchmarks/blob/aedea273b54d5b3d51836b4d6c06a8be69a1c052/.github/workflows/bench-oss.yml) and the results page provide the source commits and raw samples.

The timer excludes checkout, toolchain preparation, system-package setup, and code generation. Build commands can still download dependencies. These results do not measure queue time, runner startup, complete workflow latency, or invoiced savings.

The historical 2 vCPU results used a private benchmark setup. The current public workflow uses 4 vCPU for those projects, producing a different comparison. Review its configuration before treating a new public run as a reproduction of the historical rows.

## Runner acceleration, remote execution, and remote caching

**Runner acceleration** changes the machine executing your GitHub Actions job. In a local Bazel build, that machine performs the build actions. You can evaluate a different runner without making remote execution part of the experiment.

**Bazel remote execution** distributes build and test actions across multiple machines. It requires a compatible execution service and build configuration. Moving a job to another runner does not itself configure remote execution. See the [Bazel remote execution overview](https://bazel.build/remote/rbe).

**Bazel remote caching** shares action outputs so subsequent builds can reuse matching results. With remote caching and local execution, actions that miss the cache still run locally. Cache transfer and the remaining work both affect duration. See the [Bazel remote caching documentation](https://bazel.build/remote/caching).

This article evaluates 9apes as a runner provider. It does not establish a 9apes Bazel remote-execution or remote-cache service. Evaluate those capabilities separately when comparing build infrastructure.

For workflow delays outside compilation and linking, follow the [build-time reduction guide](/blog/reduce-github-actions-build-times/) to measure queueing, setup, and the required-check critical path.

## Run a representative pilot

Choose one job and record its source commit, compiler and Bazel versions, targets, runner configuration, and cache policy. Keep those inputs fixed across providers. Use a matched vCPU comparison before changing size.

Run at least three independent repetitions per configuration and retain every duration, including slower runs. Compare medians and ranges. Record the build-step boundary explicitly, then measure whole-job completion separately for your actual developer feedback loop.

Test cold builds, unchanged builds, and a representative source edit as distinct cases. A cold-build result cannot tell you how a warm remote cache behaves. Verify the expected outputs and tests as well as the time.

For a 9apes pilot, follow the [quick start](/docs/quick-start/) to install the GitHub App, then select a supported label such as `runs-on: 9apes-8vcpu-ubuntu-2404`. Public configurations cover Linux x86_64 on Ubuntu 24.04, from 1 to 8 vCPU. Linux arm64, Windows, and macOS require contacting 9apes about availability. Check [available runners](/docs/available-runners/) and [current pricing](/pricing/) before choosing a tier.

Use the [managed runner selection guide](/blog/managed-github-actions-runners/) to compare isolation, operating responsibilities, and complete workload cost alongside the build results.

## Does more vCPU always make a C++ build faster?

No. Additional vCPU helps only where the workload has runnable parallel work and enough supporting resources. Compare measured duration instead of assuming a proportional improvement.

## Should I fix Bazel caching before changing runners?

Inspect cache behavior first so you know what the runner still executes. Then compare runner performance under the same cache policy, including realistic cache misses.

## Is a faster GitHub Actions runner a Bazel remote executor?

No. A runner hosts the workflow job. Bazel remote execution needs a separately configured service that accepts build and test actions.

## Can I use the published times to forecast my CI bill?

Use them to decide what to test. Forecast costs from your own job durations, applicable runner rates, billing rules, and account allowances after the pilot.
