The BYOC Runner Lifecycle for GitHub Actions

A BYOC runner is one VM created in your cloud account when a GitHub Actions job is queued and terminated when it ends. Stages, ownership, and cost.

Overview

A BYOC runner is a single virtual machine that WarpBuild creates in your own cloud account when a GitHub Actions job is queued for its label, and terminates when that job finishes. The decision to create and destroy it belongs to the WarpBuild control plane; the machine, the disk, and every byte of build data belong to your account.

That one-job-one-VM rule is what the rest of this page unpacks: which component performs each step, what exists at each stage, and what survives termination. BYOC runs on AWS, GCP, and Azure. The stage names below use AWS vocabulary because the BYOC AWS architecture reference documents the resource graph in that form, and the sequence is the same on the other two clouds.

The full cycle, from a queued job to an empty account:

  1. A workflow job is queued with a runs-on label matching one of your custom runners. Custom runners are addressed by Runner ID, which is the runner name prefixed with warp-custom-.
  2. The control plane matches the label to that runner configuration: instance types in priority order, disk size and throughput, subnet placement, optional instance profile, spot or on-demand.
  3. The control plane assumes the role you granted at connection time and calls your cloud API to launch the instance, either fresh or by powering on a pooled standby VM.
  4. The VM boots inside the stack VPC with the stack security group attached. Local NVMe volumes, where the chosen instance type has them, are detected and mounted to the work directory at boot with no configuration.
  5. The runner agent registers as a just-in-time runner and holds an outbound long poll. GitHub assigns the job to it.
  6. The job runs. Cache reads and writes and runner system logs go to the object storage bucket in your stack.
  7. The job completes. The control plane terminates the instance, and the encrypted volume is destroyed with it.

Architecture

The split that makes the lifecycle predictable is that scheduling authority and execution live in different places. The control plane holds no build data and runs no build step. The data plane holds every instance and never decides on its own when to create one.

ResponsibilityOwned byRuns in
Watching the GitHub Actions job queue through the GitHub App connectionControl planeWarpBuild
Matching a runs-on label to a custom runner configurationControl planeWarpBuild
Deciding instance type, spot or on-demand, and fallback orderControl planeWarpBuild
Issuing the launch call against the cloud APIControl plane, through your granted roleAPI call into your account
The instance, its encrypted volume, and the job filesystemData planeYour account
Runner agent registration and the outbound poll to GitHubData planeYour account
Artifact cache, container layer cache, and runner logsData planeYour stack object storage bucket
Issuing the terminate call when the job completesControl plane, through your granted roleAPI call into your account
Compute, storage, and data transfer chargesData planeYour cloud invoice

Two consequences follow. First, a WarpBuild outage cannot leave build data stranded somewhere you cannot reach, because the data never left your account. Second, the launch authority is exactly the permission set in the connection role, so revoking that role stops new instances from being created without touching anything already running. The role and its policy blocks are covered on the AWS BYOC hub page, and the terms are defined in control plane and data plane.

Isolation is a property of the same lifecycle. Each runner runs in its own VM, created on demand and destroyed after each build, and each runner has its own encrypted storage volume with the same lifetime. VMs are ephemeral and never reused. WarpBuild does not access or store build secrets; secrets stay in your repository and reach only the runner environment. The details are in the security documentation.

What exists at each stage, and who pays for it:

StageWhat exists in your accountWhat is billable
Job queuedNothing newNothing
Launch call issuedA launch template and a pending instance requestNothing yet
Boot and registrationOne instance, one encrypted volume, one just-in-time runner registrationInstance and volume, at your cloud rates
Job runningThe same instance, plus cache and log objects being writtenInstance, volume, storage, and the WarpBuild per-minute fee
TerminationThe instance and volume are deletedCharges for both stop
After the jobCache objects and runner logs in the stack bucketStorage only

Configuration

Four runner settings change the shape of the cycle. Everything else in a custom runner configuration affects what the job can do rather than how the machine comes and goes.

Fallback instance types. A runner configuration can list several instance types in priority order. When capacity for the first type is unavailable in the region, the launch call falls through to the next one, which keeps queued jobs from stalling on a single shape. Pick types with similar performance so that a fallback does not change your build times, for example m7a.xlarge and m7i.xlarge.

Spot or on-demand. Spot instances suit short, interruptible jobs. A spot instance can be reclaimed at any time, which ends that job's run, so the trade is capacity price against a retry. The instance type is chosen from availability and price across the types you listed.

Standby disks. A standby disk is a VM of the runner instance type that is booted once so that networking and machine configuration complete, then shut down without being terminated. When a job arrives, one is powered on and the job starts in about 15 seconds instead of building a machine from scratch. If the pool is empty, a fresh VM is created and the job starts at the normal provisioning time. Spot instances are not supported for standby disks. Sizing guidance is on the standby disks page.

Disk configuration. Size, throughput, and IOPS are set per runner. The documented minimum is 100 GB at 125 MBps and 3000 IOPS, with 150 GB at 400 MBps and 4000 IOPS recommended. The volume is created at boot and destroyed at termination, so this is a per-job cost rather than a standing one.

None of this reaches the workflow file. A job moves onto a BYOC runner by label alone:

.github/workflows/ci.yml
name: ci

on:
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: warp-ubuntu-latest-x64-2x
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint

  integration:
    runs-on: warp-custom-linux-x64-8x
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:integration

The lint job runs on WarpBuild-hosted capacity and the integration job runs in your account. Both jobs get a fresh VM and both leave nothing behind, so the mixed fleet is a placement choice rather than a behavior change.

Operations

The question that decides most reviews is what remains after a job ends.

ResourceState after the job
Runner instanceTerminated
Encrypted volume attached to itDestroyed with the instance
Workspace, checked-out source, job environmentDestroyed with the volume
Just-in-time runner registrationConsumed by that one job
Launch template created for the jobDeleted
Artifact cache objects in the stack bucketKept until your lifecycle policy expires them, 7 days recommended
Runner system logs in the stack bucketKept under the same policy
Standby pool VMsShut down rather than terminated, and refilled by the control plane
Stack VPC, subnets, security group, and bucketUnchanged, outliving every job
Custom runner configuration and cloud connectionUnchanged

Both bucket prefixes grow without bound if you leave them alone, so the retention rule is the one piece of housekeeping the lifecycle asks of you. Every instance carries the warpbuild-runner-id, warpbuild-runner-labels, and warpbuild-stack-name tags, which is how you trace a line in a cost report back to the runner class that produced it, or confirm that nothing tagged managed-by: warpbuild is still running after a quiet weekend.

Tearing down runs in the reverse order of setup, and the dashboard enforces it: a stack cannot be deleted while custom runner configurations depend on it, and a cloud connection cannot be deleted while stacks depend on it. A full list of the resources involved is on what WarpBuild creates in your cloud account.

Billing follows the same boundary. WarpBuild charges $0.002 per BYOC runner minute for Linux runners and $0.002 per minute for Windows runners, measured against job time, and your cloud account bills the instance, the volume, and data transfer at your own rates. The fee does not scale with instance shape:

ShapeWarpBuild-hosted rate per minuteWarpBuild BYOC fee per minute
Linux 2 vCPU, 8 GB$0.004$0.002
Linux 8 vCPU, 32 GB$0.016$0.002
Linux 32 vCPU, 128 GB$0.064$0.002
Windows 8 vCPU, 32 GB$0.032$0.002

Rates come from the pricing page, checked on 2026-08-13. The BYOC fee above applies to the Linux and Windows runners you can place in your own account.

FAQ

What creates and destroys a BYOC runner instance?

The WarpBuild control plane does both, through the IAM role or service account you granted when you connected the cloud account. It watches the GitHub Actions job queue, matches the runs-on label to one of your custom runners, calls your cloud API to launch an instance in your VPC, and calls it again to terminate that instance when the job completes. The instance itself only ever exists in your account.

Does anything from my build stay behind after the runner is destroyed?

The instance and its encrypted volume are destroyed with the job, and the workspace, the checked-out source, and the job environment go with them. What persists is what you asked to persist: artifact cache objects and runner system logs in the stack object storage bucket, which the documentation recommends expiring on a 7 day lifecycle policy. WarpBuild does not access or store build secrets.

Is one instance reused for a second job?

No. Each GitHub Actions job runs on its own freshly provisioned VM that is terminated when the job finishes, so no state carries from one job to the next. A standby disk pool changes the boot path rather than that rule: a pooled VM is pre-initialized and shut down, powered on for one job, and terminated at the end of it.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.