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
| Field | Meaning | Guardrail |
|---|---|---|
scale_set_name | Stable GitHub workflow label and scale-set identity | Unique per deployment; changing it creates a new routing target |
runner_group | Existing GitHub runner group that receives the scale set | Restrict its access policy to the intended repositories and workflows |
architecture | x64 or arm64 | Must match AMI, instance type, and runner image manifest |
capacity_mode | On-Demand, Spot, or Spot with fallback | Choose from job retryability and start-time requirements |
subnet_ids | Ordered runner launch locations | Use reviewed egress and enough addresses; multiple zones for Spot |
security_group_ids | Existing runner network policy | Empty creates an outbound-only group using approved egress CIDRs |
policy_arns | Policies attached to a generated runner role | Prefer empty plus job-scoped GitHub OIDC |
iam_instance_profile_arn | Existing runner profile | Requires its matching iam_role_arn |
min_runners | Baseline idle minimum | Scheduled and adaptive policies can raise it; warm runners incur idle cost |
max_runners | Maximum concurrent instances | Integer from 1 to 1,000 and not below the minimum |
max_runner_lifetime | Independent hard-expiry duration | Default six hours; must be long enough for valid jobs |
root_volume_size | Encrypted root volume in GiB | At 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 verifyAdditional 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
| Mode | Behavior | Use when |
|---|---|---|
on-demand | Launches On-Demand capacity in the next configured subnet. | Start reliability matters more than compute discount. |
spot | Tries Spot across configured subnets and fails if capacity is unavailable. | Jobs are safely retryable and price-sensitive. |
spot-with-on-demand-fallback | Tries 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-requestNo standing AWS access
Untrusted validation
buildRead-only artifact inputs
Normal CI compilation
releaseRestricted 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
- Identify whether the change alters compatibility, trust, cost, or only implementation detail. Create a new label for a breaking change.
- Pin the replacement runner image by digest and confirm it publishes the fleet architecture.
- Review the generated launch-template version, IAM diff, network diff, and maximum capacity in
terraform planortofu plan. - Apply in a non-production repository and run a job that exercises the image, network, and required job-scoped permissions.
- Verify termination and cost tags before moving protected workflows. Keep the previous digest and fleet record for rollback.
Cost controls
- Set
min_runners = 0and 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.