Configuration#

Config file location#

When you do not pass --config / -c, the config file is chosen in this order:

  1. GH_SR_CONFIG — path to a YAML file (absolute or relative to the current working directory).
  2. ~/.gh-sr/runners.yml — default after gh sr init.

There is no automatic discovery of ./config/runners.yml in the current directory; use GH_SR_CONFIG or -c if your file lives elsewhere.

If you pass -c /path/to/runners.yml, that path is always used (and GH_SR_CONFIG is ignored).

Run gh sr config path to see which config file and ~/.gh-sr/env path apply in your environment.

Authentication#

gh sr uses the GitHub CLI only: run gh auth login on the machine where you run gh sr. Do not use github.pat in YAML or GITHUB_PAT / GITHUB_TOKEN for gh sr (legacy github.pat is rejected at load time).

See Authentication for permissions and troubleshooting.

Secrets (~/.gh-sr/env)#

Before the YAML file is loaded, gh sr applies environment variables from ~/.gh-sr/env if that file exists (dotenv-style: KEY=value, optional export , # comments). This is optional and intended for other tooling if needed — not for GitHub API tokens used by gh sr. Create the directory and file with gh sr init, or run gh sr config edit-env.

Keep ~/.gh-sr permissions tight (chmod 700 ~/.gh-sr, chmod 600 ~/.gh-sr/env if you create files by hand).

Example runners.yml#

Edit ~/.gh-sr/runners.yml (after gh sr init), or set GH_SR_CONFIG / -c to another YAML file. You can open the resolved file in $VISUAL or $EDITOR with gh sr config edit.

hosts:
  my-laptop:
    addr: local              # run on the local machine (no SSH)
    # os and arch are auto-detected; override if needed

  mac-mini:
    addr: user@192.168.1.50
    os: darwin
    arch: arm64

  win-pc:
    addr: user@192.168.1.51
    os: windows
    arch: amd64

  vps-1:
    addr: root@203.0.113.10
    os: linux
    arch: amd64

runners:
  - name: enjoy-local
    repo: an-lee/enjoy
    host: my-laptop
    count: 1
    labels: [self-hosted, Linux, X64]
    runner_mode: native

  - name: enjoy-mac
    repo: an-lee/enjoy
    host: mac-mini
    count: 1
    labels: [self-hosted, macOS, ARM64]

  - name: enjoy-win
    repo: an-lee/enjoy
    host: win-pc
    count: 1
    labels: [self-hosted, Windows, X64]

  - name: hangar-ci
    repo: an-lee/hangar
    host: vps-1
    count: 2
    labels: [self-hosted, Linux, X64]
    runner_mode: container

  # GitHub Agentic Workflows — profile: agentic always uses container mode (DinD).
  - name: hangar-aw
    repo: an-lee/hangar
    host: vps-1
    profile: agentic
    count: 2

Config reference#

FieldDescription
hosts.<name>.addrSSH target (user@host or user@ip), or local to run on the machine where gh sr is running. Remote commands run as that user; on Linux, privilege expectations for setup / update follow Linux SSH user and privileges.
hosts.<name>.oslinux, darwin, or windows. Auto-detected when addr is local.
hosts.<name>.archamd64 or arm64. Auto-detected when addr is local.
hosts.<name>.windows_psOptional; Windows hosts only. Which executable runs remote PowerShell payloads: powershell (default, powershell.exe) or pwsh (pwsh.exe). gh sr uses -EncodedCommand so the user’s SSH default shell (cmd.exe or pwsh) does not break nested quoting.
runners[].nameBase name (instances become name-1, name-2, …)
runners[].repoGitHub owner/repo. Required unless org is set.
runners[].orgGitHub organization name. Use instead of repo for org-level runners.
runners[].groupRunner group name (org-level runners only). Passed as --runnergroup during registration.
runners[].hostReferences a key under hosts
runners[].countNumber of parallel instances (default: 1)
runners[].labelsLabels for workflow runs-on matching. Include agentic when the runner should serve GitHub Agentic Workflows. With profile: agentic, the agentic label is added automatically if omitted.
runners[].runner_modenative (default) or container. container runs each runner instance in its own privileged Docker container with an inner dockerd (DinD), fully isolating /tmp/gh-aw, iptables, and the MCP gateway port between concurrent jobs on the same host. The image is built locally by gh sr setup and includes Docker CE, dnsmasq, gh-aw, AWF, and the actions runner. profile: agentic always uses container mode (and runner_mode: native + profile: agentic is rejected). See Agentic Workflows.
runners[].profileOptional. Set to agentic for GitHub Agentic Workflows: implies runner_mode: container, adds the agentic label, and bakes gh-aw/AWF/DNS/tooling into the locally built runner image. See Host setup — GitHub Agentic Workflows.
runners[].ephemeralOptional boolean. When true, the runner handles one job and deregisters. Container mode uses --restart no; native passes --ephemeral to config.sh.
runners[].agentic_mcp_ports / runners[].agentic_mcp_port_baseRemoved. The per-instance MCP port-label scheme is no longer used: container mode isolates the MCP gateway port per runner. gh sr rejects these fields with a migration message — delete them.
container_runner_image.extra_apt_packagesOptional list of additional Debian package names to install in the locally built container runner image (runner_mode: container). At most 256 entries; each name must match [a-z0-9][a-z0-9+.-]* (max 200 chars). When set, the image tag gains a -x<8-hex> suffix so Docker does not reuse an image built without those packages. Core packages are in the repo manifest internal/runner/agentic-runner-image/apt-packages-core.txt.
container_runner_image.mtuOptional integer (576–1500). Forces the Docker network MTU for runner_mode: container — both the outer runner container’s egress interface and the inner dockerd bridge. Leave unset (0) to auto-detect the host’s egress MTU, which fixes the common reduced-MTU case (cloud overlay networks like GCP’s 1460, VPN/WireGuard) where large-packet TLS handshakes otherwise fail with Client network socket disconnected before secure TLS connection was established (e.g. actions/setup-go). Set this only when the host’s real path MTU is below its NIC MTU (a tunnel the NIC is unaware of) so auto-detection cannot see it. Only ever lowers the MTU; applied at container-create time, so changing it requires gh sr rebuild <name>.

Unique runner names per repository: GitHub registers each self-hosted runner by its instance name (name-1, name-2, …). That name must be unique within a given owner/repo. If two machines use the same base name and count: 1, both try to register as name-1 and only one registration remains active. Prefer distinct base names (for example myapp-win vs myapp-linux) so every machine has its own GitHub runner record and gh sr status matches the right row.