Skip to content

Configure / Configuration

Configure runner fleets

A fleet maps one stable GitHub label to infrastructure approved by the platform team. Repository code can request that label; it cannot select an arbitrary subnet, AMI, instance profile, or security group.

Choose optional runner image profiles and gp3 storage performance for each fleet. Pin the approved image digest and review storage costs before applying changes.

Configure approved instance alternatives and UTC minimum-capacity windows to prepare fleets for busy periods while keeping their existing labels and permissions.

For a configuration example, see connecting a runner to a private RDS database.

Fleet definition

fleets = {
  build_arm64 = {
    scale_set_name     = "example-build-arm64"
    runner_group       = "gondola-build"
    architecture       = "arm64"
    capacity_mode      = "spot-with-on-demand-fallback"
    instance_type      = "m7g.large"
    subnet_ids         = var.arm64_subnet_ids
    security_group_ids = [aws_security_group.build.id]
    policy_arns        = [aws_iam_policy.build_readonly.arn]
    min_runners        = 0
    max_runners        = 40
    max_runner_lifetime = "1h"
  }
}

Each fleet can choose x64 or ARM64, its own AMI and runner image, VPC placement, IAM role, root-volume size, runner limits, and cost tags. One controller manages all configured fleets concurrently.

Field reference

FieldMeaningGuardrail
scale_set_nameStable GitHub workflow label and scale-set identityUnique per deployment; changing it creates a new routing target
runner_groupExisting GitHub runner group that receives the scale setRestrict its access policy to the intended repositories and workflows
architecturex64 or arm64Must match AMI, instance type, and runner image manifest
capacity_modeOn-Demand, Spot, or Spot with fallbackChoose from job retryability and start-time requirements
subnet_idsOrdered runner launch locationsUse reviewed egress and enough addresses; multiple zones for Spot
security_group_idsExisting runner network policyEmpty creates an outbound-only group using approved egress CIDRs
policy_arnsPolicies attached to a generated runner rolePrefer empty plus job-scoped GitHub OIDC
iam_instance_profile_arnExisting runner profileRequires its matching iam_role_arn
min_runnersBaseline idle minimumScheduled and adaptive policies can raise it; warm runners incur idle cost
max_runnersMaximum concurrent instancesInteger from 1 to 1,000 and not below the minimum
max_runner_lifetimeIndependent hard-expiry durationDefault six hours; must be long enough for valid jobs
root_volume_sizeEncrypted root volume in GiBAt least 30 GiB; billed while the runner exists

The label is the routing contract

Workflows request the scale-set name, not cloud parameters. Treat that name as a long-lived platform interface: document its architecture, software image, trust level, intended repository set, and support owner. Add a new fleet when those promises diverge instead of silently changing a widely used label.

jobs:
  verify-arm64:
    runs-on: example-build-arm64
    steps:
      - uses: actions/checkout@<reviewed-sha>
      - run: make verify

Additional labels may describe a fleet to GitHub, but they do not let a workflow override its module-defined AMI, role, network, instance type, or capacity policy.

Limit who can request the fleet

For an organization deployment, GitHub runner-group policy is the repository access boundary. Create a group with access limited to the repositories—and, where your GitHub plan supports it, selected workflows—that should use the fleet, then set runner_group to that existing group. Repository selection on the GitHub App installation does not replace this policy for organization-level self-hosted-runner permission.

Do not leave a sensitive fleet in a broadly available default group by accident. Use separate runner groups as well as separate Gondola fleets when trust levels differ.

Capacity modes

ModeBehaviorUse when
on-demandLaunches On-Demand capacity in the next configured subnet.Start reliability matters more than compute discount.
spotTries Spot across configured subnets and fails if capacity is unavailable.Jobs are safely retryable and price-sensitive.
spot-with-on-demand-fallbackTries Spot first, then On-Demand after definitive capacity exhaustion.You want savings without treating Spot shortage as a hard failure.

Spot retry semantics

Gondola rotates through configured subnets only after a definitive AWS capacity response. It does not retry an ambiguous launch result in a second Availability Zone because request idempotency is zonal and a cross-zone retry could create a duplicate. A fallback fleet attempts On-Demand only after definitive Spot-capacity exhaustion.

Separate trust domains

Use distinct fleets when workloads need different secrets, data access, network destinations, or change-control owners. A privileged release fleet should not share an IAM role or broad network policy with pull-request tests.

pull-request

No standing AWS access

Untrusted validation

build

Read-only artifact inputs

Normal CI compilation

release

Restricted publishing role

Protected branches only

Repository trust belongs in the routing decision as well. Workflows triggered by untrusted pull requests should not reach a fleet with publishing credentials, production network routes, or durable caches. Use branch protection and GitHub environment approval in addition to fleet isolation; an EC2 boundary does not repair an unsafe workflow.

Ownership and adoption

Gondola marks every GitHub scale set with a deployment-scoped ownership label and refuses to update or delete an unmarked same-named registration. Use the adoption switch only for a reviewed migration of a scale set already owned by the same Gondola deployment, then turn it off again.

To migrate an older Gondola-owned scale set, enable adopt_existing_scale_sets for one reviewed apply, confirm the ownership marker, then disable it. Never use adoption to take over a scale set managed by another installation.

Change a fleet safely

  1. Identify whether the change alters compatibility, trust, cost, or only implementation detail. Create a new label for a breaking change.
  2. Pin the replacement runner image by digest and confirm it publishes the fleet architecture.
  3. Review the generated launch-template version, IAM diff, network diff, and maximum capacity in terraform plan or tofu plan.
  4. Apply in a non-production repository and run a job that exercises the image, network, and required job-scoped permissions.
  5. Verify termination and cost tags before moving protected workflows. Keep the previous digest and fleet record for rollback.

Cost controls

  • Set min_runners = 0 and leave scheduled and adaptive warm capacity disabled to stop replenishing idle runners. Existing runners remain until their job completes or their lifetime expires.
  • Use at least two runner subnets in different Availability Zones when relying on Spot.
  • Activate the gondola:* cost-allocation tags in AWS Billing.
  • Remember that NAT gateways, public IPv4, logs, metrics, and data transfer can exceed the controller baseline.

Optional fleet caches

Enable cache_fleets for selected trusted fleets to reuse dependencies and Docker layers. Follow the S3 caching setup for bucket configuration, runner permissions, and workflow examples.

Next

Read the architecture

Follow a job through coordination, launch, registration, and retirement.