Bowse CI · GitHub Actions runners

Move CI off hosted runners without touching your steps.

Bowse is a drop-in runner pool for GitHub Actions. Swap the runs-on label, keep every step, and each job gets a clean Ubuntu microVM sized to the work.

~/.github/workflows/test.ymlOne-line change
name: teston: [push, pull_request]jobs:  test:    runs-on: ubuntu-latest    runs-on: bowse    timeout-minutes: 20    steps:      - uses: actions/checkout@v4      - uses: actions/setup-node@v4        with: { node-version: 22, cache: npm }      - run: npm ci && npm test
Runner label
2 vCPU · 8 GB · $0.0012/min

Estimate

Run the numbers on your own CI volume.

Set job length and monthly job count, enter what you pay per minute now, and see what every Bowse size costs for the same minutes.

CI runtime estimate

USD · per month
Bowse runner size

20,000 runtime minutes / month

  • Your current runner

    at $0.0080/min

    $160.00

  • Bowse

    bowse-2x8 · $0.0012/min

    $24.00

Monthly difference: $136.00

20,000 min × $0.0012

Monthly runtime cost for each Bowse size at the minutes above
SizevCPU / RAMPer minuteThis month
1x2bowse-1x21 vCPU · 2 GB$0.0005$10.00
2x4bowse-2x42 vCPU · 4 GB$0.0010$20.00
2x8bowse-2x82 vCPU · 8 GB$0.0012$24.00
4x16bowse-4x164 vCPU · 16 GB$0.0024$48.00
8x32bowse-8x328 vCPU · 32 GB$0.0048$96.00
16x64bowse-16x6416 vCPU · 64 GB$0.0096$192.00

Assumes the same runtime on both machines. Leaves out free allowances, storage, discounts, and tax. Bowse runtime includes machine preparation and cleanup.

How this estimate works

Monthly cost is runtime minutes × a per-minute rate. The comparison row uses the rate you type, so it is only as accurate as that number — use the rate for a machine with similar vCPU and RAM.

Bowse figures exclude free allowances, credits, storage, discounts, and tax, and they assume a job runs equally long on both machines. Real runtimes differ between providers and machine sizes, so the fairest test is a week of one job on each.

Why now

Agent-written pull requests multiply your CI minutes.

A coding agent that pushes ten revisions before lunch runs your full suite ten times. Per-minute price stops being a rounding error and becomes the line item someone asks about.

  1. 01 · Your workflow

    runs-on: bowse

    GitHub queues the job for the Bowse runner pool, exactly like any self-hosted label.

  2. 02 · Bowse dispatcher

    job event → machine

    Receives the job event, mints a one-job runner token, and boots a machine of the requested size.

  3. 03 · Fresh microVM

    Ubuntu 24.04 · x64

    Runs the job, streams logs back to GitHub, then is destroyed along with its disk.

Caching

Warm caches, cold machines.

Every runner starts empty, but dependencies don’t have to download again. The cache endpoint your actions already call is answered by Bowse, so actions/cache and the cache: option on setup actions keep working.

  • Docker layers ride the same store through BuildKit’s type=gha backend.
  • One repository can’t read another organization’s cache entries.
  • Pull requests read caches saved on the default branch, the same scoping rules GitHub uses.
.github/workflows/test.yml
steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: 22
      cache: npm          # no change needed
  - run: npm ci && npm test
  - uses: docker/setup-buildx-action@v3
  - uses: docker/build-push-action@v6
    with:
      cache-from: type=gha
      cache-to: type=gha,mode=max

Runner sizes

Pick a size per job, not per account.

Put the label on the job that needs it: lint on bowse-1x2, the Docker build on bowse-4x16. Runtime includes machine preparation and cleanup.

Bowse CI runner sizes with per-minute and per-hour rates
Size / labelvCPURAMRoot diskPer minutePer hour
1x2bowse-1x212 GB16 GB$0.0005$0.030
2x4bowse-2x424 GB32 GB$0.0010$0.060
2x8defaultbowse-2x828 GB48 GB$0.0012$0.072
4x16bowse-4x16416 GB96 GB$0.0024$0.144
8x32bowse-8x32832 GB160 GB$0.0048$0.288
16x64bowse-16x641664 GB256 GB$0.0096$0.576

Runtime

What a Bowse job runs on.

Supported

  • Ubuntu 24.04 on x64, booted new for each job and never reused
  • Six sizes, picked per job with a single label
  • Docker Engine, Buildx, Compose, and service containers
  • actions/cache and setup-* caching without workflow edits
  • Status, logs, and re-runs in the GitHub Actions UI you already use

Not supported

  • Windows or macOS runners, and Arm hosts
  • GitHub’s hosted runner image — Bowse ships its own image; install anything extra in a step
  • GitHub Enterprise Server — Bowse connects to github.com organizations and personal accounts

FAQ

Before you move a workflow.

Something missing? support@bowse.dev

How do I move my first job?

Create a workspace, install the Bowse GitHub App on the repositories you choose, and change runs-on on one job. The next push queues that job for Bowse; every other job keeps its current runner.

The CI quickstart walks through it step by step.

What do I change in my workflow?

One line per job. Swap runs-on: ubuntu-latest for runs-on: bowse for the default 2 vCPU / 8 GB machine, or a sized label like bowse-8x32. Steps, secrets, matrices, and reusable workflows stay as they are.

How do labels map to machines?

Each label is bowse-<vCPU>x<GB RAM>. Plain bowse is shorthand for bowse-2x8. There are six shapes, from 1×2 to 16×64; see the size table above.

What is on the runner image?

Ubuntu 24.04 x64 with the GitHub Actions runner, Git, Docker Engine with Buildx and Compose, and common language toolchains. The image reference lists what’s installed. It is not GitHub’s hosted image, so a job that assumes a specific preinstalled tool should install it.

Do I pay for time in GitHub’s queue?

No. The meter starts when the machine boots and stops when cleanup ends, so it covers boot and teardown as well as your steps. Time a job spends waiting in GitHub’s queue isn’t billed.

Is a runner ever reused between jobs?

No. A runner registers for exactly one job using a just-in-time token. When that job ends, the machine and its disk are deleted. Use the cache or artifacts to carry data forward.

Can Docker layers be cached between jobs?

Yes, through BuildKit’s GitHub Actions cache backend: cache-from: type=gha and cache-to: type=gha,mode=max with docker/build-push-action. Layers live in the same cache store as your dependency caches.

What about concurrency and timeouts?

Each workspace has a concurrency limit, shown in the console. Jobs above it wait in GitHub’s queue instead of failing. Set timeout-minutes the way you would on hosted runners; Bowse also enforces a safety ceiling per job.

What happens if a runner can’t start?

Bowse posts a setup check on the commit that says why — for example, the repository isn’t connected to a workspace, or the workspace reached its spend limit. Fix the cause and re-run the job from GitHub. Troubleshooting covers each check.

Move one job first. Watch the bill.

Pick your slowest or most frequent job, switch its label, and compare a week of runtime.