GitHub Actions runner specs: vCPU, RAM and disk by size
GitHub Actions runner specs depend on two things your workflow never mentions: the label you asked for, and whether the repository is public or private. The tables below are from the GitHub-hosted runners reference read on 2026-09-20, beside what a latchkey-small runner actually measured the same day.


The number that surprises people is the private repository one. The same runs-on: ubuntu-latest line gets 4 vCPU and 16 GB of RAM on a public repository and 2 vCPU and 8 GB on a private one, which is half the machine for identical YAML. A build sized against the public figure and then moved into a private repository is the origin of a large share of out-of-memory reports.
The second thing worth knowing before the tables: disk is 14 GB on every standard runner regardless of size, and the preinstalled toolchain is already inside it when your job starts. Larger runners are the only way to change that number on GitHub-hosted hardware.
Standard runners
Every row here is from the GitHub-hosted runners reference, read on 2026-09-20. macOS is the exception to the public and private split: the shapes are identical either way.
| Runner | Public repository | Private repository | SSD |
|---|---|---|---|
Linux x64: ubuntu-latest, ubuntu-24.04, ubuntu-26.04, ubuntu-22.04 | 4 vCPU, 16 GB | 2 vCPU, 8 GB | 14 GB |
Linux x64 slim: ubuntu-slim | 1 vCPU, 5 GB | 1 vCPU, 5 GB | 14 GB |
Linux arm64: ubuntu-24.04-arm, ubuntu-26.04-arm, ubuntu-22.04-arm | 4 vCPU, 16 GB | 2 vCPU, 8 GB | 14 GB |
Windows x64: windows-latest, windows-2025, windows-2022 | 4 vCPU, 16 GB | 2 vCPU, 8 GB | 14 GB |
Windows arm64: windows-11-arm | 4 vCPU, 16 GB | 2 vCPU, 8 GB | 14 GB |
macOS arm64: macos-latest, macos-26, macos-15 | 3 vCPU, 7 GB | 3 vCPU, 7 GB | 14 GB |
macOS Intel: macos-26-intel, macos-15-intel | 4 vCPU, 14 GB | 4 vCPU, 14 GB | 14 GB |
Larger runners
Larger runners are the paid tier and the only way to change the hardware without self-hosting. They are also where the disk stops being 14 GB: the sizes carry between 75 GB and 2 TB, which for a Docker-heavy build is frequently the reason to move rather than the CPU.
The specifications below are the larger runners reference read on 2026-09-20. GitHub bills them "at the per-minute rate for the amount of time workflows are executed on them", with "no cost associated with creating a larger runner that is not being used", and free minutes do not apply. Linux and Windows larger runners can also carry static IP addresses, which is the usual reason a team on a firewalled network picks them over standard runners.
| vCPU | RAM | SSD | Architectures | Operating systems |
|---|---|---|---|---|
| 2 | 8 GB | 75 GB | x64, arm64 | Ubuntu |
| 4 | 16 GB | 150 GB | x64, arm64 | Ubuntu, Windows |
| 8 | 32 GB | 300 GB | x64, arm64 | Ubuntu, Windows |
| 16 | 64 GB | 600 GB | x64, arm64 | Ubuntu, Windows |
| 32 | 128 GB | 1200 GB | x64, arm64 | Ubuntu, Windows |
| 64 | 256 GB on x64, 208 GB on arm64 | 2040 GB | x64, arm64 | Ubuntu, Windows |
| 96 | 384 GB | 2040 GB | x64 | Ubuntu, Windows |
| 4, plus 1 Tesla T4 GPU | 28 GB, and 16 GB of VRAM | 176 GB | Not published: the GPU table carries no architecture column | Ubuntu, Windows |
macOS larger runners
macOS is sized differently enough that the reference gives it its own table, and it is the one place where a "larger" runner can have fewer cores than you expect. The XLarge is an M2 with five CPU cores; the Large is an older Intel machine with twelve.
| Runner size | Architecture | Processor (CPU) | Memory (RAM) | Storage (SSD) | Workflow label |
|---|---|---|---|---|---|
| Large | Intel | 12 | 30 GB | 14 GB | macos-latest-large, macos-14-large, macos-15-large (latest), macos-26-large |
| XLarge | arm64 (M2) | 5 (+ 8 GPU hardware acceleration) | 14 GB | 14 GB | macos-latest-xlarge, macos-14-xlarge, macos-15-xlarge (latest), macos-26-xlarge, xcode-27-xlarge (public preview) |
ARM, and when it is the right answer
ARM64 runners are standard-tier on Linux and Windows and carry the same shapes as their x64 equivalents: 4 vCPU and 16 GB on a public repository, 2 and 8 on a private one. The labels are explicit rather than automatic, which is the important part: ubuntu-24.04-arm is a different label from ubuntu-24.04, and ubuntu-latest does not resolve to ARM under any circumstances.
The reason to use them is usually not benchmarks. It is that your production target is ARM, and building on x64 with emulation is slow enough that a native runner pays for itself on Docker builds alone. If your artifacts are architecture-independent, the case is weaker and the migration cost is real: a native dependency without an ARM wheel or prebuilt binary will find you.
Check it from inside your own job
The only specification that is definitely true for your run is the one your run prints. This step costs about a second and turns "the runner must have changed" from a theory into a line you can diff against the previous run.
- name: Runner hardware
run: |
echo "cpus: $(nproc)"
awk '/^MemTotal:/{printf "mem: %.2f GiB\n", $2/1048576}' /proc/meminfo
df -h / | awk 'NR==2{print "disk: "$2" total, "$4" available"}'
echo "image: ${ImageOS:-unknown} ${ImageVersion:-unknown}"
uname -srmMeasured on a latchkey-small runner
github-actions-runner-specs.sh, under content/repro/, runs those same commands and a few more. It ran on a Latchkey latchkey-small runner on 2026-09-20, whose documented size is 2 vCPU and 8 GB, and the point of printing it here is to show the gap between a documented size and a measured one rather than to claim parity with anything.
Two things in the output are worth reading carefully. MemTotal is 7.55 GiB against a documented 8 GB, which is the ordinary difference between what the kernel hands userspace after firmware and kernel reservations, reported in binary units, and a size sold in decimal ones. Every vendor is a few percent short by this measure, including GitHub, and the practical consequence is that a heap ceiling set to the sold number is a ceiling the machine cannot honor. Budget against the measured number.
The second is swap: 0B on this machine. With no swap, a process that exceeds memory is killed rather than slowed, which is why the symptom is exit code 137 and not a build that crawls. Print free -h in your own job before assuming any runner has a safety margin underneath the RAM figure, because that line is the difference between a slow step and a killed one.
os: Ubuntu 24.04.4 LTS (x86_64, kernel 6.17.0-1019-aws)
cpu model: AMD EPYC 7R13 Processor
nproc: 2
cpu cores: 2
MemTotal: 7.55 GiB (7919632 kB)
MemAvailable: 6.84 GiB
free -h: total 7.6Gi, available 6.8Gi
swap: 0B
cgroup mem max: not exposed to the job
root disk: 96G size, 54G used, 43G available
workspace disk: 96G size, 43G available
root inodes: 12976128 total, 11982404 free
image labels: ImageOS=unset ImageVersion=unset
package arch: amd64
runner user: runner (sudo: yes)Right-sizing, which is mostly about disk
A lint job does not need sixteen cores, and you pay per minute whether or not the cores are busy, so the smallest size that keeps the job acceptably fast is the correct one. That is the easy half.
The harder half is that most jobs that genuinely need a bigger runner need it for memory or disk rather than CPU. A test suite killed at 137 needs RAM. A Docker build that fails partway through needs disk, and on a standard runner it has 14 GB minus whatever the image already occupies, which is why freeing disk space is such a common workaround. Adding cores to either of those changes nothing except the bill.
Measure before you move. Print the peak with /usr/bin/time -v on the heavy step, or watch df -h around the Docker build, and you will usually find that one job in the matrix needs the larger machine and the other eleven do not.
Key takeaways
- The same label gets 4 vCPU and 16 GB on a public repository and 2 vCPU and 8 GB on a private one.
- Standard runners have 14 GB of disk at every size, with the toolchain already inside it.
- Larger runners go to 96 vCPU and 2 TB of disk, are billed from the first minute, and get no free minutes.
- ARM labels are explicit:
ubuntu-latestnever resolves to arm64. - A documented 8 GB reads as 7.55 GiB on a real machine. Budget against the measurement.
Frequently asked questions
Does a private repository runner really only have 7 GB of memory?
free -g tends to report, because the kernel does not hand all of the installed memory to userspace. Both numbers describe the same machine. Size your heap against what /proc/meminfo reports in the job, not against either published figure.Are GitHub Actions larger runners worth it?
How do I increase disk space on a GitHub Actions runner?
How do standard, larger and self-hosted runners compare on hardware?
Related guides
References
- GitHub-hosted runners reference: standard runner specifications (read 2026-09-20)
- Larger runners reference: sizes, architectures and macOS shapes (read 2026-09-20)
- Larger runners concepts: billing and static IP addresses (read 2026-09-20)
- Choose the runner for a job: labels, arrays and runner groups (read 2026-09-20)
- GitHub Actions documentation