This guide is for downstream repositories that want to consume the runner contracts from this repo without guessing which jobs belong on self-hosted Synology runners, Linux Docker runners, Windows Docker runners, the Lume macOS pool, and GitHub-hosted runners.
| Job class | Synology shell-only pool | Linux Docker pool | Windows Docker pool | Lume macOS pool | GitHub-hosted runners | Notes |
|---|---|---|---|---|---|---|
| Node install, lint, test, build | Yes | Yes | Case-by-case | Usually unnecessary | Yes | On Synology, use OMT-Global/github-runner-fleet/actions/setup-shell-safe-node instead of actions/setup-node. |
| Python 3.12 lint/test | Yes | Yes | Case-by-case | Optional | Yes | actions/setup-python@v6 with python-version: '3.12' resolves locally on the Synology image. Other Python versions should stay hosted unless you control the full toolchain. |
| Terraform fmt/validate/init without cloud sidecars | Yes | Yes | Case-by-case | Optional | Yes | Keep plugin cache under RUNNER_TEMP or another writable container-local path. |
| Docs checks, markdown lint, shell validation | Yes | Yes | Case-by-case | Optional | Yes | Good fit for the shell-only pool when the job only needs baked-in CLI tools. |
| Release image builds, Buildx, QEMU, registry publish | No | Yes | No | No | Yes | Use Linux Docker for trusted private workloads; keep untrusted or public fork builds hosted. |
Docker daemon, docker build, docker compose, service containers |
No | Yes | Windows containers only | No | Yes | The Synology runner class intentionally avoids Docker socket mounts and does not support service containers. |
container: jobs |
No | Yes | Windows containers only | No | Yes | Use Docker-capable private planes for trusted repos; use hosted runners for untrusted code. |
| Browser/UI/E2E jobs needing extra distro packages | No | Case-by-case | Case-by-case | Sometimes | Yes | Prefer hosted runners unless the self-hosted requirement is explicit and owned. |
| macOS signing, Xcode builds, Swift/macOS validation | No | No | No | Yes | Yes | Use the Lume pool when you need a self-hosted macOS environment. |
| Public fork pull requests | No | No | No | No | Yes | Keep fork PRs on GitHub-hosted runners so untrusted code does not land on self-hosted infrastructure. |
Use these rules when deciding where a workflow job should run:
- Use
runs-on: [self-hosted, synology, shell-only, public]for trusted shell-safe jobs that can run with the baked-in Linux toolchain. - Use
runs-on: [self-hosted, linux, docker-capable, private]for trusted private Linux jobs that need Docker,container:, or service containers. - Use
runs-on: [self-hosted, windows, docker-capable, private]only for trusted private Windows container work. - Use
runs-on: [self-hosted, macos, arm64]only when you intentionally target the Lume macOS pool and control the repo trust boundary. - Keep pull requests from forks on GitHub-hosted runners.
- Keep any untrusted workflow using
container:,services:, browsers, Docker daemon access, Buildx, or extra distro package assumptions on GitHub-hosted runners. - Prefer a split workflow over forcing one runner class to handle incompatible jobs.
Use this when the repo is trusted and the job only needs Node plus standard shell tooling.
name: shell-safe node ci
on:
push:
branches: [main]
pull_request:
jobs:
test_trusted:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on:
- self-hosted
- synology
- shell-only
- public
env:
RUNNER_TEMP: /tmp/github-runner-temp
RUNNER_TOOL_CACHE: /opt/hostedtoolcache
AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache
steps:
- uses: actions/checkout@v6
- run: mkdir -p "$RUNNER_TEMP" "$RUNNER_TOOL_CACHE"
- uses: pnpm/action-setup@v5
with:
version: 10.32.1
- uses: OMT-Global/github-runner-fleet/actions/setup-shell-safe-node@main
with:
node-version: 24.14.1
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm testWhy this pattern exists:
actions/setup-nodecan fail on the shell-only Synology pool when extracting archives onto restrictive mounts.- The bundled setup action stays within the runner's supported contract.
This is the default split when a repo wants self-hosted speed for trusted code but safe isolation for public forks.
name: split trust ci
on:
push:
branches: [main]
pull_request:
jobs:
test_self_hosted_trusted:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on:
- self-hosted
- synology
- shell-only
- public
env:
RUNNER_TEMP: /tmp/github-runner-temp
RUNNER_TOOL_CACHE: /opt/hostedtoolcache
AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache
steps:
- uses: actions/checkout@v6
- run: mkdir -p "$RUNNER_TEMP" "$RUNNER_TOOL_CACHE"
- uses: pnpm/action-setup@v5
with:
version: 10.32.1
- uses: OMT-Global/github-runner-fleet/actions/setup-shell-safe-node@main
with:
node-version: 24.14.1
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm test
test_public_fork_pr:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v5
with:
version: 10.32.1
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm testUse this pattern whenever the repository is public or accepts outside contributions.
Use this when the job only needs the built-in Python toolchain shipped in the runner image.
jobs:
python312_trusted:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on:
- self-hosted
- synology
- shell-only
- public
env:
RUNNER_TEMP: /tmp/github-runner-temp
RUNNER_TOOL_CACHE: /opt/hostedtoolcache
AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache
steps:
- uses: actions/checkout@v6
- run: mkdir -p "$RUNNER_TEMP" "$RUNNER_TOOL_CACHE"
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: python --version
- run: python -m pip install -r requirements-dev.txt
- run: pytestBoundary condition:
- If you need Python 3.11, 3.13, or a matrix across versions, keep those lanes on GitHub-hosted runners unless you intentionally build and own a wider self-hosted contract.
jobs:
terraform_validate_trusted:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on:
- self-hosted
- synology
- shell-only
- public
env:
RUNNER_TEMP: /tmp/github-runner-temp
TF_PLUGIN_CACHE_DIR: /tmp/github-runner-temp/terraform-plugin-cache
steps:
- uses: actions/checkout@v6
- run: mkdir -p "$RUNNER_TEMP" "$TF_PLUGIN_CACHE_DIR"
- run: terraform fmt -check
- run: terraform init -backend=false
- run: terraform validateThis works well for pure CLI Terraform jobs. If the workflow also builds containers, talks to Docker, or needs sidecar services, split those parts onto the Linux Docker private plane for trusted repos or back to GitHub-hosted runners for untrusted code.
Use the Lume pool for self-hosted macOS work such as Swift validation, Xcode-dependent checks, or other tasks that explicitly need a macOS guest.
jobs:
macos_trusted:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on:
- self-hosted
- macos
- arm64
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v5
with:
version: 10.32.1
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm test
- run: swift --versionUse the hosted macos-latest image instead when the repository does not need self-hosted state or when you want GitHub-managed isolation for untrusted code.
- the workflow uses
container:and is not trusted private work assigned to a Docker-capable plane - the workflow uses
services:and is not trusted private work assigned to a Docker-capable plane - the job requires Docker daemon access, Buildx, or QEMU and is not trusted private Linux Docker work
- the job needs browsers or large sets of distro packages not already present in the runner contract
- the change comes from a public fork or another untrusted source
- the job depends on a language/version combination outside the documented self-hosted contract