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.
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.
Launch the stack
one clickPick your AWS region: the console opens with the RunsOn template loaded.
Americas
Europe
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:
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:
runs-on: ubuntu-latestruns-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64A complete minimal workflow:
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.
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
RunsOnWorkerClusterwith “Unable to assume the service linked role”: the AWS account has no ECS service-linked role yet. Create it once withaws 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.
3. Make it fast with caching (recommended)#
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:
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.
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:
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=ghaOn Fleet, use the label of a fleet whose runner has s3-cache.
Learn more in the Magic Cache reference.