Self-Hosted Runners
Use the runs-on frontmatter field to target a self-hosted runner instead of the default ubuntu-latest.
Runners must be Linux with Docker support. macOS and Windows are not supported.
Self-hosted runners may require sudo depending on the selected engine and configuration. For the default GitHub Copilot engine, there are two distinct sudo considerations:
-
AWF (Agentic Workflow Firewall): Runs rootless in the default network-isolation mode. Egress is enforced via Docker network topology — an internal Docker network (
awf-net) with no internet route and a dual-homed Squid proxy as the sole egress path. Nosudoand noNET_ADMINare required on the runner for AWF in this mode. Container-leveliptables, Squid proxy ACLs, and capability drops provide defense in depth, all managed inside the Docker daemon’s domain. -
Copilot CLI install: The
install_copilot_cli.shscript runs as the runner user but escalates viasudofor specific file operations (fixing.copilotdirectory ownership, cleaning stale chroot directories, and installing the Copilot binary). ARC pods withallowPrivilegeEscalation: falsewill fail at this step withsudo: The "no new privileges" flag is set.
ARC with Docker-in-Docker (DinD)
Section titled “ARC with Docker-in-Docker (DinD)”For a complete ARC DinD setup walkthrough for GitHub Copilot coding agent, see How to run GitHub Copilot coding agent on ARC with Docker-in-Docker.
Actions Runner Controller (ARC) deployments that use a Docker-in-Docker sidecar split the runner container and the Docker daemon container across separate filesystems.
Set runner.topology: arc-dind in workflow frontmatter for this environment.
Compiled workflows emit a runtime probe that inspects DOCKER_HOST.
Any tcp:// endpoint (for example tcp://localhost:2375, tcp://dind:2375, or tcp://172.30.0.5:2375) is treated as ARC DinD, so ensure DOCKER_HOST points to the DinD daemon for that runner pod.
With ARC DinD handling enabled, AWF receives --docker-host, shared-work sysroot staging is applied, and chroot config patching is enabled. The runtime no longer uses --docker-host-path-prefix.
Docker socket override for split-daemon topologies
Section titled “Docker socket override for split-daemon topologies”When DOCKER_HOST is a TCP address (e.g., tcp://localhost:2375) and the Docker socket is mounted via a bind mount at a non-standard path, the MCP gateway needs explicit configuration to find the socket and determine its group ID.
Set these environment variables at the runner level (e.g., in your ARC runner pod spec or Kubernetes deployment):
GH_AW_DOCKER_SOCK_PATH— absolute path to the bind-mounted Docker socket (e.g.,/dind-sock/docker.sock)GH_AW_DOCKER_SOCK_GID— numeric group ID of the Docker socket (e.g.,999)
Example runner pod configuration:
env: - name: GH_AW_DOCKER_SOCK_PATH value: /dind-sock/docker.sock - name: GH_AW_DOCKER_SOCK_GID value: "999"When both overrides are set, the MCP gateway will mount the specified socket path and add the specified group to the container without attempting automatic detection. If either variable is omitted, the gateway falls back to auto-detection from DOCKER_HOST and stat, and will fail with an actionable error if resolution fails.
runs-on formats
Section titled “runs-on formats”String — single runner label:
---on: issuesruns-on: self-hosted---Array — runner must have all listed labels (logical AND):
---on: issuesruns-on: [self-hosted, linux, x64]---Object — named runner group, optionally filtered by labels:
---on: issuesruns-on: group: my-runner-group labels: [linux, x64]---Sharing configuration via imports
Section titled “Sharing configuration via imports”runs-on must be set in each workflow — it is not merged from imports. Other settings like network and tools can be shared:
---network: allowed: - defaults - private-registry.example.comtools: bash: {}------on: issuesimports: - shared/runner-config.mdruns-on: [self-hosted, linux, x64]---
Triage this issue.Configuring the detection job runner
Section titled “Configuring the detection job runner”When threat detection is enabled, the detection job runs on the agent job’s runner by default. Override it with safe-outputs.threat-detection.runs-on:
---on: issuesruns-on: [self-hosted, linux, x64]safe-outputs: create-issue: {} threat-detection: runs-on: [self-hosted, linux, x64]---This is useful when your self-hosted runner lacks outbound internet access for AI detection, or when you want to run the detection job on a cheaper runner.
Configuring the framework job runner
Section titled “Configuring the framework job runner”Framework jobs — activation, pre-activation, safe-outputs, unlock, APM, update_cache_memory, and push_repo_memory — default to ubuntu-slim. Use runs-on-slim: to override all of them at once:
---on: issuesruns-on: [self-hosted, linux, x64]runs-on-slim: [self-hosted, linux, x64]safe-outputs: runs-on: [self-hosted, linux, x64] create-issue: {}---Configuring the maintenance workflow runner
Section titled “Configuring the maintenance workflow runner”The generated agentics-maintenance.yml workflow defaults to ubuntu-slim for all its jobs. To use a self-hosted runner for maintenance jobs, set runs_on in .github/workflows/aw.json:
Single label:
{ "maintenance": { "runs_on": "self-hosted" }}Multiple labels (runner must match all):
{ "maintenance": { "runs_on": ["self-hosted", "linux", "x64"] }}This setting applies to every job in agentics-maintenance.yml (close-expired-entities, cleanup-cache-memory, run_operation, apply_safe_outputs, create_labels, validate_workflows, and activity_report). Re-run gh aw compile after changing aw.json to regenerate the workflow.
Related documentation
Section titled “Related documentation”- Frontmatter —
runs-onandruns-on-slimsyntax reference - Imports — importable fields and merge semantics
- Threat Detection — detection job configuration
- Network Access — configuring outbound network permissions
- Sandbox — container and Docker requirements
- Ephemerals — full
aw.jsonmaintenance configuration reference - Enterprise Configuration — custom API endpoints for GHEC/GHES
Runner environment requirements
Section titled “Runner environment requirements”Self-hosted runners must meet these requirements for agentic workflows to run reliably.
Docker
Section titled “Docker”A working Docker daemon is required. The MCP gateway and sandbox run as containers.
- Unix socket: Docker must be accessible via a Unix socket (typically
/var/run/docker.sock). IfDOCKER_HOSTis unset, the gateway mounts/var/run/docker.sock. IfDOCKER_HOSTisunix://...or a bare absolute path, the gateway mounts that socket path. Other schemes (for exampletcp://...) are ignored for mounts and default back to/var/run/docker.sock. - Docker group: The runner user must be in the
dockergroup, or the socket must be world-readable. - ARC/Kubernetes: Docker-in-Docker (DinD) is required for ARC. Set
containerMode.type="dind"in your ARC Helm configuration. ThecontainerMode.type="kubernetes"mode is not supported. The dind sidecar must share the Docker socket via anemptyDirvolume, and the gateway retries the socket check for up to 10 seconds to handle startup race conditions. See How to run GitHub Copilot coding agent on ARC with Docker-in-Docker for the complete setup guide, and ARC (Actions Runner Controller) below for pod security details. - Split-daemon override: On ARC or other split-daemon topologies where the socket path or group ID cannot be auto-detected, set
GH_AW_DOCKER_SOCK_PATHandGH_AW_DOCKER_SOCK_GIDenvironment variables at the runner level. See Docker socket override for split-daemon topologies for details.
Filesystem
Section titled “Filesystem”- Use
RUNNER_TEMPfor transient state. Put sandbox state, tool downloads, and intermediate outputs in$RUNNER_TEMP, which is cleaned between jobs. On shared runners, avoid writing arbitrary workflow data to/tmpbecause it can persist across jobs. - No root assumption. Tool installs, file operations, and sandbox setup should run as the unprivileged runner user. The Copilot CLI install script escalates via
sudofor specific operations; see the sudo requirements above. - No global installs. Do not install packages to
/usr/local/,/opt/hostedtoolcache/, or other system-wide paths. These may be read-only, shared across runners, or bind-mounted read-only inside the sandbox. Use job-scoped writable locations instead. - No hardcoded
HOMEpaths. The runner’s home directory may not be/home/runner. Use$HOMEor$RUNNER_TEMPinstead of hardcoded paths.
Post-job cleanup
Section titled “Post-job cleanup”Self-hosted runners persist between jobs. Agentic workflows should clean up after themselves:
- Files written to
$RUNNER_TEMPare automatically cleaned. - Docker containers on the
awf-netbridge are stopped and removed by the sandbox teardown. - If your workflow creates files outside
$RUNNER_TEMP(e.g. in$GITHUB_WORKSPACE), the runner’s built-in workspace cleanup handles this.
Network
Section titled “Network”Self-hosted runners need outbound HTTPS access to:
api.githubcopilot.com(or your enterprise Copilot endpoint)github.com(or your GHES instance)ghcr.io(to pull the MCP gateway container image)- Any domains listed in your workflow’s
network.allowedconfiguration
GHES (GitHub Enterprise Server)
Section titled “GHES (GitHub Enterprise Server)”Agentic workflows can run on GHES with some additional configuration.
GHES compatibility mode
Section titled “GHES compatibility mode”GHES does not support the @actions/artifact v2.0.0+ backend used by upload-artifact@v4+ and download-artifact@v4+. Compiled workflows use the latest artifact action versions by default, which fail on GHES with GHESNotSupportedError.
Enable GHES compatibility mode in .github/workflows/aw.json to compile with GHES mode explicitly enabled:
{ "ghes": true}Or compile with --ghes for one-off workflow generation:
gh aw compile --ghes my-workflow.mdArtifact actions continue using the latest non-v3 pins because v3 artifact actions are deprecated.
API endpoint
Section titled “API endpoint”GHES instances need the api-target engine configuration. See Enterprise Configuration for full setup instructions.
---engine: id: copilot api-target: api.enterprise.githubcopilot.comnetwork: allowed: - defaults - github.company.com - api.enterprise.githubcopilot.com---ARC (Actions Runner Controller)
Section titled “ARC (Actions Runner Controller)”GitHub Copilot coding agent requires Docker-in-Docker (DinD) mode on ARC. Set containerMode.type="dind" in your ARC Helm configuration. The containerMode.type="kubernetes" mode is not supported.
Set runner.topology: arc-dind in workflow frontmatter to enable ARC DinD split-filesystem handling. See the ARC with Docker-in-Docker (DinD) section above and the ARC DinD setup guide for a complete walkthrough.
Docker-in-Docker (dind) sidecar
Section titled “Docker-in-Docker (dind) sidecar”The MCP gateway:
- Resolves the Docker socket path from
DOCKER_HOST(supportsunix://paths and bare absolute paths) - Auto-detects the socket’s group ID for correct permissions
- Retries the socket check for up to 10 seconds to handle the race condition where the gateway starts before
dockerd
Pod security
Section titled “Pod security”The dind sidecar requires privileged: true so dockerd can run. The runner container does not need privileged: true or NET_ADMIN.
In network-isolation mode (the default for topology: arc-dind), AWF enforces egress via Docker network topology — an internal Docker network with no internet route and a dual-homed Squid proxy. All network enforcement happens inside the Docker daemon’s domain (the dind sidecar). The runner container only issues Docker API commands via the socket; it never manipulates host iptables or network namespaces.