Skip to content
Latchkey

How to Run GitHub Actions on ARM (and Whether You Should)

Every provider prices ARM below x64, frequently by 25 to 40%. It is the largest unclaimed CI saving available, and the reason most teams skip it is a compatibility fear that is mostly out of date.

ARM is cheaper on every runner price list. GitHub lists Linux arm64 2-core at $0.005/min against $0.006 for x64; Blacksmith lists $0.0025 against $0.004; WarpBuild lists arm64 25% below its own x64 rates at every size.

The historical objection was that the ecosystem was not ready. For most interpreted and JIT languages that has not been true for some time. The remaining risk is concentrated in native dependencies and prebuilt binaries, which is testable in an afternoon.

What the saving actually is

ProviderLinux x64, 2 vCPULinux arm64, 2 vCPUDifference
GitHub-hosted$0.006/min$0.005/min17%
Blacksmith$0.004/min$0.0025/min38%
WarpBuild$0.004/min$0.003/min25%
Semaphore$0.0075/min$0.003/min60%

Switch and see what breaks

.github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-24.04-arm    # GitHub-hosted ARM
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci && npm test

What actually breaks

  • Native modules without ARM prebuilds. They fall back to compiling from source, which needs a toolchain present and is slower. Usually it works; occasionally it fails outright.
  • Downloaded binaries with a hardcoded architecture. Any script fetching an x86_64 release asset breaks. Detect the architecture rather than assuming it.
  • Docker images without an arm64 variant. A single-arch base image fails to pull, or silently runs under emulation and is dramatically slower.
  • Anything vendored as a compiled artifact where you never controlled the build.

Building multi-arch images

.github/workflows/ci.yml
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
  with:
    platforms: linux/amd64,linux/arm64
    push: true
    tags: ghcr.io/${{ github.repository }}:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

Measure before you optimise

Pipeline optimisation usually targets the step people assume is slow. Get the real per-step timings first, because the answer is frequently dependency install or a cold cache rather than the build itself.

Terminal
# per-job timings for the last 20 runs
gh run list --limit 20 --json databaseId,conclusion,createdAt,updatedAt \
  --jq '.[] | "\(.conclusion)\t\(.createdAt)\t\(.updatedAt)"'

# per-step timing inside one run
gh run view <run-id> --log | grep -E "^\S+\s+.*Run |##\[group\]" | head -40

Frequently asked questions

Are ARM runners cheaper?
Yes, on every provider that publishes both. GitHub-hosted arm64 is $0.005/min against $0.006 for x64; Blacksmith is $0.0025 against $0.004; Semaphore is $0.003 against $0.0075. It is usually the largest unclaimed saving in a CI budget.
Will my build work on ARM?
For most interpreted and JIT languages, yes, with no changes. Risk is concentrated in native modules without ARM prebuilds, scripts that download x86_64 binaries, and Docker base images with no arm64 variant. Test one job for a week; failures surface immediately.
Is ARM slower than x64?
Not inherently, and modern ARM server cores are competitive or better per clock. It is dramatically slower if you accidentally run x64 images under QEMU emulation, which is the usual cause of an "ARM is slow" conclusion.
How do I build images for both architectures?
Use buildx with platforms: linux/amd64,linux/arm64. For speed, build each architecture on a native runner and merge the manifest rather than emulating, since QEMU is several times slower than native.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card