RunsOn RunsOn

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 root user, before the GitHub Actions runner agent is launched. The script must be a valid bash script, and will be executed with bash -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 schedule
  • match.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