Why agents fit ephemeral runners
- They run untrusted output. An agent executes code it just wrote. On Runloft that happens in a VM that is deleted when the job ends, so nothing it installs or leaves on disk reaches the next job.
- They come in bursts. A review bot on every comment, or one agent per issue, means dozens of jobs at once. The default cap is 200 concurrent jobs per account, with no plan to upgrade.
- They are long and spiky. Agent sessions run for minutes and spend much of that waiting on a model API. Per-second billing at $0.003 a minute keeps that idle time cheap; the CPU size only matters for the builds and tests the agent runs.
For most agent jobs, 4 vCPU is the right starting point: enough to install dependencies and run a test suite quickly, at half the per-minute cost of 8 vCPU.
Claude Code
Anthropic's claude-code-action responds to @claude mentions in issues and pull requests, or runs on a schedule with a prompt. Set runs-on on the job that uses it:
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: runloft-ubuntu-24.04-4cpu
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}The API key stays a GitHub secret. GitHub passes it to the runner for that job only; it never goes through Runloft.
OpenAI Codex
The Codex GitHub Action runs Codex in a job, for reviews or for making changes. The same change applies:
name: Codex review
on:
pull_request:
types: [opened, synchronize]
jobs:
codex:
runs-on: runloft-ubuntu-24.04-4cpu
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: Review this pull request and point out bugs.GitHub Copilot coding agent
The Copilot coding agent runs its sessions in GitHub Actions and reads its environment from a special workflow, .github/workflows/copilot-setup-steps.yml, with a single job named copilot-setup-steps. The runs-on of that job decides where every Copilot session runs, so setting it to a Runloft label moves the agent itself:
# .github/workflows/copilot-setup-steps.yml
name: Copilot Setup Steps
on: workflow_dispatch
jobs:
# The job must be named copilot-setup-steps.
copilot-setup-steps:
runs-on: runloft-ubuntu-24.04-4cpu
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci- The file has to be on the default branch to take effect.
- Copilot requires Ubuntu x64 runners, so use one of the
runloft-ubuntu-24.04sizes, not arm64. - GitHub's agent firewall is designed around GitHub-hosted runners. If sessions fail to reach the network on self-hosted runners, review the firewall setting in the repository's Copilot coding agent settings.
Review bots on many pull requests
Comment-triggered workflows can fire far more often than CI. Two settings keep them under control:
- A
concurrencygroup per pull request, so a new comment cancels the previous run instead of stacking up behind it. - A monthly spend limit on the account, so a loop cannot run up a bill.
concurrency:
group: review-${{ github.event.pull_request.number || github.event.issue.number }}
cancel-in-progress: true