Get started / Install
Install Gondola.
Connect GitHub, deploy to AWS, and run your first build. Work through the steps below; your entries carry forward.
Set up your account
You’ll need GitHub admin access, AWS deployment permissions, and an existing VPC with two subnets in different Availability Zones that can reach the internet over HTTPS.
Tools to have ready
Terraform 1.5 or newer, or OpenTofu 1.12 or newer, plus AWS CLI and jq. Run the commands in a terminal signed into the AWS account you want to use.
Your setup bundle
Sign up to continue here with your setup details filled in.
Already signed up? Load your setup file
The file is read in your browser; nothing is uploaded.
AWS bills your infrastructure separately, including during a free trial. Controllers and other persistent resources continue to cost money between jobs. Estimate your costs.
Connect your GitHub account
Create a private GitHub App so Gondola can register runners. The button opens GitHub with its permissions filled in.
Create a private GitHub App
These values stay in this browser and are sent directly to GitHub.
Add your GitHub account in step 1. Change setup
Use the GitHub setup helper
The Gondola release binary creates the App and saves its IDs after you approve it on GitHub. Already ran it? Load its installation file below.
gondola github-app setup --owner <your-owner> --account-type organizationDefault location: .gondola/github-app/installation.json. Keep its private-key.pem file for the next step.
Change the App name
- Create the private App on GitHub with the prefilled permissions and webhook disabled.
- On the App’s General page, copy its Client ID below. Use the Client ID, not the numeric App ID.
- Select Generate a private key and save the downloaded PEM file. You’ll use its file path in the next step.
- Open Install App and install it on your account. Copy the number at the end of the installation URL:
/settings/installations/<id>.
Choose which repositories can use the runners
In your organization, open Settings → Actions → Runner groups → New runner group. Create the group below, choose Selected repositories, and add your repository.
Store your credentials in AWS
Run these commands in your terminal. They read the files on your computer and send the credentials directly to AWS Secrets Manager.
Save credentials to Secrets Manager
Enter the paths to your downloaded files. Each command returns a secret ARN: paste that reference below the command.
AWS region: choose a region in step 1. Change setup
GitHub App private key
Run this command, then paste its output below.
aws secretsmanager create-secret \
--region '<aws-region>' \
--name 'gondola/github-app-private-key' \
--secret-string 'file://<path-to-downloaded-pem>' \
--query 'ARN' \
--output 'text'Signed Gondola entitlement
Run this command, then paste its output below.
jq -er '.signedEntitlement' '<path-to-setup-json>' | \
aws secretsmanager create-secret \
--region '<aws-region>' \
--name 'gondola/entitlement' \
--secret-string 'file:///dev/stdin' \
--query 'ARN' \
--output 'text'Optional. Adds a scheduled AWS task that checks for renewed access. AWS task, secret and log charges apply.
AWS CLI reads each file and sends it directly to Secrets Manager. Enter file paths and secret ARNs here; keep private key contents and tokens out of this page.
Keep the setup bundle’s activation key in your password manager for renewal. Store or remove the downloaded GitHub private key according to your team’s credential policy.
Choose your AWS network
Add your VPC and subnet IDs, then download gondola.tf. Your GitHub and secret settings are already carried over.
Your deployment configuration
Choose the existing network Gondola will use. The generated file contains secret references, never secret values.
Choose a GitHub owner · Choose an AWS region · Choose a network. Edit account or region
Choose from your AWS subnets
Run this read-only command, then load the file. Your AWS credentials stay on your computer.
aws ec2 describe-subnets --region '<aws-region>' --query 'Subnets[].{id:SubnetId,vpc:VpcId,zone:AvailabilityZone,state:State,available:AvailableIpAddressCount}' --output json > gondola-subnets.jsonUse an existing VPC with working outbound HTTPS. Gondola does not create a NAT gateway or change your routes.
- GitHub App
- Add the App IDs · Edit
- AWS secrets
- Add both secret ARNs · Edit
- Runner group
- gondola-pilot · Edit
- Setup bundle
- Load your saved setup JSON · Edit
Advanced settings
Complete your setup values before downloading.
Show missing or invalid values
- AWS region
- explicit network mode
- VPC ID
- two distinct subnet IDs
- GitHub owner
- GitHub organization or repository URL
- GitHub App client ID
- GitHub App installation ID
- GitHub App secret ARN
- entitlement secret ARN
- verification public key
- signing key ID
Generated deployment file
Controller and runner images are pinned to a published release.
Preview gondola.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.0"
}
}
}
provider "aws" {
region = "<aws-region>"
}
module "gondola" {
source = "gondola-build/gondola/aws"
version = "0.8.1"
name = "gondola-pilot"
vpc_id = "<vpc-id>"
subnet_ids = [
"<subnet-a>",
"<subnet-b>",
]
container_image = "ghcr.io/gondola-build/gondola@sha256:ac8d25877adc96a73c4be07c1f079b1d6502aab7d8f6bee863682199b0dacb53"
# Existing subnets must provide the egress described in the install guide.
assign_public_ip = false
github_config_url = "<https://github.com/owner-or-owner/repository>"
github_app_client_id = "<github-app-client-id>"
github_app_installation_id = 12345678
github_app_private_key_secret_arn = "<github-app-secret-arn>"
entitlement_secret_arn = "<entitlement-secret-arn>"
entitlement_public_key = "<verification-public-key>"
entitlement_key_id = "<signing-key-id>"
desired_count = 2
deployment_generation_nonce = "pilot-1"
require_image_digest = true
require_runner_image_digest = true
fleets = {
linux_x64 = {
scale_set_name = "gondola-linux-x64"
runner_group = "gondola-pilot"
architecture = "x64"
capacity_mode = "on-demand"
instance_type = "m7i.large"
min_runners = 0
max_runners = 2
}
}
}
output "gondola" {
value = {
cluster_arn = module.gondola.cluster_arn
service_name = module.gondola.service_name
log_group = module.gondola.log_group_name
deployment_generation = module.gondola.deployment_generation
controller_reference = "ghcr.io/gondola-build/gondola@sha256:ac8d25877adc96a73c4be07c1f079b1d6502aab7d8f6bee863682199b0dacb53"
aws_region = "<aws-region>"
github_url = "<github-url>"
runner_label = module.gondola.fleet_scale_set_names["linux_x64"]
}
}This configuration uses one Linux x64 On-Demand fleet and pinned controller and runner images. Caching, Spot and the optimizer can be added later.
Deploy Gondola
Save gondola.tf in a new folder, open a terminal there, and run the commands for your tool.
Preview the resources Gondola will create:
terraform init
terraform validate
terraform plan -out=gondola.tfplanReview the AWS account, network, permissions and resources in the plan. Keep your infrastructure state protected. Then deploy:
terraform apply gondola.tfplan
terraform output -json gondolaWait for the apply to finish. Gondola connects to GitHub and prepares the runner fleet during startup. Save the deployment output with your infrastructure records.
OpenTofu cannot find the module version
Registry indexing can lag a release. Set source = "registry.terraform.io/gondola-build/gondola/aws" in the module block, keep the version, and run tofu init again.
If setup stalls, use the troubleshooting guide.
Run one disposable job
Choose a recipe for your project, save the workflow in your repository, then run it from GitHub’s Actions tab.
A repository with package-lock.json and working test and build scripts. This recipe uses Node 24; change it to your supported version.
Save as .github/workflows/gondola-first-build.yml in your repository. Run it from the Actions tab. Your runner label is filled in.
name: Gondola first build
on:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: "gondola-linux-x64"
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
cache: npm
- run: npm ci
- run: npm test
- run: npm run build
Manual renewal is selected. You can enable automatic renewal in the AWS secrets step. Review renewal.
Add any extra tools your project needs. Check image compatibility, caching, containers, and OIDC.
Check the result
- The job leaves the queue and completes on your Gondola fleet.
- Its EC2 instance terminates after the job.
- The runner disappears from GitHub’s runner list.
Once these checks pass, your first runner is ready. Troubleshoot a queued or failed job.
After your first build
- Move an existing workflow, then add caching or the capacity optimizer when you need them.
- Review renewal and notifications before your entitlement expires.
- Use the operations guide for monitoring, upgrades and recovery. Separate fleets for workloads with different permissions or trust levels.
- Finished trying Gondola? Remove the AWS deployment and manage your subscription separately.
Release verification and advanced setup
Verify the release signatures, checksums, and image digest. See the release history for changes and compatibility notes.
Optional independent verification and private ECR copy
The public OCI release bundle contains compiled Linux and macOS tools, signed checksums, per-binary SBOMs, license notices, and a signed manifest. Retrieve it when your supply-chain policy requires independent evidence:
oras pull "ghcr.io/gondola-build/gondola:v0.8.2-artifacts"
cosign verify-blob \
--bundle dist/release-manifest.json.bundle \
--certificate-identity='https://github.com/gondola-build/gondola/.github/workflows/release.yml@refs/tags/v0.8.2' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
dist/release-manifest.json
if command -v sha256sum >/dev/null; then
(cd dist && sha256sum --check checksums.txt)
else
(cd dist && shasum -a 256 -c checksums.txt)
fi
export CONTROLLER_REF='ghcr.io/gondola-build/gondola@sha256:ac8d25877adc96a73c4be07c1f079b1d6502aab7d8f6bee863682199b0dacb53'
test "$(jq -r '.controller.reference' dist/release-manifest.json)" = "ghcr.io/gondola-build/gondola@sha256:ac8d25877adc96a73c4be07c1f079b1d6502aab7d8f6bee863682199b0dacb53"
cosign verify \
--certificate-identity='https://github.com/gondola-build/gondola/.github/workflows/release.yml@refs/tags/v0.8.2' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
"$CONTROLLER_REF"The public digest can be used directly. To keep pulls under your AWS controls, copy the verified multi-architecture image into private ECR and use the destination digest—not its mutable tag:
export CONTROLLER_REF='ghcr.io/gondola-build/gondola@sha256:ac8d25877adc96a73c4be07c1f079b1d6502aab7d8f6bee863682199b0dacb53'
export AWS_REGION='<region>'
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
aws ecr describe-repositories --repository-names gondola >/dev/null 2>&1 || \
aws ecr create-repository \
--repository-name gondola \
--image-scanning-configuration scanOnPush=true >/dev/null
aws ecr get-login-password --region "$AWS_REGION" | \
docker login --username AWS --password-stdin "$ECR_REGISTRY"
docker buildx imagetools create \
--tag "$ECR_REGISTRY/gondola:v0.8.2" \
"$CONTROLLER_REF"
docker buildx imagetools inspect \
"$ECR_REGISTRY/gondola:v0.8.2"Optional guided helper
The verified release binary can automate the GitHub App checks. It serves a temporary page on a loopback address, submits the App manifest directly to GitHub, verifies the completed installation, writes the PEM with mode 0600, and exits. No hosted Gondola setup service receives the key.
gondola github-app setup \
--owner example \
--account-type organization \
--name "Example Gondola Runners"Its installation.json records the non-secret client and installation IDs. Copy the Client ID exactly as shown in the App settings; its prefix can vary. Use that value instead of the numeric App ID.