GitHub Actions Runners

CI without managing runners.

A single-tenant microVM for every job, on bare metal we own. One line changes in your workflow file. Billed by the second, with queue time free.

npx sandal migrate --repo your-org/your-repo
.github/workflows/ci.yml−1 +1
name: ci
on: [push, pull_request]
jobs:
test:
- runs-on: ubuntu-latest
+ runs-on: sandal-ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: pnpm install --frozen-lockfile
- run: pnpm test

That is the whole migration. Same steps, same actions, same secrets. Only the executor underneath changes.

<1smicroVM boot
1.9smedian queue
per secondmetering
0reused machines
EU + USregions
Isolation

A clean machine, every job.

Self-hosted runners rot: state from the last job leaks into the next, and a malicious fork PR gets a machine somebody else will use later. Sandal never reuses a machine.

Ephemeral

One job, one microVM.

Firecracker boots a single-tenant VM when the job is queued and destroys it the moment the job exits. Every job gets its own guest kernel over KVM, run sudo, Docker, Kubernetes and kernel modules directly, no Docker-in-Docker, no user-namespace hacks.

Boot <1s · teardown 180ms · runner token lifetime: one job.

.github/workflows/ci.yml
jobs:
build:
runs-on: sandal-ubuntu-24.04-8
steps:
- uses: actions/checkout@v4
# a real kernel: docker, kvm and sudo just work
- run: docker build -t app:ci .
- run: sudo modprobe wireguard
- run: kind create cluster
Caching

Ephemeral machines, persistent cache.

A sticky disk is a named volume attached to a label. It survives across jobs even though the machine does not, mounted by name, restored lazily on first read, and committed as a delta at teardown rather than re-uploaded.

.github/workflows/ci.yml
jobs:
build:
runs-on: sandal-ubuntu-24.04-8
env:
SANDAL_STICKY_DISK: pnpm-store-v1
steps:
- uses: actions/checkout@v4
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build test
# mounted at /mnt/sandal/pnpm-store-v1
Sticky disk
3.1snode_modules restore, down from 47s with actions/cache
2.4suntil a 6.2 GB pnpm store is usable after mount
40–90 MBtypical delta committed at teardown, not a full re-upload
$0.15per GB-month · first 10 GB per org free · idle disks released after 30 days
macOS

Apple silicon we actually own.

Apple-silicon Mac minis in our racks, one job at a time. Every job gets a fresh VM created with Virtualization.framework from a golden image, deleted when the job ends. Keychains, provisioning profiles and derived data never cross between builds.

Simulators and Xcode builds
.github/workflows/ios.yml
jobs:
archive:
runs-on: sandal-macos-m4
env:
SANDAL_STICKY_DISK: derived-data-main
steps:
- uses: actions/checkout@v4
- run: xcodebuild archive -scheme App \
-destination "generic/platform=iOS" \
-archivePath build/App.xcarchive
Mac hosts
HostMac mini, Apple silicon, dedicated · one job per host
Xcode16.4 · 16.2 · 15.4, selectable with xcode-select
RuntimesiOS 18.4 · iOS 17.5 · watchOS 11
Claim12s median from a warm pool
Rate$0.01 / min
Matrix

Linux, Arm and macOS in one file.

Runner size and OS are part of the label, so machine selection lives in the workflow file and travels with the pull request that needs it. Matrix x86, aarch64 and Apple silicon legs in the same job and the same account.

.github/workflows/release.yml
jobs:
build:
strategy:
matrix:
include:
- runner: sandal-ubuntu-24.04-16
target: x86_64-unknown-linux-gnu
- runner: sandal-ubuntu-24.04-arm-16
target: aarch64-unknown-linux-gnu
- runner: sandal-macos-m4
target: aarch64-apple-darwin
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- run: cargo build --release --target ${{ matrix.target }}
Getting started

Three steps to faster CI.

No runner VMs to provision, no agent to install on a box you own, no autoscaler to babysit. Median time from install to first green job on Sandal hardware: 4 minutes.

.github/workflows/ci.ymlthe whole migration
name: ci
on: [push, pull_request]
jobs:
test:
- runs-on: ubuntu-latest
+ runs-on: sandal-ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: pnpm install --frozen-lockfile
- run: pnpm test
  1. 01

    Install the GitHub App

    Grant it Actions and workflow read on the repositories you want to move. It never reads your source outside of a job it is running.

  2. 02

    Swap the label

    Change runs-on: ubuntu-latest to runs-on: sandal-ubuntu-24.04. The migration wizard rewrites the labels for you and opens a pull request.

  3. 03

    Push

    The next queued job claims a fresh microVM on our metal instead of a shared GitHub VM. Logs, annotations, secrets, OIDC, artifacts and re-runs work as before.

Speed

Faster because
the hardware is faster.

One vCPU is one physical core on a 4.5 GHz EPYC, not a hyperthread on a five-year-old cloud instance. Local NVMe instead of network block storage. Medians from workflows that run every six hours on public repositories.

Workload Test Comparison

2.7xFaster
Node monorepo, install + test
3m 04s
8m 12s
3.3xFaster
Rust release build, cold cache
6m 41s
21m 48s
3.1xFaster
Docker image build
4m 02s
12m 35s
2.2xFaster
Go test suite, race detector
2m 51s
6m 24s
2xFaster
iOS build on macOS
7m 18s
14m 50s
2.3xFaster
Python wheels, 4-way matrix
7m 44s
18m 06s
Sandal GitHub-hosted
Label grammar

Pick it in the workflow file.

Label
vCPU / RAM
Arch
sandal-ubuntu-24.04Linux
2 / 4 GB
x86_64
sandal-ubuntu-24.04-8Linux
8 / 16 GB
x86_64
sandal-ubuntu-24.04-32Linux
32 / 64 GB
x86_64
sandal-ubuntu-24.04-armLinux on Arm
2 / 4 GB
aarch64
sandal-ubuntu-24.04-arm-32Linux on Arm
32 / 64 GB
aarch64
sandal-windows-2025Windows, preview
2 / 8 GB
x86_64
sandal-windows-2025-16Windows, preview
16 / 64 GB
x86_64
sandal-macos-m4macOS
8 / 16 GB
arm64

RAM scales with vCPU at 2 GB per core on Linux and 4 GB per core on Windows. Disk is 150 GB on every shape. If a label does not exist, the job fails fast at claim time with the list of valid labels, rather than queueing forever.

sandal-

Routes the job to our fleet.

The prefix moves the job off GitHub’s pool and onto ours. Everything after it describes the machine.

<os>

Image family and version.

ubuntu-24.04, windows-2025, macos-m4. Add -arm to any Ubuntu label to move it to aarch64.

<size>

vCPU count.

2, 4, 8, 16, 32, 64. Omit it for the 2 vCPU / 4 GB default. Machine selection travels with the pull request that needs it.

Cost

No CI tax.

A runner is just a sandbox billed by the minute, priced at the same $0.036/VM-hour machine, so a CI minute is a fraction of a cent. GitHub’s published per-minute rate sits alongside ours.

Runner
GitHub-hosted / min
Sandal / min
Linux2 vCPU / 4 GB
$0.008
$0.0003
Linux4 vCPU / 8 GB
$0.016
$0.0006
Windows2 vCPU / 8 GB
$0.016
$0.0006
macOSM-series, Apple silicon
$0.062
$0.01

Billed per second, never rounded to the minute, a 71-second job bills 71 seconds. Queue time is free; a cancelled job bills only the seconds that ran.

Concurrency

Run every job at once.

There is no per-plan concurrency licence to buy. A 400-job matrix starts 400 microVMs; the job start rate is 60/s on every plan and the ceilings are published up front.

Queue time

1.9s median, 4.2s P99.

Measured over 30 days on our own monorepo, 1.2M jobs, through a 500-job spike. GitHub’s P50 on the same runs was 11.4s. Warm pools keep it flat as the burst grows.

Per-OS pages

One page per runner type.

macOS, ARM and Windows runners each have their own benchmarks, labels and rate-vs-GitHub table.

Plans

One balance. Every machine you run.

Runners draw from the same credit pool as your sandboxes, simulators and Xcode builds, billed by the second at one published rate card.

Starter

For a solo build or a first agent in production.

$20/ month

$20 of usage credits

≈ 550 VM-hours or 33,000 CI minutes

Get started
  • 20 concurrent machines
  • Committed in-plan rates on every product
  • Linux & Windows, sandboxes, VMs, runners
  • Per-second billing, paused is free
  • Full API, SDKs, CLI & MCP server
  • Unlimited seats
  • Community support
Most popular

Team

Apple silicon unlocked, and a balance that rolls over.

$100/ month

$100 of usage credits

≈ 2,700 VM-hours or 10,000 Mac build minutes

Get started
  • 100 concurrent machines
  • Everything in Starter, plus:
  • macOS, iOS/Android simulators & Xcode builds
  • Unused credits roll over one month
  • Usage exports & per-second line items
  • Email support

Business

Higher ceilings for a team leaning on the fleet daily.

$250/ month

$250 of usage credits

≈ 6,900 VM-hours or 25,000 Mac build minutes

Get started
  • 300 concurrent machines
  • Everything in Team, plus:
  • Raised concurrency & start rate
  • Priority support & shared Slack channel
  • Single sign-on (SAML / OIDC)
  • Volume credit rate

Enterprise

Dedicated capacity, your compliance surface, one invoice.

Custom

Committed volume rate

Committed volume rate, negotiated per account

Talk to sales
  • Reserved bare-metal capacity
  • BYOC or self-host the control plane
  • SSO & SCIM, audit logs, data residency
  • Annual invoicing, MSA, DPA
  • Support SLA & solutions engineering
  • Custom regions on request

Every plan spends its credit pool first, then usage keeps flowing and bills per second at your committed in-plan rate, no hard stop mid-run, no forced upgrade. Without a plan you pay the on-demand rate instead. Prices are illustrative pending final list pricing; the per-second billing model is final.

Rate card

The runner rate card.

Linux

MachineIn-plan / minuteOn-demand / minute
2 vCPU / 4 GB$0.0003$0.0004
4 vCPU / 8 GB$0.0006$0.0009
8 vCPU / 16 GB$0.0012$0.0018
16 vCPU / 32 GB$0.0024$0.0036
32 vCPU / 64 GB$0.0048$0.0072
64 vCPU / 128 GB$0.0096$0.014

Windows

MachineIn-plan / minuteOn-demand / minute
2 vCPU / 4 GB$0.0006$0.0009
4 vCPU / 8 GB$0.0012$0.0018
8 vCPU / 16 GB$0.0024$0.0036
16 vCPU / 32 GB$0.0048$0.0072
32 vCPU / 64 GB$0.0096$0.014
64 vCPU / 128 GB$0.019$0.029

macOS · Apple silicon

MachineIn-plan / minuteOn-demand / minute
M-series · 4 cores / 8 GB$0.01$0.015
M-series Pro · 8 cores / 16 GB$0.02$0.03

Linux runners are the VM ladder priced per minute; Windows is 2x. macOS runners are dedicated Apple-silicon Mac minis. On-demand is 1.5× the in-plan rate. All usage bills by the second; paused machines are not billed.

Get started

Give your AI agents somewhere to run today.

Your agents get their own machines across Linux, macOS, Windows, iOS and Xcode in milliseconds, and you stop paying for idle.

npx sandal init