self-host →

Environments

Use RunsOn environments to isolate stacks, runtime permissions, GitHub App boundaries, networks, and regions.

RunsOn environments are stack and runtime isolation boundaries. They are not GitHub Actions environments, and they do not replace GitHub environment protection rules. A RunsOn environment decides which RunsOn stack should pick up a job, which GitHub App boundary is used, and which AWS IAM and network configuration the runner gets.

The default environment is production. If a Flex job omits an env label, it targets that default.

Usage

Flex

In Flex, a workflow selects the stack environment directly from the runs-on label:

jobs:
build:
runs-on: runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/env=staging

Fleet

In Fleet, the generated runner label includes the stack environment and approved fleet:

jobs:
build:
runs-on: runs-on/fleet=linux-small/env=production

See Flex job labels and Fleet runner groups for the product-specific controls.

When to use multiple environments

Use separate RunsOn environments when the blast radius should be different — production deployments vs ordinary CI, public vs private repositories, workloads with different IAM policies, runners in different VPCs / accounts / regions, or temporary branch-isolated stacks. Each Flex environment is backed by a separate RunsOn stack and private GitHub App, so a different set of repositories can be allowed per environment with separate AWS permissions.

Common patterns:

  • Single stack — keep production only. Enough for most teams.
  • Production plus CI — separate deployment workflows from normal pull-request CI.
  • Multi-region — one environment per region, targeted with Flex env + region labels, or with separate Fleet deployments.
  • Branch isolation — a temporary environment for a risky branch migration, removed once the branch lands.

How it works

Each Flex stack is deployed with an Environment stack parameter. The default is production. Workflows opt into a non-default environment by adding the env label — only the matching stack picks up the job, and all other stacks ignore it.

In Fleet, the equivalent isolation lives in the runner fleet itself. The runner fleet emits the runs-on/fleet=<name>/env=<environment> label that workflows target directly — see the example in Usage. Workflow authors don’t author the env value; it comes from the runner fleet’s Terraform configuration and the Fleet runner groups. Put separate isolation requirements into separate runner fleets, runner groups, or Fleet deployments, depending on whether the boundary is GitHub access, AWS capacity, IAM, or networking.

Flex vs Fleet

Same isolation goal, opposite ownership: Flex environments are workflow-driven, Fleet environments are baked into the runner fleet.

AspectFlexFleet
Where definedEnvironment stack parameterRunner fleet + Fleet GitHub boundary
How a workflow opts inenv=<name> label on runs-onTarget a fleet label that already carries env
Default valueproduction if env is omittedFleet emits its own label; no workflow default
Per-job override— (workflow can’t change the fleet’s env)
Typical boundary useOne stack per blast-radius / regionSeparate runner fleets, groups, or deployments

Considerations

Each RunsOn stack incurs a cost (AWS resources + regular stack upgrades), so create as few as possible. The majority of clients only run one stack.

For exact Flex syntax, see job labels. For Fleet boundaries, see Fleet runner groups and Fleet networking and IAM.