Can I Attach an IAM Role to a BYOC Runner?

Yes. AWS BYOC runners take an IAM instance profile per runner class, so GitHub Actions jobs receive scoped AWS credentials without an access key in a secret.

Answer

Yes. Every custom runner in an AWS BYOC stack takes an Instance Profile ARN, so the EC2 instance WarpBuild launches for a job starts with an IAM role already attached and the AWS SDK inside your build steps reads credentials from instance metadata (instance profile setup). This is the documented way to give GitHub Actions jobs cloud access without an access key sitting in a repository secret, and the field lives on the runner configuration rather than on the stack, so permissions are scoped per workload type (AWS BYOC configuration).

Two AWS objects have to exist before the field accepts a value, and both are described in the AWS documentation the WarpBuild guide points at: an IAM role that trusts ec2.amazonaws.com, and an instance profile holding that role (AWS IAM roles for EC2, AWS IAM instance profiles).

Detail

The one grant people miss

WarpBuild launches instances in your account through the IAM role created when you connected the cloud, named warpbuild-<UUID> and visible on the BYOC connections page. AWS refuses to let any principal attach a role it has no permission to pass, so that connection role needs iam:PassRole on your runner role, scoped by a condition to EC2:

aws iam put-role-policy \
    --role-name <warpbuild-role-name> \
    --policy-name PassRolePolicy \
    --policy-document '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "iam:PassRole",
                "Resource": "arn:aws:iam::<account-id>:role/ci-runner-role",
                "Condition": {
                    "StringEquals": {
                        "iam:PassedToService": "ec2.amazonaws.com"
                    }
                }
            }
        ]
    }'

Confirm the grant landed before you queue a job, using the simulator rather than a failed workflow run:

aws iam simulate-principal-policy \
    --policy-source-arn <warpbuild-role-name> \
    --action-names iam:PassRole \
    --resource-arns arn:aws:iam::<account-id>:role/ci-runner-role \
    --context-entries ContextKeyName=iam:PassedToService,ContextKeyType=string,ContextKeyValues=ec2.amazonaws.com

The full sequence, including the role creation and the policy attachment, is in the security hardening guide.

One profile per runner class

The Instance Profile ARN is per custom runner, which makes one profile per runner class the pattern to reach for. A shared role that carries every permission any job might need turns the weakest workflow in the repository into the blast radius for the strongest. Splitting by class keeps each policy small enough to read.

A worked split for one stack, with the policy shapes drawn from the security hardening guide:

Runner ID in runs-onWhat jobs on it doInstance profilePolicy scope
warp-custom-test-use1-8xUnit tests, lint, type checksNone attachedNo AWS authority at all
warp-custom-build-use1-16xBuild and push container imagesci-ecr-push-profileecr:GetAuthorizationToken on *, plus layer and image actions on arn:aws:ecr:<region>:<account-id>:repository/<your-repo>
warp-custom-deploy-use1-4xUpload release artifactsci-artifacts-profiles3:PutObject, s3:GetObject, s3:ListBucket on one bucket and its objects

The workflow side carries no credential wiring at all. The label selects the machine, and the machine arrives with its authority:

.github/workflows/release.yml
name: release
on:
  push:
    branches: [main]

jobs:
  unit-tests:
    runs-on: warp-custom-test-use1-8x
    steps:
      - uses: actions/checkout@v4
      - run: make test

  image:
    needs: unit-tests
    runs-on: warp-custom-build-use1-16x
    steps:
      - uses: actions/checkout@v4
      - run: aws ecr get-login-password | docker login --username AWS --password-stdin "$ECR_REGISTRY"
      - run: docker build -t "$ECR_REGISTRY/app:$GITHUB_SHA" .
      - run: docker push "$ECR_REGISTRY/app:$GITHUB_SHA"

Each job runs on a freshly provisioned instance that is terminated when the job finishes, so the credentials the profile supplies never outlive the job that used them (security hardening guide).

Instance profile or an OIDC exchange

The alternative is an OpenID Connect exchange from inside the workflow: GitHub issues a short-lived token for the job, and your role trust policy decides which repository, branch, or environment may assume the role (GitHub OIDC in AWS). The exchange itself is normally handled by the public aws-actions/configure-aws-credentials action.

DimensionInstance profileOIDC exchange in the workflow
Where the identity is decidedOn the runner class, in WarpBuildPer job, in the role trust policy
Workflow changesNonepermissions: id-token: write plus a credentials step
GranularityEvery job using that runs-on labelRepository, branch, environment, or job claims
Works on hosted runnersNo, EC2 BYOC runners onlyYes
Failure modeEvery job on the class inherits the roleA trust policy condition typo blocks the job

Take the instance profile for baseline access that every job on a class needs, such as pulling base images or reading a shared bucket. Take the exchange when authority has to differ by branch or environment, or when the same job sometimes lands on hosted runners. They stack cleanly, because the credentials the action writes into the job environment take precedence over the instance metadata credentials for the remaining steps under the AWS SDK provider chain (AWS SDK credential resolution). A common shape is a narrow profile on the class and an exchange for the deploy step alone.

Two settings finish the hardening. Require IMDSv2 per runner, under Runner Specs in the dashboard, which closes the request-forgery path to instance credentials (security hardening guide). Keep the runner stack in a dedicated VPC or a dedicated AWS account so the profile's reach ends at the workloads it was written for.

What the profile costs

Nothing on the WarpBuild side. BYOC runners bill at $0.002 per runner minute for Linux runners and $0.002 per runner minute for Windows runners, rates checked on 2026-08-13, and AWS bills your own account for the EC2 instances, disks, and transfer those jobs consume.

The instance profile applies to the EC2-backed BYOC ones, since macOS runners stay on WarpBuild-hosted capacity where the OIDC exchange is the route to AWS. The architecture around all of this is on the AWS BYOC page.

What does WarpBuild need before it can attach my IAM role?

One grant. The WarpBuild connection role in your account, named warpbuild-<UUID>, needs iam:PassRole on the role behind your instance profile, with a condition restricting iam:PassedToService to ec2.amazonaws.com. Without that grant the launch fails, because AWS blocks any principal from passing a role it has no permission to pass. The commands are in the instance profile setup guide, and the walkthrough is on instance profiles for AWS BYOC runners.

Can two runners in the same stack carry different AWS permissions?

Yes. The Instance Profile ARN is a field on the custom runner configuration rather than on the stack, so a deploy runner and a unit-test runner sharing one VPC and one bucket can hold different AWS authority. That is the least-privilege pattern to use: one profile per runner class, sized to what jobs on that class actually touch. The term itself is defined on IAM instance profile.

Should I use an instance profile or an OIDC exchange from the workflow?

Use an instance profile for the baseline access every job on a runner class needs, such as pulling base images from ECR. Use an OIDC exchange when the authority has to vary per repository, branch, or environment, or when the same job also runs on hosted runners. They compose, and the exchanged credentials win for the rest of the job once the workflow sets them in the environment. The exchange is walked through on authenticating to AWS from GitHub Actions with OIDC.

Do GCP and Azure BYOC runners support the same thing?

GCP does, through a custom service account attached to the GCE instance (attach a service account). Azure custom IAM roles are marked as not yet available in the feature matrix, checked on 2026-08-13. BYOC runs on AWS, GCP, and Azure, so check that matrix row before assuming a per-runner identity exists on your cloud.

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.