Play ComputeBeta

Cut GitHub Actions
cost by 8x

Change one line of a workflow to runs-on: play-4vcpu. Every job runs on its own fresh microVM, and you pay per second for what it used.

Install GitHub App
GitHub-hosted$9,573a month
vercel/next.js
astral-sh/ruff
grafana/grafana
django/django
spring-projects/spring-boot
Play Compute$1,118a month
vercel/next.js
astral-sh/ruff
grafana/grafana
django/django
spring-projects/spring-boot
$8,455 a month stays with you

A month of the five open-source workflows in the race below, on each runner. Sample numbers, not measurements.

Same job, same commit, two runners

Five open-source workflows, replayed on a GitHub-hosted runner and on Play Compute.

vercel/next.jsbuild-and-test / test dev · 8 vCPU on bothSample numbers
GitHub-hosted13:23$0.308
Queued
Queued
Set up job
Set up job
Checkout
Checkout
Restore cache
Restore cache
Install dependencies
Install dependencies
Build
Build
Run tests
Run tests
Upload artifacts
Upload artifacts
Done in 13m 23s
Play Compute6:07$0.033
Queued
Queued
Set up job
Set up job
Checkout
Checkout
Restore cache
Restore cache
Install dependencies
Install dependencies
Build
Build
Run tests
Run tests
Upload artifacts
Upload artifacts
2.2x faster
Done in 6m 07s
Cost of this runOne side bills every started minute, the other every second.
you.$2,604kept a monthThis workflow switched. Deal with it.
paid 7.5 billion for your git host.$0billed a monthThis workflow switched. Will round up for food.
Each workflow's job on a GitHub-hosted runner and on Play Compute
WorkflowSpeedSaved a month
2.2x faster$2,604
2.3x faster$1,559
1.9x faster$1,675
1.8x faster$641
2.0x faster$1,977

Sample numbers, not measurements. Each row is one job at one commit with the same vCPUs on both sides; GitHub-hosted is priced at list price for a private repository, and a month is the job's runs in the last 30 days.

Pay for the memory a job used

A GitHub-hosted runner bills its whole machine for every minute it starts. Play Compute bills the memory a job is using, second by second.

GitHub-hosted bills the whole machine
All 64 GiB, for 8 started minutes
Play Compute bills the memory in use
8.6 GiB on average, for 7m 29s
016 GiB32 GiB48 GiB64 GiB7:29
77% less

than GitHub-hosted for this 7m 29s build on play-16vcpu, priced on each runner.

Play Compute
$0.076Blacksmith
$0.256GitHub-hosted
$0.336
  • play-2vcpu
    2 vCPUs · 8 GiB
    $0.081 an hour
    $0.360 on GitHub-hosted
  • play-4vcpu
    4 vCPUs · 16 GiB
    $0.161 an hour
    $0.720 on GitHub-hosted
  • play-8vcpu
    8 vCPUs · 32 GiB
    $0.323 an hour
    $1.32 on GitHub-hosted
  • play-16vcpu
    16 vCPUs · 64 GiB
    $0.645 an hour
    $2.52 on GitHub-hosted

Linux x86-64 on Ubuntu 24.04, 4 GiB of memory for each vCPU. An hour is priced for a job that uses 25% of its memory; a job that uses less pays less.

Change one line. Keep the rest.

The pull request changes runs-on and nothing else. The actions a workflow already uses answer on the same service APIs as on GitHub, run next to the machines.

Run CI on Play Compute
acme/api: into main from play-compute/runs-on
Open
.github/workflows/ci.yml+1 −1
1 name: ci
2 on: [push, pull_request]
3
4 permissions:
5 contents: read
61 id-token: write
7
8 jobs:
9 test:
10− runs-on: ubuntu-latest
10+ runs-on: play-4vcpu
11 strategy:
122 matrix:
13 node: [20, 22]
14 services:
153 postgres:
16 image: postgres:17
17 steps:
18 - uses: actions/checkout@v5
194 - uses: actions/setup-node@v4
20 with:
21 node-version: ${{ matrix.node }}
225 - uses: actions/cache@v4
23 with:
24 path: ~/.pnpm-store
25 key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
26 - run: pnpm install && pnpm test
276 - uses: actions/upload-artifact@v4
28 with:
29 name: coverage
30 path: coverage/
  1. 1
    OIDC tokens
    id-token: write for keyless sign-in to AWS, GCP, Azure, Vault
  2. 2
    Matrix, needs, if, strategy
    Fan-out, fail-fast, max-parallel, include and exclude
  3. 3
    Service containers
    services: postgres, redis, ... with health checks and ports
  4. 4
    Tool cache
    setup-node, setup-python, setup-go, setup-java, rustup
  5. 5
    Cache service v2
    actions/cache@v4, setup-* caches, restore keys, cross-branch scopes
  6. 6
    Artifacts v4
    actions/upload-artifact@v4 and download-artifact@v4, retention, merge

Also unchanged: Live logs and log archive, Job summaries, Annotations and problem matchers, Artifact attestations, Packages and container registry, Reusable workflows and composite actions, Outputs and environment files, Concurrency groups, Environments and approvals, Re-run failed jobs, Timeouts and cancel, Every trigger, Container jobs, Docker, buildx and compose.

Not offered: KVM inside the job, Linux arm64, macOS and Windows, GPU. Keep those jobs on their current runners: the switch is per job.

The GitHub App opens it for you

One pull request per repository you pick, covering every workflow in it.

  1. 1

    Install the App

    Pick the repositories it may serve. It never reads your secrets.

  2. 2

    Review the pull request

    It changes runs-on and nothing else, with a size suggested for each job.

  3. 3

    Merge it

    The next push runs here, with the same actions, caches and secrets.

Your coding agent gets the CLI

One command signs a machine in for as long as you choose, installs the playc CLI and tells your agent how to use it.

your agent's session
!curl -fsSL https://compute.play.fast/connect/PC-7K2M-9XQD | sh
okplayc installed
okthis machine is signed in to acme for 8 hours
okinstructions for the agent printed

An agent never sees secrets and cannot change billing or settings.

A failed job opens at the error

Steps and logs come first, scrolled to the first error, and every log is searchable across runs.

Failedplay-8vcpuweb / CI / e2e (shard 2/3)Run 933
  1. Set up job1.1 s
  2. Run actions/checkout@v52.1 s
  3. Restore cache3.7 s
  4. Install dependencies28 s
  5. Build app1m 48s
  6. Run Playwright tests2m 33s
  7. Upload artifact
  8. Post Restore cache1.4 s
  9. Complete job512 ms
Run Playwright tests6,752 lines in this step
6745 1) [chromium] > checkout.spec.ts > applies a coupon at checkout
6746 Error: expect(locator).toHaveText(expected)
6747 Expected string: "Total 18.00"
6748 Received string: "Total 20.00"
6749 Call log: waiting for getByTestId('order-total') (5000ms)
6750 1 flaky-looking failure: this test passed on 11 of its last 14 runs
6751 1 failed, 6,739 passed (2.4m)
6752Process completed with exit code 1.
Memory at its peak
9.1 GiB of 32 GiB
Cost
$0.026

Questions before you switch

Is a job isolated from other jobs?
Yes. Each job gets a new microVM with its own kernel, and the machine is deleted when the job ends. Jobs share nothing but the caches you save.
How do I switch back?
Change runs-on back, for one job or for all of them. There is no contract to end and nothing to uninstall first.
What is on the machine?
An image that follows GitHub's ubuntu-24.04 runner image: the same languages, Docker, browsers and tools, with passwordless sudo, so setup actions find what they expect.
How long can a job run, and how many at once?
Up to 5 days, GitHub's own limit for a job on a self-hosted runner. We set no limit on how many jobs run at once.
Where do jobs run?
In us-south1 (Dallas), across three zones. Cache and artifacts are stored in the same region as the job.
What does a slow queue or a lost job cost me?
Nothing. Billing starts when the runner picks up the job, and a job lost to the platform is re-run without a charge.

Try it on one workflow

Switch one job, and compare its next run with its last.

Install GitHub App
A short job, billed both ways
play-4vcpu, 4 GiB of 16 in use
Ran for2m 07s
GitHub-hosted$0.036
3 started minutes, in full
Play Compute$0.006
127 seconds, none rounded-mark up
Kept$0.030