GitHub Actions, on a machine per job
Change one line, runs-on: pilots, and every job gets a fresh machine of
its own, made when the job is queued and deleted when it ends. It costs
70–74% less than GitHub’s own runners at every
size, billed by the second.
Ask your agent
Pilots is driven by your agent, and runners are no different: say what you want, and the agent enables the repository, builds the image and changes the workflow.
-
Say it
In Claude Code, Cursor, Codex or any agent with Pilots added.
Run the GitHub Actions jobs of this repository on Pilots runners. -
The agent does the rest
It enables the repository, starts the runner image, changes
runs-onin the workflow and pushes. The one screen that stays yours is GitHub’s own page for installing the pilots app, which the agent links you to the first time. -
Or from your terminal
The same two commands the agent runs, then one line in a workflow.
curl -fsSL https://pilots.run/install.sh | shpilot runner enable acme/shop
jobs:
test:
runs-on: pilots # was: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
A machine that was already running
A runner is not booted. It is a copy of your team’s runner image, which was photographed with the runner already loaded, so the job starts on a machine that is up from its first moment.
Measured on the production fleet. Hover a number to see how it was taken.
Every job gets a new machine, forked from your ready image, so no job waits for another to free a shared runner and every tool below is already installed when the job starts.
What a minute costs, beside the others
A pilots runner is an ordinary machine, so it costs what any machine costs: processor and memory by the second while it runs. These are those prices multiplied out to a minute, at the sizes the others sell.
| Size | pilots | GitHub-hosted | Blacksmith | Depot |
|---|---|---|---|---|
| 2 vCPU, 8 GBpilots | $0.00156 | $0.006 | $0.004 | $0.006 |
| 4 vCPU, 16 GBpilots-4cpu-16gb | $0.00312 | $0.012 | $0.008 | $0.012 |
| 8 vCPU, 32 GBpilots-8cpu-32gb | $0.00624 | $0.022 | $0.016 | $0.024 |
| 16 vCPU, 64 GBpilots-16cpu-64gb | $0.01248 | $0.042 | $0.032 | $0.048 |
| Free each month | 600 minutes | 2,000 or 3,000 minutes | 3,000 minutes | 2,000 minutes |
pilots costs 70–74% less than GitHub’s own runners at every size, and less than Blacksmith and Depot too. Depot’s minutes come inside a monthly plan.
Every month starts with about 600 minutes of the default runner free, from the same allowance as your other machines.
pilots charges by the second. A job that takes ninety seconds on the default runner is $0.00234, and a ten-minute one is $0.0156. A runner also writes to its own disk while it exists, which is charged at the disk price for those minutes and comes to a small fraction of a cent.
Rates read from each product’s own pricing page. Hover a figure to see where and when. How runners are billed.
What is on the machine
- System
- Ubuntu 24.04, with git, git-lfs, the usual build tools and the GitHub CLI
- Languages
- Node 24, Bun, Go and Python 3 with pip and venv
- Browsers
- The Playwright browsers, Chromium, Firefox and WebKit, already downloaded
- Containers
- Docker with compose and buildx
- The runner
- The GitHub Actions runner, running as a user with passwordless sudo
Sizes
pilots is 2 vCPU, 8 GiB, the shape GitHub gives a
private repository, so a workflow that fits there fits here. Name any other size the
same way as pilots-4cpu-16gb, up to 32 vCPU, 128 GB.
The image is a template in your team, rebuilt when you ask for it with
pilot runner image --fresh.
Each job is alone on its machine
-
A virtual machine per job
Each job runs in a microVM with its own kernel, alone. It shares no file system, no process list and no network with any other job, yours or anybody else’s.
-
Made for the job, deleted after it
The machine is made from your team’s image when the job is queued and deleted when it ends. The next job never sees what the last one left behind.
-
A registration good for one job
GitHub hands out a just-in-time configuration bound to one runner name, usable once. It reaches the machine as the environment of one process, is never written to its disk, and is never logged. No long-lived registration token exists anywhere.
-
Only the labels the job asked for
A runner registers with exactly its job’s labels, so it cannot take a job meant for self-hosted runners of your own.
-
Where it stops
A runner is a machine in your team. Anybody on the team can open a shell in one while it runs, and a job’s secrets are in it while the job runs. That is why only a repository administrator can enable a repository.
Good to know
Most CI runs here unchanged. Three facts before you move a whole repository.
- x86-64 Linux. Runners are Ubuntu 24.04 on x86-64, the platform most GitHub Actions workflows already target.
- Docker, not nested VMs. Containers, compose and buildx work as they do on GitHub’s runners. A job that boots its own virtual machine, such as an Android emulator, keeps a bare-metal runner.
- You choose the concurrency. 10 jobs run at once by default, and you can raise it, so a wide matrix is your choice and never a surprise on the bill.
A runner machine is deleted when its job ends, and none is kept past 6 h 15 min.
Questions people ask before moving their CI
- Does it work for private repositories?
- Yes. A runner is registered for the repository you enable, and the job runs the same way in a private repository as in a public one.
- Do jobs on pilots use my GitHub Actions minutes?
- No. GitHub does not charge minutes for a job on a self-hosted runner, which is what a pilots runner is to GitHub. You pay pilots for the seconds the machine runs.
- Can I keep some jobs on GitHub-hosted runners?
- Yes. runs-on is set per job, so a workflow can send one job to pilots and leave the others where they are.
- What happens when a job asks for something pilots does not have?
- The commit gets a status named pilots runners that says what was wrong and what to write instead. Without it the job would wait in GitHub’s queue for a day with nothing saying why.
- Is a runner kept warm between jobs?
- No. A runner exists from the moment its job is queued until the job ends, and it is then deleted. Nothing runs, and nothing is charged, while no job is waiting.
- Who can send jobs to my team’s machines?
- Only a repository somebody enabled, and only a person who administers that repository on GitHub can enable it. The pilots GitHub App is how that is checked.
- Can a job use Docker?
- Yes. Docker runs inside the runner, with compose and buildx, so docker build, docker compose and service containers such as a Postgres for the tests all work.
- Can I install more tools in a job?
- Yes. The job runs as a user with passwordless sudo, so a step can install anything with apt or any other installer, as on a GitHub-hosted runner.
- Does it cache dependencies between jobs?
- Only through GitHub’s own actions/cache, which works as it does anywhere. pilots has no cache storage of its own for runners, and every job starts from the same clean image.
Your app, its sandboxes and its CI on one platform, driven from one command line and on one bill. A runner is the same kind of machine your app runs on.