self-host →

Rightsizing runners

Choose the right instance family, CPU, RAM, and disk for RunsOn runners — fast enough to cut wall-clock time without overpaying for idle capacity.

Rightsizing is picking the smallest runner shape that still runs your job fast. Because RunsOn launches real EC2 instances that you pay for by the second, a bigger box is often cheaper per job when it finishes proportionally faster — but only up to the point where your workload stops scaling.

The three levers are the instance family, the CPU/RAM target, and the disk. In Flex these are job labels; in Fleet they are baked into the Terraform runner catalog.

Family

The family selects the EC2 instance family — the generation and class of hardware. Newer generations (e.g. c8i, m8i, r8g) are faster and frequently cheaper per unit of work than older ones.

  • Compute-bound (compilation, tests): the c class (c8i, c8g) — high clock, lower RAM ratio.
  • Balanced (most builds): the m class (m8i, m8g).
  • Memory-bound (large test matrices, JVM, data): the r class (r8i, r8g).
  • arm64: *g families (Graviton) are usually cheaper — use them when your toolchain has arm64 support. The default image is x64, so a Graviton family also needs an arm64 image, e.g. family=c8g/image=ubuntu24-full-arm64; selecting a *g family without an arm64 image fails with an architecture mismatch.
# Pin a family
runs-on: runs-on=${{ github.run_id }}/family=c8i/cpu=4
# Or widen the pool so Spot has more capacity to choose from
runs-on: runs-on=${{ github.run_id }}/family=c8i+m8i/cpu=4

CPU and RAM

Target a cpu (and optionally ram) instead of a fixed instance size, and RunsOn picks an instance that satisfies it within the chosen family:

runs-on: runs-on=${{ github.run_id }}/cpu=8/ram=16

Guidance:

  • Match cpu to your job’s actual parallelism. A single-threaded job gains nothing from 16 vCPUs.
  • Use a range (cpu=4+16) when the exact size doesn’t matter — it expands the available pool.
  • Measure before sizing up: the runner metrics show CPU, memory, and I/O utilization per job, so you can see whether a job is actually CPU-, memory-, or I/O-bound.

Disk

Default EBS volumes are fine for most jobs. For I/O-heavy workloads (large Docker builds, big checkouts), the bottleneck is often disk, not CPU:

  • Choose families with local NVMe storage for the fastest scratch space.
  • Or tune the EBS volume (size, IOPS, throughput) via the volume label.
  • For pure throughput on data that doesn’t need to survive the job, see YOLO mode (tmpfs).
  • Spot pricing — run rightsized instances on Spot capacity for up to 90% savings.
  • Warm pools — keep rightsized runners pre-warmed for sub-10s pickup.
  • Runner metrics — measure utilization to validate your sizing.