← Providers

Run on Fly Machines.

Available

The shortest production-piloted path. A small controller app starts one Machine per GitHub job in a separate, secret-free worker app.

01 · Scaffold

Install and authenticate flyctl and gh. Start in a trusted private repository.

Terminal
npx runneryard init \
  --provider fly \
  --github https://github.com/acme/widgets \
  --max-runners 2

Ready whenThe generated diff contains one controller config and one isolated canary workflow.

02 · Isolate

Control and worker apps must be separate because every Machine in a Fly app inherits that app's secrets. Workers stay on a separate private network with zero idle capacity.

Terminal
fly apps create acme-ci-runners --network runneryard-workers
fly apps create acme-ci-controller --network runneryard-control
fly volumes create runneryard_state \
  --app acme-ci-controller --region cdg --size 1 --yes

Ready whenBoth apps exist, the controller volume is attached, and the worker app has no secrets.

03 · Connect GitHub

The CLI opens GitHub to create your private, owner-controlled App. Its one-time key goes straight to the controller app over stdin; you never copy or see it.

Terminal
npx runneryard auth github create \
  --github https://github.com/acme/widgets \
  --controller-app acme-ci-controller

Ready whenThe CLI reports a verified installation for the exact repository.

04 · Add compute access

Create a deploy token scoped only to the worker app and store it only on the controller. This is a Fly credential, not a GitHub token. Keep reviewed policy such as MAX_RUNNERS in TOML, never in secrets or pull-request-controlled input.

Terminal
fly tokens create deploy --app acme-ci-runners
fly secrets set --app acme-ci-controller \
  FLY_API_TOKEN='<worker-app deploy token>'

Ready whenThe worker app has zero secrets; the controller has GitHub App credentials and one worker-app token.

05 · Budget and deploy

Initialize the fail-closed budget ledger exactly once, then run doctor before the controller starts. The generated worker uses two sustained performance CPUs and 8 GB of RAM; shared CPUs remain an explicit option for short, bursty jobs.

Terminal
fly machine run ghcr.io/gwendall/runneryard:0.5.6 \
  --entrypoint "/usr/local/bin/controller-entrypoint" \
  --env RUNNER_BUDGET_FILE=/var/lib/runneryard/budget.json \
  --app acme-ci-controller --region cdg \
  --volume runneryard_state:/var/lib/runneryard --rm \
  -- budget init --file /var/lib/runneryard/budget.json

npx runneryard doctor --provider fly \
  --controller-app acme-ci-controller \
  --worker-app acme-ci-runners

fly deploy --app acme-ci-controller \
  --config .runneryard/fly.controller.toml --ha=false

Each shared vCPU has a 6.25% sustained baseline plus burst capacity. Performance workers cost more per second, but avoid the post-burst slowdown that can turn tests into timeouts and reruns. Measure cost per successful job and keep the operator-owned ceiling low until a representative canary proves the workload. Older scaffolds that omit RUNNER_CPU_KIND remain shared on upgrade; set both shape variables to opt in. Review worker sizing.

Ready whenDoctor passes and the controller status is healthy with zero desired workers.

06 · Canary and route

Trigger the generated workflow. A valid canary proves the pinned Node toolcache, Fly-compatible fuse-overlayfs, and a real BuildKit build and container run. It also has a complete controller lifecycle and leaves no worker behind. Only then route a low-risk job.

Terminal
gh workflow run runneryard-canary.yml --repo acme/widgets

fly ssh console --app acme-ci-controller \
  --command '/usr/local/bin/runneryard status'
fly machines list --app acme-ci-runners

npx runneryard route enable \
  --github https://github.com/acme/widgets \
  --label acme-linux --confirm-canary

Keep ubuntu-latest available as the explicit emergency route. Size capacity and budget, then read the complete Fly guide for rotation and recovery details.

Outboard

Disable the route, finish active jobs, stop the controller, and verify zero workers. Revoke the worker-app token and delete the two apps and volume. Revoke and delete a dedicated GitHub App. For a shared BYO App, remove this controller's local credential; change installation scope only after confirming no other consumer needs it.

Terminal
npx runneryard route disable --github https://github.com/acme/widgets