The official GitHub Actions self-hosted runners documentation is split across two GitHub Docs pages: "Self-hosted runners" (concepts) and "Self-hosted runners reference" (requirements, routing, autoscaling, communication). As of November 2026, those pages state that a runner needs a supported OS (macOS 11.0 Big Sur or later, Ubuntu 20.04 or later, Windows 10 64-bit or later and more), must be online and idle to receive a job, gets a job re-queued if it does not pick it up within 60 seconds, and that any job left queued for more than 24 hours fails. This reference collects those facts with the exact commands, then shows how to keep a runner online, how to cut self-hosted runner startup time with a warm pool, and when a hosted Linux runner at $0.001 a minute replaces the fleet entirely.
Where the GitHub Actions self-hosted runners documentation lives
These are the GitHub Docs pages an engineer needs, by topic:
- Self-hosted runners (concepts): what a self-hosted runner is, and the repository, organization and enterprise levels.
- Self-hosted runners reference: machine requirements, supported operating systems and architectures, routing precedence, autoscaling, runner software updates and network communication.
- Adding self-hosted runners: the registration steps with
config.sh. - Configuring the self-hosted runner application as a service:
svc.sh, the way to keep a runner online after a reboot. - Monitoring and troubleshooting self-hosted runners: runner status (Idle, Active, Offline) and log locations.
- Actions Runner Controller: the Kubernetes autoscaling reference implementation, also documented in the actions/actions-runner-controller repository on GitHub.
Key numbers from the self-hosted runner docs
| Rule | Value | GitHub Docs page |
|---|---|---|
| Time for an assigned runner to pick up a job before re-queue | 60 seconds | Self-hosted runners reference |
| Maximum time a job stays queued before it fails | 24 hours | Self-hosted runners reference |
| Window to update the runner after a new version ships | 63 days | Self-hosted runners reference |
| Minimum network speed, upload and download | 70 kilobits per second | Self-hosted runners reference |
| Outbound connection required | HTTPS on port 443 | Self-hosted runners reference |
| Minimum macOS version | macOS 11.0 (Big Sur) | Self-hosted runners reference |
| Minimum Ubuntu version | Ubuntu 20.04 | Self-hosted runners reference |
Self-hosted runner requirements: operating systems and architectures
The Self-hosted runners reference lists three requirements for a runner machine: it can install and run the runner application, it can communicate with GitHub Actions, and it has enough hardware for your workflows. The runner application itself uses minimal resources.
Supported operating systems, per the same page:
- Linux: Red Hat Enterprise Linux 8 or later, CentOS 8 or later, Oracle Linux 8 or later, Fedora 29 or later, Debian 10 or later, Ubuntu 20.04 or later, Linux Mint 20 or later, openSUSE 15.2 or later, SUSE Enterprise Linux (SLES) 15 SP2 or later.
- Windows: Windows 10 64-bit, Windows 11 64-bit, Windows Server 2016, 2019 and 2022 64-bit.
- macOS: macOS 11.0 (Big Sur) or later.
Supported processor architectures: x64 on Linux, macOS and Windows, and ARM64 on Linux, macOS and Windows, a line the page marks as public preview.
One rule catches many teams: Docker container actions and service containers require a Linux runner with Docker installed.
Self-hosted macOS requirements (GitHub Docs)
For a self-hosted macOS runner, the Self-hosted runners reference requires macOS 11.0 (Big Sur) or later. The setup follows the same config.sh flow as Linux, using the macOS runner package from the New self-hosted runner page. To keep the macOS runner online, run ./svc.sh install and ./svc.sh start as the logged-in user: on macOS the service installs as a launchd agent, per the GitHub Docs page Configuring the self-hosted runner application as a service.
macOS runners cannot run Docker container actions or service containers, since those require Linux. A common split is macOS for iOS and Xcode builds and Linux for everything else, and the Linux side is where the bulk of CI minutes go.
Add a self-hosted runner: exact steps
From the GitHub Docs page Adding self-hosted runners:
- Open the repository, organization or enterprise settings, then Actions, Runners, New self-hosted runner.
- Pick the OS and architecture, and run the download commands GitHub shows.
- Register the runner with the scope URL and registration token from that page. Here
RUNNER_SCOPE_URLis your repository or organization URL on GitHub, exactly as the New self-hosted runner page prints it:
./config.sh --url "$RUNNER_SCOPE_URL" --token "$RUNNER_TOKEN" --labels linux,x64,build - Start it in the foreground to test:
./run.sh - Target it from a workflow by labels, or by runner group plus labels:
jobs: build: runs-on: group: build-runners labels: [self-hosted, linux, x64] The Self-hosted runners concepts page explains scope: repository-level runners serve one repository, organization-level runners serve many repositories, and enterprise-level runners can be assigned to multiple organizations.
Runner online status: "runner must be online" and "job queued until runner online"
The routing precedence section of the Self-hosted runners reference defines availability exactly:
- If GitHub finds an online and idle runner matching the job's
runs-onlabels and groups, it assigns the job to that runner. - If that runner does not pick up the job within 60 seconds, the job is re-queued for another runner.
- If no online and idle runner matches, the job remains queued until a runner comes online.
- If the job stays queued for more than 24 hours, it fails.
A labeled job with no matching runner does not fail at queue time; it waits out the 24-hour window. A job stuck on "Waiting for a runner to pick up this job" means one of three things: no runner carries every requested label, the matching runners are all busy, or the runner is Offline. The Monitoring and troubleshooting self-hosted runners page shows each runner's status as Idle, Active or Offline under Settings, Actions, Runners. Offline means the runner application is not running or cannot reach GitHub.
Keep runner online: run the runner application as a service
./run.sh stops when the terminal closes. To keep a runner online across logouts and reboots, install it as a service, per Configuring the self-hosted runner application as a service:
# Linux (systemd) sudo ./svc.sh install sudo ./svc.sh start sudo ./svc.sh status On macOS, run the same commands without sudo. On Windows, the config.cmd registration prompt offers to run the runner as a service.
Three things keep a persistent runner Idle instead of Offline:
- The service is enabled at boot, so a reboot brings it back.
- Outbound HTTPS on port 443 stays open to the GitHub domains listed in the reference (see the communication section below).
- The runner version stays current. If a runner is not updated within 63 days of a new version, GitHub stops assigning jobs to it.
Self-hosted runner startup time and warm pools
A persistent runner is always online but carries state between jobs: the concepts page notes that self-hosted runners do not need a clean instance per job. The Self-hosted runners reference recommends autoscaling with ephemeral runners instead, because GitHub assigns exactly one job to an ephemeral runner and then de-registers it:
./config.sh --url "$RUNNER_SCOPE_URL" --token "$RUNNER_TOKEN" --ephemeral Ephemeral runners trade state for startup time: each job needs a new runner to boot and register. The documented autoscaling options are:
- Actions Runner Controller (ARC): the reference implementation of GitHub's scale set APIs and the recommended Kubernetes option. In the runner scale set Helm chart,
minRunnerskeeps a warm pool of idle runners online so queued jobs are assigned immediately, andmaxRunnerscaps the pool. - GitHub Actions Runner Scale Set Client: an open source Go module (the actions/scaleset repository) for building autoscaling on VMs, containers or on-premises hardware outside Kubernetes.
- workflow_job webhooks: react to the
queued,in_progressandcompletedactions. The reference notes this approach depends on webhook delivery timing. - Just-in-time runners: create ephemeral runners through the REST API endpoints for self-hosted runners.
The reference also tells you to forward ephemeral runner logs to external storage, and to use --disableupdate for container images so runners do not self-update on every boot, as long as you ship new runner versions within 63 days.
Authentication for autoscalers, per the same page: tokens need the repo scope for private repositories, public_repo for public repositories, admin:org for organizations and manage_runners:enterprise for enterprises. A GitHub App needs the administration permission for repositories or organization_self_hosted_runners for organizations.
Network communication requirements
From the communication section of the Self-hosted runners reference: the host needs at least 70 kilobits per second up and down and outbound HTTPS on port 443. The page groups the required GitHub domains by function: essential operations, downloading actions, uploading and downloading logs, artifacts, job summaries and caches, runner version updates, OIDC tokens, GitHub Packages and containers, Git Large File Storage, Dependabot jobs and release assets. Open the groups your workflows use in your firewall, and add rules for CNAME targets where your firewall needs them. If your organization uses an IP allow list, add the runner's IP address to it.
Where Sengi fits: Linux runners that are online for every job
Everything above is the work of running a fleet: services, version updates inside 63 days, warm pool sizing, firewall rules and log forwarding. Sengi removes that work for Linux x64 jobs. Sengi runs GitHub Actions jobs on its own hosted Ubuntu 24.04 runners, and every job runs in a fresh VM, so each job gets the clean, single-job environment GitHub's docs recommend, with no runner to keep online and no pool to size.
The price leads: Sengi Basic costs $0.001 a minute at 2 vCPU, $1 per 1,000 minutes and one-sixth of GitHub's $0.006. Sengi Premium is the faster class for CPU-bound builds at half GitHub's price.
| Size | Sengi Basic | Sengi Premium | GitHub hosted |
|---|---|---|---|
| 2 vCPU | $0.001/min | $0.003/min | $0.006/min |
| 4 vCPU | $0.002/min | $0.006/min | $0.012/min |
| 8 vCPU | $0.004/min | $0.012/min | $0.022/min |
Source: Sengi pricing and GitHub's hosted runner per-minute rates, November 2026. For the full cost case against running your own machines, see https://sengi.run/blog/github-self-hosted-runner-alternative.
Keep macOS jobs on your self-hosted macOS runner and move the Linux jobs, which is where Docker builds, tests and service containers run.
Moving a job to Sengi: the one-line runs-on change
Connect your repositories to Sengi, then change one line per job:
jobs: test: runs-on: sengi-basic-2-ubuntu-2404 For CPU-bound builds, pick a Premium label such as sengi-premium-4-ubuntu-2404. Labels exist for 2, 4 and 8 vCPU in both classes: sengi-basic-2-ubuntu-2404, sengi-basic-4-ubuntu-2404, sengi-basic-8-ubuntu-2404, sengi-premium-2-ubuntu-2404, sengi-premium-4-ubuntu-2404 and sengi-premium-8-ubuntu-2404. Workflows, required checks and logs stay in GitHub. You start with 3,000 included minutes a month and no card. The setup steps are at https://sengi.run/guide.
FAQ
Where is the official GitHub Actions self-hosted runners documentation?
It is in GitHub Docs, on the "Self-hosted runners" concepts page and the "Self-hosted runners reference" page. Setup lives on "Adding self-hosted runners" and service setup on "Configuring the self-hosted runner application as a service".
What are the macOS requirements for a self-hosted runner?
The Self-hosted runners reference requires macOS 11.0 (Big Sur) or later. Docker container actions and service containers need a Linux runner instead.
Why is my job queued until a runner comes online?
GitHub assigns jobs only to online and idle runners whose labels and groups match runs-on. With no match, the job waits, and it fails after 24 hours in the queue, per the Self-hosted runners reference.
How do I keep a self-hosted runner always online?
Install the runner as a service with ./svc.sh install and ./svc.sh start, keep outbound HTTPS on port 443 open, and update the runner within 63 days of each new version.
How do I reduce self-hosted runner startup time?
Keep a warm pool of idle ephemeral runners, for example with the minRunners value in an Actions Runner Controller runner scale set. Hosted runners such as Sengi remove the pool entirely: every job gets a fresh VM.
Does Sengi support macOS or ARM64 runners?
Sengi runs Ubuntu 24.04 on x64. Move Linux jobs with a one-line runs-on change, such as runs-on: sengi-basic-2-ubuntu-2404, and keep macOS jobs on your macOS runner.
How much does a Sengi runner cost compared with GitHub's hosted runner?
Sengi Basic is $0.001 a minute at 2 vCPU, one-sixth of GitHub's $0.006. Sengi Premium is $0.003 a minute at 2 vCPU, half GitHub's price.

