quickstart →

Quickstart

From zero to a cached, 10x-cheaper GitHub Actions build running in your own AWS account.

This quickstart takes you from nothing to a fast, cached GitHub Actions build running on your own AWS account in about 15 minutes. Three steps: install RunsOn in your AWS account, point a workflow at it, then make it fast with caching.

  1. 01 Install in AWS ~10 min
  2. 02 Run your first job 1 line
  3. 03 Turn on caching 2 lines

Before you start#

You need three things. Nothing else has to be set up in advance.

  • A RunsOn license key: start a 15-day trial, or get one free for non-commercial use. You paste it into the AWS form in step 1.
  • An AWS account with admin access. A dedicated sub-account keeps your CI isolated.
  • A GitHub organization (or personal account) where you can create GitHub Apps. On an organization, that usually means an owner.

Stuck at any point? Email ops@runs-on.com or book an onboarding call.

RunsOn comes in two modes. Flex, where most teams start, lets each job pick its own runner. Fleet lets a platform team publish a few standardized runner fleets. Each step has a tab for both; see Flex vs Fleet to choose.

1. Install RunsOn in your AWS account#

Flex installs as a single CloudFormation stack, in about 10 minutes.

a. Launch the stack. Click your region. AWS opens the CloudFormation quick-create screen with the RunsOn template already loaded.

b. Fill in three fields and create the stack. Under Main configuration, enter your GitHub organization name, your license key, and an email for cost reports and alerts. Tick the acknowledgment boxes at the bottom, click Next, then Submit on the review screen. The stack reaches CREATE_COMPLETE after about 10 minutes. AWS also emails you an alert subscription request: confirm it.

c. Register the GitHub App. Open the stack’s Outputs tab, click the RunsOnEntryPoint URL, then Register GitHub App. Pick the repositories RunsOn should run jobs for. This creates a private GitHub App whose credentials live only in your AWS account. Stay logged in to GitHub during this step. GitHub then sends you back to the RunsOn status page: every check should be under Passing.

Want screenshots of every screen? Follow the Flex install guide. Need Terraform / OpenTofu, an existing VPC, or IAM permission boundaries? Install Flex with the Terraform module instead; every input is in the Flex Terraform reference.

Fleet installs with the official Terraform / OpenTofu module. On top of the list above, you need Terraform or OpenTofu, and a VPC with at least one public subnet.

a. Create the GitHub App. Enter your organization name, then click Create App. GitHub opens its app form with the name and permissions already filled in.

Enter an organization name to generate the link.

Once the app exists, note its App ID, generate a private key (GitHub downloads a .pem file), and click Install App to install it on your organization.

b. Add the module and apply. Paste this into your Terraform configuration and fill in your license key, alert email, App ID, private key file, and network:

main.tf
module "runs_on_fleet" {
source = "runs-on/runs-on/aws//modules/fleet"
version = "v3.5.0"
stack_name = "runs-on-fleet"
license_key = "your-license-key"
email = "alerts@example.com"
github_app_id = 123456
github_app_private_key = file("runs-on-fleet.private-key.pem")
vpc_id = "vpc-0123456789abcdef0"
public_subnet_ids = ["subnet-0123456789abcdef0"]
# Same shape as the Flex 2cpu-linux-x64 runner: 2 vCPUs, like GitHub's
# standard Linux runner.
runners = {
small-x64 = {
cpu = 2
family = ["m8", "r8"]
image = "ubuntu24-full-x64"
}
}
fleets = {
linux-small = {
runner = "small-x64"
}
}
}

Run terraform init and terraform apply (or the tofu equivalents), then confirm the alert subscription email AWS sends you. This publishes one fleet, linux-small, in the production environment. It registers into your organization’s Default runner group; set runner_group on a fleet to use another group, which you create first since Fleet never creates one.

Running Fleet across a GitHub Enterprise, or on private subnets? The Fleet install guide covers enterprise mode, runner groups, and private networking. Every input is in the Fleet configuration reference.

2. Run your first job on RunsOn#

Swap GitHub’s hosted label for a RunsOn label — that’s the only required change:

.github/workflows/ci.yml
runs-on: ubuntu-latest
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64

A complete minimal workflow:

.github/workflows/ci.yml
jobs:
build:
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v7
- run: echo "Hello from RunsOn"

The label is composable — request exactly the machine you want, for example cpu=4/ram=16/family=m8a+m8i or runner=16cpu-linux-arm64. See all job labels and Linux runners.

.github/workflows/ci.yml
jobs:
build:
runs-on: runs-on/fleet=linux-small/env=production
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v7
- run: echo "Hello from RunsOn Fleet"

Workflows target a fleet by name and environment: runs-on/fleet=<fleet>/env=<environment>. The platform team decides which fleets exist; workflow authors only pick one.

If something doesn't work 4 known issues
  • The stack fails on RunsOnWorkerCluster with “Unable to assume the service linked role”: the AWS account has no ECS service-linked role yet. Create it once with aws iam create-service-linked-role --aws-service-name ecs.amazonaws.com, delete the failed stack, and launch it again.
  • GitHub shows a 500 error after Register GitHub App: log in to GitHub in that browser, then retry.
  • A Flex job stays queued: allow repository-level self-hosted runners in your organization’s Settings → Actions → General, and check that the GitHub App is installed on that repository.
  • A Fleet job stays queued: check that the label matches runs-on/fleet=<fleet>/env=<environment> exactly, and that the repository can use the fleet’s runner group.

More fixes in Troubleshooting, or email ops@runs-on.com.

Turn on Magic Cache by adding extras=s3-cache to your runner label. RunsOn then transparently routes the GitHub Actions cache to a fast, in-VPC S3 backend:

  • 5x faster than the official GitHub Actions cache.
  • Unlimited size (items evicted by your stack’s policy, default 10 days).
  • Local to your VPC, so it’s fast, reliable, and secure.

Dependencies#

Keep using the standard actions/cache action — no other workflow changes:

.github/workflows/ci.yml
jobs:
build:
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache
steps:
- uses: runs-on/action@v2
- uses: actions/cache@v5
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

On Fleet, Magic Cache is a runner setting. Add extras = ["s3-cache"] to the runner from step 1 and re-apply; every fleet that uses it gets the cache, and workflows keep using actions/cache with the same runs-on/fleet=linux-small/env=production label.

main.tf
runners = {
small-x64 = {
cpu = 2
family = ["m8", "r8"]
image = "ubuntu24-full-x64"
extras = ["s3-cache"]
}
}

Docker layers#

The same extras=s3-cache runner also accelerates Docker builds. Point Buildx at the type=gha cache backend and RunsOn routes it to S3 transparently:

.github/workflows/ci.yml
jobs:
docker:
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache
steps:
- uses: runs-on/action@v2
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
cache-to: type=gha,mode=max
cache-from: type=gha

On Fleet, use the label of a fleet whose runner has s3-cache.

Learn more in the Magic Cache reference.

Next steps#