Repository configuration
Learn all about .github/runs-on.yml configuration file
RunsOn supports describing various options through a configuration file. This file is located at .github/runs-on.yml (must be named exactly that) in the repository where you are using RunsOn runners.
The configuration file supports defining:
- custom images
- custom runners
- runner pools for faster job pickup times
- the list of admins having SSH access to the runners
- a global configuration file to extend from
Structure
The configuration file is a YAML file with the following top-level keys: _extends, runners, images, pools, and admins.
# .github/runs-on.yml
_extends: ...
runners: {...}
images: {...}
pools: {...}
admins: [...string]
_extends
If set, the configuration file will inherit from the configuration file in the repository specified by the _extends value.
A common use case is to define a global configuration file in the .github-private repository of your organization, and then inherit from it in other repositories. Do not forget to allow the RunsOn GitHub App to access the repository hosting the global configuration file.
Example
# .github/runs-on.yml
_extends: .github-private
runners:
...
In this example, the configuration file in the repository will inherit from the configuration file stored at .github/runs-on.yml in the .github-private repository.
runners
Mapping of custom runner names to runner configuration.
Available configuration options: cpu, ram, volume, disk (deprecated), family, spot, image, ssh, extras, private, retry, preinstall, prerun, tags.
For more details about each option, please refer to the job-level labels documentation.
Example
# .github/runs-on.yml
runners:
docker_with_ipv6:
cpu: [2, 4]
ram: [2, 8]
volume: 80gb:gp3
family: ["c7", "m7", "r7"]
spot: capacity-optimized
tags:
- custom-tag-key1:custom-tag-value1
- custom-tag-key2-no-value
preinstall: |
cat > /etc/docker/daemon.json <<EOF
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64"
}
EOF
systemctl restart docker
prerun: |
echo "Refresh short-lived credentials right before the job starts"
su - runner -c "aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com"
other-runner:
...
And the runner will be available for use in the repository:
jobs:
test:
runs-on: runs-on=${{ github.run_id }}/runner=docker_with_ipv6
runners.family
Required. Array of strings. e.g ["c7", "m7"].
runners.image
Required. String. e.g ubuntu22-full-x64.
runners.cpu
Array of integers. e.g [2, 4].
runners.ram
Array of integers. e.g [2, 8].
runners.volume
String specifying volume configuration. Format: size:type:throughput:iops (e.g., 80gb:gp3:125mbs:3000iops).
All parts are optional and can be in any order:
- Size:
80gb,500g,1tb - Type:
gp3,gp2,io1,io2,st1,sc1,standard - Throughput:
125mbs,250mbps(gp3 only) - IOPS:
3000iops,4000iops(gp3, io1, io2)
Examples: 100gb, 80gb:gp3, 200gb:gp3:500mbs:4000iops
runners.disk
Deprecated since v2.9 - use volume instead.
String. e.g default or large. This is automatically converted to the equivalent volume configuration.
runners.spot
Boolean or string. e.g capacity-optimized, co, lowest-price, lp, price-capacity-optimized, pco, true, false.
runners.ssh
Boolean. e.g true, false.
runners.extras
Array of strings. e.g ["s3-cache", "otel"].
Supported values: s3-cache, ecr-cache, efs, tmpfs, otel.
runners.private
Boolean. e.g true, false.
runners.retry
String. e.g when-interrupted, false.
runners.preinstall
String. Script to run on the runner, before the GitHub Actions runner agent is executed (see images.preinstall for more details).
runners.prerun
String. Script to run on the runner just before the workflow job starts (see images.prerun for more details).
runners.tags
Array of strings containing custom key:value tags to apply to the runner underlying EC2 instance. e.g. ["custom-tag-key1:custom-tag-value1", "custom-tag-key2-no-value"].
Cannot start with the runs-on- prefix. Tag keys and values will be automatrically sanitized to remove any special characters (/ is allowed for values).
images
Mapping of custom image names to image configuration.
Available configuration options: name, ami, platform, arch, owner, preinstall, prerun, tags.
Example
# .github/runs-on.yml
images:
custom:
owner: "123456789"
name: "my-org/my-image-name-*"
arch: x64
platform: linux
tags:
# filter with specific value
is-production-ready: "true"
# allow any value
other-tag: "*"
fixed-ami:
ami: "ami-0abcd159cbfafefgh"
And you can use the image in your workflows like this:
jobs:
test:
runs-on: runs-on=${{ github.run_id }}/image=custom
# or
runs-on: runs-on=${{ github.run_id }}/image=fixed-ami
images.name
Required (unless ami is set). String. Can include wildcards, in which case RunsOn will pick the most recent image matching the pattern.
images.ami
Required (unless name is set). String. If set, the image will be pinned to the specified AMI, irrespective of the name option.
images.platform
String. Either linux or windows. Not required if ami is set.
images.arch
String. Either x64 or arm64. Not required if ami is set.
images.owner
String. AWS account ID where the image is hosted.
images.preinstall
String. Script to run on the runner, before the GitHub Actions runner agent is executed. Takes precedence over the preinstall option at the runner level if both are set.
Useful to perform pre-authentication steps, such as logging into private registries. Installing additional software will make your runner boot time slower, it is recommended that you create your own AMI instead.
If the preinstall script fails, the job will fail. You will find the preinstall exit status and logs in the “Set up runner” section of the job logs.
Platform behaviour:
-
On Linux, the preinstall script is executed as the
rootuser, before the GitHub Actions runner agent is launched. The script must be a valid bash script, and will be executed withbash -e(so any failure will fail the script immediately). -
This option is not yet available for Windows images.
images.prerun
String. Script to run on the runner just before the workflow job starts.
Available since v2.12.0. This is mainly useful with warm pools, where preinstall runs during the warm-up phase and can therefore happen long before a job is assigned. Use prerun for just-in-time setup that must still be fresh when the job begins, such as docker login or other short-lived credentials.
images.tags
Mapping of key-value tags to filter the image when searching for it.
pools
Mapping of pool names to pool configurations. Pools allow you to pre-provision runners that stay warmed up and ready to pick up jobs immediately, reducing queue times from ~25 seconds (cold-start) to under 6 seconds for hot instances.
For comprehensive documentation about pools, see the Runner pools guide.
Available configuration options: env, runner, timezone, schedule.
Example
# .github-private/.github/runs-on.yml
runners:
small-x64:
image: ubuntu24-full-x64
ram: 1
family: [t3]
volume: gp3:30gb:125mbps:3000iops
pools:
small-x64:
env: production
runner: small-x64
timezone: "America/New_York"
schedule:
- name: business-hours
match:
day: ["monday", "tuesday", "wednesday", "thursday", "friday"]
time: ["08:00", "18:00"]
stopped: 5
hot: 2
- name: default
stopped: 2
hot: 1
And you can use the pool in your workflows like this:
jobs:
test:
runs-on: runs-on/pool=small-x64
pools.env
String. The stack environment this pool belongs to (e.g., production, dev). This ensures the pool only serves jobs targeting that environment.
pools.runner
Required. String. Reference to a runner definition in the runners section. The pool will create instances matching this runner specification.
pools.timezone
String. IANA timezone for schedule calculations (e.g., America/New_York, Europe/Paris). Defaults to UTC if not specified.
pools.schedule
Required. Array of schedule rules. Each rule defines target capacity (hot/stopped instances) for specific time periods.
Schedule rule fields:
name: Human-readable name for this schedulematch.day: Array of days when this schedule applies (e.g.,["monday", "tuesday"])match.time: Time range[start, end]in 24-hour format (e.g.,["08:00", "18:00"])stopped: Number of stopped instances to maintain (pre-warmed but not running)hot: Number of hot instances to maintain (running and ready)
Schedules are evaluated in order. The first matching schedule is used. The last schedule without a match field serves as the default.
admins
List of GitHub usernames that have SSH access to the runners launched for this repository. They come in addition to the admins defined at the CloudFormation stack level.
Example
# .github/runs-on.yml
admins:
- crohr
- other-github-user
Full example
# .github/runs-on.yml
_extends: .github-private
images:
mycustomimage:
platform: "linux"
arch: "x64"
owner: "099720109477"
# will take the most recent AMI matching the wildcard pattern
name: "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
otherimage:
platform: "linux"
arch: "x64"
ami: "ami-0abcd159cbfafefgh"
runners:
cheap:
# Useful for workflows that do not require a lot of CPU / RAM.
ram: [2, 4, 8]
# Burstable instances, valid for both x64 and arm64
family: ["t3", "t4"]
image: otherimage
ssh: false
fast:
cpu: 32
volume: 200gb:gp3:750mbs:4000iops
family: ["c7a", "m7a"]
spot: false
# reference custom image defined above
image: mycustomimage
preinstall: |
echo "doing some stuff before the GitHub Actions runner agent is executed..."
echo "For instance, disabling the host's IPv6 if needed"
sysctl -w net.ipv6.conf.all.disable_ipv6=1
admins:
- crohr
- other-github-user
Using the configuration in a GitHub Workflow
# .github/workflows/my-workflow.yml
jobs:
test:
runs-on: runs-on=${{github.run_id}}/runner=2cpu-linux-x64/image=mycustomimage
# .github/workflows/my-workflow.yml
jobs:
test:
runs-on: runs-on=${{github.run_id}}/runner=2cpu-linux-x64/image=otherimage
# .github/workflows/my-workflow.yml
jobs:
test:
runs-on: runs-on=${{github.run_id}}/runner=cheap
# .github/workflows/my-workflow.yml
jobs:
test:
runs-on: runs-on=${{github.run_id}}/runner=fast
Sharing configuration across repositories
RunsOn comes with a feature that allows a local configuration file to inherit from a globally defined configuration file, by using the _extends directive.
The recommendation is to store the global configuration file in the special .github-private repository of your organization, but you can choose any other repository as well (public or private).
Example:
# your-repo/.github/runs-on.yml
_extends: .github-private
# .github-private/.github/runs-on.yml
runners:
cheap-arm64:
cpu: [1, 2]
family: ["t4g"]
image: ubuntu22-full-arm64