Skip to content

Docs / Self-hosted CI

Self-hosted CI runners

Replace Blacksmith or Actions-hosted runners with your own VPS. Change one line in your workflow — runs-on: developerz-2vcpu — and jobs run on your hardware.

Self-hosted CI runners

developerz.ai turns your VMs into JIT (just-in-time) GitHub Actions runners. Change one line in any workflow file:

jobs:
  test:
    runs-on: developerz-2vcpu  # was: ubuntu-latest

That's it. The job runs on your own hardware, logs are stored for 72 hours, and your billing is whatever your VPS costs — not GitHub's metered per-minute runner fees. See the GitHub billing note below for the one private-repo exception, with current date-stamped terms.

Prerequisites

  • A box enrolled in your fleet (see quickstart).
  • The GitHub App installed on the repo.
  • The box has the ci capability (set automatically on enrollment; verify with box fleet_status in the MCP).

Runner labels

Labels map to hardware tiers. Use any label your box satisfies:

Label vCPU RAM Notes
developerz-2vcpu 2 ≥ 2 GB Standard workloads
developerz-4vcpu 4 ≥ 4 GB Build-heavy / parallel tests
developerz-8vcpu 8 ≥ 8 GB Large monorepos
developerz-gpu any ≥ 8 GB GPU capability required
developerz-arm64 any any ARM64 architecture

A job whose label no box satisfies stays queued until a matching box is available or the workflow timeout fires. The assigner logs label-match failures in the audit trail.

Security model

Scoped installation token

Each CI job receives a short-lived GitHub installation token scoped to the repo the job belongs to. The token is:

  • Minted just-in-time by the API when the job is assigned.
  • Valid for 15 minutes (standard GitHub installation token TTL).
  • Revoked on job completion (best-effort; GitHub expires it anyway).

The box never holds the GitHub App private key — only the API does.

Fork PR policy

Jobs triggered by fork pull requests never run on fleet boxes. A fork PR can carry an attacker-controlled workflow file, so the assigner refuses these jobs before they are ever placed on your hardware — there is no policy key to opt in. Run fork-PR checks on GitHub-hosted runners instead.

The related repo-wide fence is ci.public_repos in .maintainer.yml (default false): even for same-repo PRs, CI on a public repo stays off until a maintainer opts in, because a public repo lets anyone open a PR whose workflow runs on your hardware.

# .maintainer.yml
ci:
  enabled: true          # default: true — kill switch for the whole CI lane
  public_repos: false    # default: false — must be true to run CI on a public repo

No App credentials on the box

The GitHub App private key is never placed on a runner. Boxes talk to the API over an authenticated control channel; the API holds all secrets and hands each job only its scoped, short-lived token.

Ephemeral containers

Each CI job runs inside an isolated container (Docker or Podman). The container is created fresh for the job and destroyed when it completes. No state persists between jobs on the same box.

Log retention

Logs are streamed to object storage (your BYO S3 or the platform bucket) in real time, compressed with zstd, and retained for 72 hours. After 72 hours the reaper deletes them automatically.

To download a log:

# MCP verb
ci log_url --ci-job-id <uuid>
# → { url: "https://…presigned…", expires_in_seconds: 3600 }

Or from the dashboard under Fleet → CI.

Idle AI devs as CI runners

Boxes that run AI coding tasks (the worker runner mode) also serve CI jobs when idle. The assigner checks box presence and capacity before routing. This means your AI dev fleet doubles as CI infrastructure with no additional cost — the hardware is already running.

GitHub billing note

Private repos only. GitHub charges $0.002/minute for self-hosted runner minutes on private repositories (2026-03 terms — verify current terms before pricing). Public / OSS repositories are exempt. The developerz.ai platform fee does not include this charge; it appears on your GitHub bill. Check github.com/pricing for the latest terms.

Checking CI run status

# MCP — list recent runs
ci runs --repo owner/repo

# Filter by status
ci runs --status failed --repo owner/repo

# Get log URL
ci log_url --ci-job-id <uuid>

Full verb reference: MCP verb reference → ci.