Service Accounts for BYOC on GCP

BYOC on GCP uses two service accounts: one the cloud connection creates in your project, one you attach to runner VMs. Roles, setup order, and review points.

A GCP BYOC deployment uses two service accounts with different owners, and telling them apart answers most of the permission questions a review raises. The cloud connection creates one service account in your project that WarpBuild impersonates to create and terminate runner instances, and you create a second one that gets attached to the runner VMs so job steps reach Artifact Registry or Cloud Storage without a key in a GitHub secret.

This page covers what each identity is allowed to do, the enablement order that has to land before the connect flow succeeds, how to narrow the roles on the instance account, and the checks a platform team runs afterwards. The click path with screenshots is in the service account setup guide, and the account-level view of the deployment is on BYOC runners on GCP for GitHub Actions.

Overview

BYOC runs on AWS, GCP, and Azure. On GCP, runners are Compute Engine instances in your own project, so every identity involved lives in that project and is visible in its IAM policy.

Connection service accountInstance service account
Created byThe cloud connect flow, in your projectYou, with gcloud or the console
Used byThe WarpBuild control plane, through impersonationThe runner VM, and therefore your workflow steps
PurposeBuild stacks, create and terminate runner instancesAuthenticate job steps to Google APIs
Configured whereOnce, when the project is connectedPer runner, in the Service Account field on the runner edit page
How manyOne per cloud connectionAs many as you want, one per workload shape
What a compromised build step reaches through itNothing; the control plane holds itExactly the roles you granted, and nothing else
Removed byDeleting the cloud connectionDeleting the service account or clearing the field

The split is what lets a deploy runner and a pull request runner hold different Google authority under one stack. Both run on the same VPC, the same bucket, and the same connection; only the attached identity differs.

Architecture

The connection side is an impersonation chain. WarpBuild's own service account calls the IAM Service Account Credentials API to mint short lived tokens for the service account that lives in your project, and those tokens are what sign the Compute Engine calls that create and terminate instances. No long-lived key is issued, which is why the credentials API has to be enabled before the connect flow starts, and why deleting the connection ends the access path.

The instance side never touches WarpBuild. The service account you select is attached to the VM at boot, the metadata server serves tokens for it, and gcloud, docker login against Artifact Registry, and the Google client libraries pick those tokens up on their own. Nothing has to be stored in a repository secret, and nothing has to be rotated on a schedule.

One binding joins the two. WarpBuild's runner-creating service account, shown as CREATOR_SA on the BYOC page in the dashboard, needs roles/iam.serviceAccountUser on the instance service account before it can pass that identity to a VM. Grant it with gcloud iam service-accounts add-iam-policy-binding, which writes the binding onto the service account resource itself. That distinction matters in review: the grant authorizes passing exactly one named identity, and every other service account in the project stays out of reach.

Configuration

Three prerequisites decide whether the connect flow succeeds, and each failure looks different in the console.

StepWhat it unlocksWhat fails without it
Link a billing account to the projectGoogle bills compute, disk, storage, and network to your accountThe project cannot hold the resources a stack creates
Enable the Identity and Access Management (IAM) APICreation of the connection service account in your projectConnect fails before any resource exists
Enable the IAM Service Account Credentials APIShort lived tokens minted by impersonating that accountImpersonation fails, so no runner instance is ever created
Enable Compute Engine, Cloud Storage, Cloud Deployment Manager V2, and Cloud Resource ManagerStack deployment, the cache bucket, and runner lifecycleThe stack deployment stops at the missing API
Grant the setup user Security Admin, Storage Admin, Deployment Manager Editor, Compute AdminCreating the integration, bucket, deployment, and compute resourcesThe flow stops at whichever resource the missing role covers

Enable every API against the project you plan to connect. The project dropdown in the GCP console is the usual cause of a failed connect, since the API gets enabled on a neighboring project. The full API table and the console links are in the GCP configuration guide.

Those four setup roles belong to a human operator. Runner instances never carry them, and they are exercised when the connection and the stack are created, and again when a pending connection or stack update is applied, so teams that keep standing project admin narrow usually hold them on a setup principal and re-grant when an update is waiting.

Creating the instance service account is three commands.

gcloud config set project "${PROJECT_ID}"

gcloud iam service-accounts create ci-runner \
  --display-name="GitHub Actions runner identity"

export SA_EMAIL="ci-runner@${PROJECT_ID}.iam.gserviceaccount.com"

# CREATOR_SA is the runner-creating service account shown on the BYOC page
gcloud iam service-accounts add-iam-policy-binding "${SA_EMAIL}" \
  --member="serviceAccount:${CREATOR_SA}" \
  --role="roles/iam.serviceAccountUser"

Then grant the account whatever the jobs actually do. The setup guide uses admin roles as an illustration; production fleets should start narrower and widen only when a step fails.

What the job doesStart withWider role to avoid by default
Pull images from Artifact Registryroles/artifactregistry.readerroles/artifactregistry.admin
Push images to Artifact Registryroles/artifactregistry.writerroles/artifactregistry.admin
Read build inputs from one bucketroles/storage.objectViewer, granted on that bucketroles/storage.admin on the project
Write test artifacts to one bucketroles/storage.objectUser, granted on that bucketroles/storage.admin on the project

Bucket-scoped grants use gcloud storage buckets add-iam-policy-binding rather than gcloud projects add-iam-policy-binding, which keeps the identity out of every other bucket in the project. Google documents the full set in the predefined roles reference. Finish by selecting the account in the Service Account field on the runner configuration. The field is editable on an existing runner, so a fleet that was created before the identity existed is updated in place rather than rebuilt.

Operations

Verify the attachment on a live instance rather than trusting the form. Open Compute Engine in the GCP console, pick a running runner instance, and read Service account under API and identity management; it should match the account you created. The same check from a workflow step is one curl against the metadata server:

.github/workflows/verify-identity.yml
jobs:
  identity:
    runs-on: warp-custom-gcp-use-linux-8x
    steps:
      - run: |
          curl -sH 'Metadata-Flavor: Google' \
            'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email'

Three review habits keep the setup honest over time. Diff the project IAM policy after every stack or connection update, since applying an update can change what the connection holds. Alert on changes to the roles/iam.serviceAccountUser binding on each instance service account, because that binding is what decides which identities can be attached to a VM. And split identities by workload before splitting them by team: a pull request runner that only reads images and a deploy runner that writes them are the two accounts most fleets end up with.

Costs sit on a separate line from permissions and do not change when you add another identity. WarpBuild charges $0.002 per minute per Linux BYOC runner, and Google bills your own project for the Compute Engine instances, the disks, the bucket, and the network at whatever rates your account already pays (WarpBuild pricing, checked 2026-08-13).

On GCP BYOC the Linux sets are the ones this service account is attached to; macOS and Windows jobs stay on hosted warp- labels in the same workflow file. For the compliance question that usually follows the IAM one: SOC 2 Type 2, with trust.warpbuild.com as the linked evidence.

FAQ

How many service accounts does BYOC on GCP use?

Two, with different owners. The cloud connection creates one in your project and WarpBuild impersonates it to create and terminate runner instances. You create the second one and select it on the runner configuration, and it is what your workflow steps authenticate as.

Do I need to put a service account key in a GitHub secret?

No. The instance service account is attached to the Compute Engine VM, so job steps get tokens from the instance metadata server and gcloud picks them up without a JSON key file anywhere in the repository.

Which roles does the person running the connect flow need?

Security Admin, Storage Admin, Deployment Manager Editor, and Compute Admin, granted on the project being connected. They cover creating the connection service account, the Cloud Storage bucket, the Deployment Manager deployment, and the compute resources, and they apply to the setup user rather than to the runners.

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.