7.2 Self-Hosted Runner Setup, Architecture & Communication
Key Takeaways
- Self-hosted runners communicate with GitHub exclusively via outbound HTTPS connections over port 443 using long polling and WebSockets, requiring zero inbound ports or public IP addresses.
- Registration tokens are short-lived credentials valid for exactly 1 hour, used exclusively during `./config.sh` initialization to exchange the token for long-lived cryptographic credentials stored in `.runner` and `.credentials`.
- The `actions-runner` package is a cross-platform .NET Core application supporting Linux (x64, ARM64, ARM32), Windows (x64, ARM64), and macOS (x64, Apple Silicon).
- Self-hosted runners can be registered at three hierarchical scopes: Repository level (isolated to one repo), Organization level (shared across org repos), and Enterprise level (shared across multiple orgs).
- Production self-hosted runners should be installed and managed as continuous OS background daemons using the `./svc.sh` wrapper script to integrate with systemd on Linux, launchd on macOS, or Windows Services.
Self-Hosted Runner Setup, Architecture & Communication
While GitHub-hosted runners offer maintenance-free execution, organizations often require dedicated compute environments to access on-premises databases, leverage custom hardware architectures (such as ARM64, custom GPUs, or Apple Silicon build farms), utilize persistent local caches, or avoid per-minute hosted billing. In these scenarios, organizations deploy self-hosted runners.
A self-hosted runner is a physical server, virtual machine, or containerized environment that you provision, secure, and maintain, on which you install the official GitHub Actions runner application. Mastering the communication architecture, token authentication lifecycle, and daemon management of self-hosted runners is critical for enterprise platform administration and the GH-200 examination.
1. Outbound HTTPS Communication Model
A common security misconception is that GitHub Actions requires an open inbound firewall port to dispatch jobs to on-premises self-hosted runners. In reality, the GitHub Actions runner architecture operates on an outbound-only communication model.
+-----------------------------------------------------------------------------+
| RUNNER POLLING & COMMUNICATION ARCHITECTURE |
| |
| +--------------------------+ +----------------------------+ |
| | GitHub Control Plane | | Corporate Network | |
| | (api.github.com & | | (On-Premises / VPC) | |
| | Actions Message Bus) | | | |
| +--------------------------+ +----------------------------+ |
| ▲ │ |
| │ │ |
| │ 1. Outbound HTTPS (Port 443) Long Poll│ |
| │ "Any jobs matching my labels?" │ |
| ├─────────────────────────────────────────┤ |
| │ ▼ |
| │ 2. Job Dispatch Payload (HTTPS 443) +----------------+ |
| │◄───────────────────────────────────────┤ Self-Hosted | |
| │ | Runner Daemon | |
| │ 3. Stream Logs & Status (HTTPS 443) | (Runner. | |
| │◄───────────────────────────────────────┤ Listener) | |
| +----------------+ |
| [NO INBOUND PORTS OPEN] [OUTBOUND ONLY THROUGH NAT/PROXY]|
+-----------------------------------------------------------------------------+
Network Requirements
- No Inbound Open Ports: The host machine running the runner daemon does not require any open inbound ports (port 80, 443, and 22 can remain completely closed to ingress traffic).
- Outbound Protocol: All communication occurs over HTTPS (TCP port 443) or Secure WebSockets (
wss://). - Long Polling & Message Queue: The
Runner.Listenerprocess initiates an outbound HTTPS connection to GitHub's Actions Message Bus, maintaining an active long-poll session. When a workflow job is queued that matches the runner's labels, GitHub delivers the job assignment over the existing established outbound connection. - Proxy Support: In corporate environments where direct outbound internet access is restricted, the runner can be routed through an authenticating forward HTTP/HTTPS proxy by configuring
.proxyor environment variables (HTTP_PROXY,HTTPS_PROXY, andNO_PROXY).
2. Registration Token Lifecycle & Authentication
To bind a self-hosted runner to a GitHub repository, organization, or enterprise, an administrator must generate a registration token.
1. Admin requests registration token via UI or API (Valid for 60 minutes)
│
▼
2. Admin executes: ./config.sh --url <url> --token <registration-token>
│
▼
3. Runner daemon exchanges temporary token with GitHub API
• Receives unique Runner ID
• Generates local RSA private/public keypair
• Saves long-lived session credentials into .runner and .credentials
│
▼
4. Registration token expires; runner authenticates via saved RSA credentials
Critical Token Rules
- Short-Lived (1-Hour Expiration): Registration tokens generated via the GitHub Web UI or the REST API (
POST /repos/{owner}/{repo}/actions/runners/registration-tokenor/orgs/{org}/...) are valid for exactly 60 minutes. - Exchange Mechanism: The registration token is used only once during the initial execution of
./config.sh. The runner daemon presents the token to GitHub, authenticates the registration, and receives a permanent cryptographic identity. - Local Credential Storage: The permanent credentials and configuration are written to hidden files in the runner directory (
.runnercontaining runner metadata and.credentialscontaining the cryptographic OAuth tokens/keys). Future daemon restarts use these local credentials directly without needing a new registration token. - Removal Tokens: To decommission a runner gracefully, an administrator generates a short-lived removal token via API/UI and executes
./config.sh remove --token <removal-token>.
3. Supported Operating Systems & Package Structure
The GitHub Actions self-hosted runner application (actions-runner) is an open-source .NET Core application supported across major operating systems and CPU architectures:
- Linux: x64, ARM64 (e.g., AWS Graviton, Ampere), ARM32 (Raspberry Pi / embedded).
- Windows: x64, ARM64 (Windows Server 2019/2022, Windows 10/11).
- macOS: x64 (Intel), ARM64 (Apple Silicon M1/M2/M3/M4).
Directory Layout of actions-runner/
actions-runner/
├── bin/ # Core .NET runner binaries (Runner.Listener, Runner.Worker)
├── externals/ # Embedded runtimes (Node.js versions used to run JavaScript actions)
├── _work/ # Workspace directory where repositories are cloned and jobs execute
├── .runner # Local runner configuration, agent ID, pool ID, and server URL
├── .credentials # Encrypted RSA keys and credentials for GitHub Actions control plane
├── .env # Custom environment variables injected into all workflow jobs
├── .path # Custom PATH entries prepended to $PATH for all jobs
├── config.sh / config.cmd # Configuration script for initial registration
├── run.sh / run.cmd # Interactive foreground execution script
└── svc.sh / svc.cmd # System service management wrapper (systemd/launchd/WinService)
4. Runner Scopes: Repository, Organization & Enterprise
Self-Hosted runners can be registered at three distinct hierarchical levels, determining their visibility and job-sharing boundaries:
+-----------------------------------------------------------------------------+
| RUNNER SCOPE HIERARCHY |
| |
| [ENTERPRISE LEVEL] ─── Registered at Enterprise Account |
| Shared across multiple Organizations |
| |
| [ORGANIZATION LEVEL]─── Registered at Organization Account |
| Shared across multiple Repositories (via Groups) |
| |
| [REPOSITORY LEVEL] ─── Registered at Single Repository |
| Strictly isolated; cannot be shared |
+-----------------------------------------------------------------------------+
| Scope Level | Registration Target | Best Use Case | Governance & Access |
|---|---|---|---|
| Enterprise | https://github.com/enterprises/my-corp | Shared centralized compute infrastructure across business units. | Managed by enterprise administrators; assigned to organizations via Enterprise Runner Groups. |
| Organization | https://github.com/my-org | Standard enterprise microservices and shared team CI pools. | Managed by org owners; assigned to repositories via Organization Runner Groups. |
| Repository | https://github.com/my-org/my-repo | Specialized legacy build machines, dedicated hardware testbeds. | Bound exclusively to the single repository; cannot be accessed by other repos. |
5. Automated Installation & Service Daemonization (svc.sh)
Running a self-hosted runner interactively in a terminal via ./run.sh is acceptable for initial testing, but production environments require the runner to operate as a continuous background daemon that survives host reboots and restarts automatically upon failure.
The GitHub Actions runner package includes the svc.sh script (on Linux/macOS) and svc.cmd (on Windows) to manage native operating system services.
Automated Production Setup Script (Ubuntu Linux / systemd)
#!/usr/bin/env bash
set -euo pipefail
# 1. Create dedicated non-root runner user
sudo useradd -m -s /bin/bash actions-runner
sudo usermod -aG docker actions-runner || true
# 2. Prepare installation directory
sudo mkdir -p /opt/actions-runner && cd /opt/actions-runner
sudo chown -R actions-runner:actions-runner /opt/actions-runner
# 3. Download official runner package and verify SHA-256 checksum
RUNNER_VERSION="2.317.0"
RUNNER_TAR="actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"
EXPECTED_HASH="9e883d3f0ee374fbd34e02f82c8e496dc53f5d752b81763761ecda10d62df13e"
sudo -u actions-runner curl -o "${RUNNER_TAR}" -L \
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/${RUNNER_TAR}"
echo "${EXPECTED_HASH} ${RUNNER_TAR}" | shasum -a 256 -c
sudo -u actions-runner tar xzf "${RUNNER_TAR}"
# 4. Install OS-level dependencies
sudo ./bin/installdependencies.sh
# 5. Execute unattended registration (Using 1-hour registration token)
REG_TOKEN="AB12CD34EF56GH78IJ90KLMN"
ORG_URL="https://github.com/my-enterprise-org"
sudo -u actions-runner ./config.sh \
--url "${ORG_URL}" \
--token "${REG_TOKEN}" \
--name "linux-prod-runner-01" \
--runnergroup "production-pool" \
--labels "self-hosted,linux,x64,gpu,nvme-storage" \
--work "_work" \
--unattended \
--replace
# 6. Install and start native systemd background service
sudo ./svc.sh install actions-runner
sudo ./svc.sh start
# 7. Check service status
sudo ./svc.sh status
svc.sh Service Command Reference
./svc.sh install [user]: Generates a systemd service file (e.g.,/etc/systemd/system/actions.runner.<owner>.<name>.service) configured to run under the specified user account../svc.sh start: Starts the registered systemd service immediately../svc.sh status: Checks current execution state and PID of the running service daemon../svc.sh stop: Gracefully stops the runner service (finishes current job before terminating)../svc.sh uninstall: Removes the service configuration from systemd.
A network security architect is reviewing the firewall configuration for a cluster of self-hosted runners hosted in an on-premises datacenter. What inbound and outbound firewall port rules are required for the self-hosted runners to receive jobs and upload execution logs to GitHub?
An administrator generates a self-hosted runner registration token from the GitHub Organization settings page at 08:00 AM. What is the validity period of this token, and how does the runner authenticate with GitHub on subsequent days after registration?
A platform engineer is configuring a persistent Linux virtual machine to host a self-hosted runner. The engineer needs the runner daemon to start automatically when the server boots, run under a dedicated service user, and integrate with system logging. Which command sequence should the engineer run after ./config.sh?