Skip to content

Configuration Reference

Elastic Fruit Runner reads one YAML config file. Unknown fields, duplicate keys, and multiple YAML documents are errors.

Without --config, the daemon checks these paths in order:

  1. ~/.elastic-fruit-runner/config.yaml
  2. /opt/homebrew/var/elastic-fruit-runner/config.yaml
  3. /usr/local/var/elastic-fruit-runner/config.yaml
  4. /etc/elastic-fruit-runner/config.yaml

Use --config PATH or the environment variable ELASTIC_FRUIT_RUNNER_CONFIG to select another file. The flag wins when both are set.

orgs:
- org: your-org
auth:
pat_token: ghp_replace_me
runner_group: Default
runner_sets:
- name: efr-macos-arm64
backend: tart
image: ghcr.io/cirruslabs/macos-tahoe-xcode:26.3
labels: [self-hosted, macos, arm64]
max_runners: 2
repos:
- repo: your-org/your-repo
auth:
pat_token: ghp_replace_me
runner_sets:
- name: efr-repo-linux-arm64
backend: docker
image: ghcr.io/actions-runner-controller/actions-runner-controller/actions-runner-dind:latest
labels: [self-hosted, linux, arm64]
max_runners: 4
platform: linux/arm64
idle_timeout: 15m
log_level: info
api_addr: 127.0.0.1:8080
db_path: ./jobs.db
log_path: ./elastic-fruit-runner.log
cors:
allow_origin: "https://runner-console.example.com"
allow_methods: "GET, POST, OPTIONS"
allow_headers: "Content-Type, Connect-Protocol-Version, X-CSRF-Token"
expose_headers: "Connect-Protocol-Version"
allow_credentials: true
max_age: 3600

At least one item must exist in orgs or repos.

FieldTypeDefaultRules
orgslistemptyOrganization runner scopes
reposlistemptyRepository runner scopes
cloudobjectemptyElastic Fruit Cloud connection, replaces orgs and repos
idle_timeoutduration15mGreater than zero and no more than 24h
log_levelstringinfodebug, info, warn, or error
api_addrstring:8080Host and port accepted by the Go network listener
db_pathstring~/.elastic-fruit-runner/jobs.dbWritable file path or :memory:
log_pathstringemptyOptional writable log file. Empty sends logs to standard output
corsobjectruntime defaultsCORS response settings

A config must set either cloud or at least one of orgs and repos, never both. Validation stops at the first of these that fails.

FieldTypeDefaultRules
server_urlstringrequiredhttps:// URL of the cloud server. http:// is accepted only for localhost and 127.0.0.1
max_runnersintegerrequiredAt least 1, the number of runners this host may run at the same time

The enroll command writes this block, see How to run in cloud mode.

Each orgs[] item supports:

FieldTypeRequiredRules
orgstringyesValid GitHub organization name
authobjectyesExactly one auth method
runner_groupstringnoDefaults to Default
runner_setslistyesAt least one runner set

An organization name uses GitHub owner rules. It contains 1 to 39 letters, numbers, or single hyphen characters. It cannot start or end with a hyphen.

Each repos[] item supports:

FieldTypeRequiredRules
repostringyesowner/repository
authobjectyesExactly one auth method
runner_setslistyesAt least one runner set

The repository part contains 1 to 100 letters, numbers, dots, underscores, or hyphen characters.

Repository runner sets use the default runner group.

Configure exactly one of pat_token or github_app. They are mutually exclusive.

FieldTypeRequiredRules
pat_tokenstringyesNonempty

For an organization scope, the token needs Organization Self hosted runners read and write access.

For a repository scope, the token also needs repository Administration read and write access.

FieldTypeRequiredRules
github_app.client_idstringyesNonempty
github_app.installation_idintegeryesGreater than zero
github_app.private_key_pathstringyesReadable PEM private key file

The private key PEM block type must contain PRIVATE KEY.

See How to configure GitHub App authentication for the setup steps and permissions.

Each runner_sets[] item supports:

FieldTypeRequiredRules
namestringyesNonempty and unique across the whole config
backendstringyesdocker or tart
imagestringyesNonempty image reference
labelslist of stringsnoGitHub runner labels
max_runnersintegeryesFrom 1 through 1000
platformstringnoBackend specific
runtimestringnoDocker only, a single word such as runsc

platform can be empty. When set, it must start with linux/, such as linux/arm64 or linux/amd64.

runtime can be empty. When set, it is passed to docker run as --runtime, and the container runs without --privileged. Docker in Docker images do not work then. See How to install gVisor on Ubuntu.

The image must contain the GitHub Actions runner and the tools needed by the workflow.

platform and runtime must be empty. The image is a local or OCI Tart VM image.

Tart requires Apple Silicon and the Tart CLI on the host.

The server applies these runtime defaults when a field is empty:

FieldTypeRuntime defaultRules
allow_originstring** or one valid origin URL
allow_methodsstringGET, POST, OPTIONSOnly GET, POST, and OPTIONS
allow_headersstringContent-Type, Connect-Protocol-Version, X-CSRF-TokenOne line
expose_headersstringConnect-Protocol-VersionOne line
allow_credentialsbooleanfalseRequires a specific allow_origin when true
max_ageinteger0From 0 through 86400 seconds

Keep the Console on the same origin when possible. The daemon does not provide TLS.

For db_path, log_path, and GitHub App private key paths, validation checks the local file system.

  • An existing file must be usable for its purpose.
  • A configured file path cannot name a directory.
  • An existing direct parent directory must be writable.
  • If the direct parent does not exist, its parent must exist.
  • :memory: is allowed for db_path.

Startup, Console validation, and Console save use the same strict validator.

Validation checks:

  • YAML structure
  • Known and duplicate fields
  • Required fields
  • Duration and number limits
  • GitHub owner and repository names
  • Global runner set name uniqueness
  • Auth method choice
  • Private key file and PEM data
  • Backend, image, and platform rules
  • API address
  • CORS values
  • Writable storage paths

GitHub connectivity is not checked. Validation returns a warning for this limit.

See How to edit and activate config for the save flow.