S3 and ECR Prerequisites for BYOC on AWS

What the S3 bucket and ECR endpoints in an AWS BYOC stack are used for, the permission set behind each one, and why Region placement decides your transfer bill.

An AWS BYOC stack needs one S3 bucket in the same Region as the stack, which holds artifact cache objects and runner system logs, and it needs a network path to ECR, which the stack provisions as VPC endpoints rather than as a repository you name up front. The authority to actually pull an image comes from a separate place: the instance profile you attach to a runner set, which the WarpBuild connection role never touches.

This page covers what each object stores, the permission set behind it, the failure you see when a piece is missing, and why the Region you pick at stack creation decides the transfer line on your AWS bill. The full prerequisite checklist lives in the AWS BYOC configuration guide, and the account-level view of the setup is on BYOC runners on AWS for GitHub Actions.

Overview

BYOC runs on AWS, GCP, and Azure. On AWS, storage and registry access break into four objects, and only two of them are things you decide.

PrerequisiteWhat it holds or carriesWho creates itChangeable after stack creation
Stack S3 bucketArtifact cache objects and runner system logsThe CloudFormation template in create mode, or you supply an existing bucket in import modeNo. The stack name, the bucket, and the Region are fixed at creation
S3 gateway VPC endpointRunner traffic to that bucket, kept on the AWS networkThe stackYes, endpoint management is part of the stack
ECR API and ECR Docker interface endpointsImage pulls from runners in public and private subnetsThe stack, automaticallyYes
Runner instance profileThe authority a job step holds to log in to ECR and read or write your own bucketsYou, per runner setYes

The bucket is the one irreversible choice on the list. Because the bucket, the stack name, and the Region are locked together at creation, teams that want runners in two AWS Regions create two stacks with two buckets rather than repointing one.

Inside the bucket, WarpBuild writes two prefixes with documented shapes, which is what makes lifecycle rules and cost queries straightforward to write:

  • Cache: <bucket_name>/<org_id>/artifact_cache/<vcs_org>/<vcs_repo>/<vcs_ref>/<version>/<key>
  • Telemetry: <bucket_name>/runner/logs/all/<runner_id>.<log_file_name>

ECR has no equivalent line item, because the stack does not store images. It provisions the path, and your registry stays where it already is.

Architecture

Both prerequisites exist to keep bytes off metered paths. The stack provisions an S3 gateway endpoint for the bucket and ECR API and ECR Docker interface endpoints for image pulls, so cache traffic and registry traffic stay on the AWS network instead of leaving through a NAT gateway or the public internet. Those endpoints keep working when the runner security group has outbound-only rules, because the runner always initiates the connection.

TrafficEndpoint typeUsed by
Cache reads and writes to the stack bucketS3 gateway endpointThe WarpBuild cache action during a job
Runner system logsS3, outbound HTTPSThe runner telemetry writer
docker login and image manifest callsECR API interface endpointJob steps on runners in public and private subnets
Layer downloads and uploadsECR Docker interface endpointThe same job steps

One version detail matters here. Stacks created with CloudFormation template versions before v1.4 can hit ECR authentication failures, usually surfacing as an ecr login that times out, for runners placed in public subnets. The documented fix is a stack upgrade to template v1.4 through the WarpBuild dashboard. If a public subnet runner cannot log in while a private subnet runner in the same stack can, check the template version before you rewrite any IAM policy. AWS documents the endpoint behavior itself in the ECR VPC endpoints guide.

Configuration

Two identities carry the permissions, and mixing them up is the most common setup mistake on this surface.

The connection role, named warpbuild-<integration-id> and created by the CloudFormation connection stack, holds the StoragePermissions context. That context covers the stack bucket only: s3:CreateBucket, s3:ListBucket, s3:GetBucketLocation, s3:GetObject, s3:PutObject, s3:DeleteObject, the multipart actions, the bucket and object ACL actions, the bucket policy actions, and s3:PutLifecycleConfiguration with its matching read. It also carries EBS snapshot actions. It holds no ECR actions at all. The NetworkPermissions context is what lets the stack create and delete the VPC endpoints described above, through ec2:CreateVpcEndpoint and ec2:DeleteVpcEndpoints. The full policy document is expanded in the configuration guide, and the context-by-context walkthrough is on IAM permissions for BYOC on AWS.

The runner instance profile is what your job steps run as. It is set per runner set, under Custom Runner configuration and the Instance Profile ARN field, and WarpBuild injects nothing into it. A pull-only profile for a deploy pipeline is short:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ecr:GetAuthorizationToken"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:<region>:<account-id>:repository/<your-repo>"
    }
  ]
}

ecr:GetAuthorizationToken has to sit on Resource: "*" because the token is issued at account level rather than per repository. Everything else is scoped to the repository ARN. Push pipelines add ecr:PutImage and the three layer upload actions on the same resource, and jobs that write artifacts to a bucket of your own add s3:PutObject, s3:GetObject, and s3:ListBucket on that bucket ARN. Both examples are written out in the security hardening guide.

With the profile attached, a workflow needs no credential handling:

name: deploy
on:
  push:
    branches: [main]

jobs:
  ship:
    runs-on: warp-custom-deploy-linux
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/amazon-ecr-login@v2
      - run: docker pull <account-id>.dkr.ecr.<region>.amazonaws.com/<your-repo>:latest
      - run: ./scripts/deploy.sh

Each prerequisite fails in its own way, which is what makes this list worth reading before a security review rather than during an incident.

Missing pieceWhat breaksWhat keeps working
StoragePermissions on the connection roleCache reads and writes fail, runner system logs stop being pushed, lifecycle configuration cannot be appliedJobs still run, at cold-cache speed
ec2:CreateVpcEndpoint in NetworkPermissionsThe stack cannot create or replace the S3 and ECR endpoints, so that traffic falls back to whichever metered path the subnet hasAn already-created stack, until an endpoint changes
ecr:GetAuthorizationToken on the instance profiledocker login to ECR fails outright, before any repository policy is consultedEvery step that does not touch the registry
Repository-scoped ECR read actionsLogin succeeds and the pull fails on the first layerLogin, and any repository the profile does list
Stack template older than v1.4ECR authentication can time out for runners in public subnetsRunners in private subnets on the same stack
A bucket lifecycle policyNothing fails; cache objects and logs accumulate and the storage line grows every monthAll job behavior

Operations

Region placement is the decision that shows up on the bill rather than in a log. The bucket is required to sit in the stack's Region, and the stack is where the runners launch, so the cache side is handled by following the prerequisite. The registry side is yours to place, and four AWS prices decide what that placement costs. All four were checked on 2026-08-13.

PathAWS priceWhen you pay it
ECR to other services in the same Region$0.00 per GB (Amazon ECR pricing)Registry and runners in one Region
Across Availability Zones in the same Region$0.01 per GB in each direction (Amazon EC2 On-Demand pricing)Subnets spread for instance availability while the resources they read sit in one zone
Managed NAT data processing$0.045 per GB in US East (N. Virginia), plus $0.045 per hour (Amazon VPC pricing)Runners in private subnets, on everything leaving through that path
ECR across RegionsInternet data transfer rates on both sides; the AWS worked example uses $0.09 per GB (Amazon ECR pricing)Registry in one Region, runners in another

Same-Region placement plus the endpoints the stack provisions is what turns the first row into the row you are on. A registry one Region away from the runners moves the same gigabytes onto the last row, and the workflow file looks identical either way. The per-pull arithmetic, including the storage charge of $0.10 per GB-month per retained image version, is worked through on what pulling images from ECR costs your pipeline.

Two habits keep this surface quiet after setup. Set an S3 lifecycle policy on the stack bucket, with seven days of retention as the documented recommendation, because cache objects and telemetry logs are both written on every job and neither expires on its own. And scope your VPC endpoint policies to the repositories and buckets the runners actually need, which narrows the endpoints without touching the security group.

Costs split cleanly along the same line as the permissions. WarpBuild charges $0.002 per runner minute for BYOC Linux runners and $0.002 per runner minute for BYOC Windows runners, while your own AWS account is billed directly for EC2, EBS, S3 storage, and data transfer at your account rates, including discounts you already hold (WarpBuild pricing, checked 2026-08-13). The Linux and Windows sets are the ones an AWS stack launches.

One enterprise note for pull-heavy pipelines: egress costs are handled on the enterprise tier; see zero egress on the WarpBuild enterprise tier.

For the full inventory of objects a stack creates in your account, including the buckets, the endpoints, and the tags they carry, see what WarpBuild creates in your cloud account.

FAQ

Does the S3 bucket have to be in the same Region as the runners?

Yes. The bucket must sit in the same Region as the stack, and the stack name, bucket, and Region are all fixed at creation time. Runners launch inside that stack, so a bucket in another Region would put every cache read and write on a cross-Region path.

Do I have to create an ECR repository before the stack works?

No. WarpBuild configures the ECR API and ECR Docker VPC endpoints automatically for runners in public and private subnets. What you supply is the authority: an instance profile attached to the runner set that allows ecr:GetAuthorizationToken plus the read actions on the repositories your jobs pull.

Why does docker login to ECR time out on public subnet runners?

Stacks created with CloudFormation template versions before v1.4 can hit ECR authentication failures for runners in public subnets. Upgrading the stack to template v1.4 through the WarpBuild dashboard is the documented fix.

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.