GitHub Agentic Workflows

How to run GitHub Copilot coding agent on ARC with Docker-in-Docker

Use this guide to run GitHub Copilot coding agent on an Actions Runner Controller (ARC) runner scale set with Docker-in-Docker (DinD).

Before starting, confirm you have a Kubernetes cluster, helm and kubectl installed, and credentials for runner registration (a GitHub PAT or GitHub App credentials).

Terminal window
helm install arc \
--namespace "arc-system" --create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

2. Create the runner namespace and auth secret

Section titled “2. Create the runner namespace and auth secret”

Create the namespace and a Kubernetes secret with your runner registration credentials. You can use either a GitHub PAT or GitHub App credentials:

Terminal window
kubectl create ns arc-runners
# Option A: Personal access token
kubectl create secret generic arc-runner-secret \
--namespace=arc-runners \
--from-literal=github_token=<YOUR_PAT>
# Option B: GitHub App (recommended for production)
kubectl create secret generic arc-runner-secret \
--namespace=arc-runners \
--from-literal=github_app_id=<APP_ID> \
--from-literal=github_app_installation_id=<INSTALL_ID> \
--from-literal=github_app_private_key=<PRIVATE_KEY>

See Authenticating to the GitHub API for details on each option.

3. Install a runner scale set in DinD mode

Section titled “3. Install a runner scale set in DinD mode”
Terminal window
helm install "arc-runner-set" \
--namespace "arc-runners" --create-namespace \
--set githubConfigUrl="https://github.com/<OWNER>/<REPO>" \
--set githubConfigSecret="arc-runner-secret" \
--set containerMode.type="dind" \
--set-json 'template.spec.containers=[{
"name": "runner",
"image": "ghcr.io/actions/actions-runner:latest",
"command": ["/home/runner/run.sh"]
}]' \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

When containerMode.type="dind" is enabled, ARC configures the DinD sidecar in privileged mode by default so the Docker daemon can run. The runner container does not require privileged: true or NET_ADMIN. If you use a custom pod template, ensure you do not remove the privileged setting on the DinD sidecar.

Open https://github.com/<OWNER>/<REPO>/settings/actions/runners (or the organization-level runners page) and confirm the arc-runner-set runner is online.

Set your workflow frontmatter to use the runner scale set label and ARC DinD topology:

---
on: issues
runs-on: arc-runner-set
runner:
topology: arc-dind
---

runner.topology: arc-dind is required so compiled workflows enable ARC DinD split-filesystem handling (a shared runner/daemon workspace root, Docker-daemon-visible mount paths, and ARC-specific sandbox setup). No other sandbox or network settings are needed — the defaults handle everything else.

After editing the frontmatter, recompile the lock file:

Terminal window
gh aw compile

Commit both the .md workflow file and the generated .lock.yml file.

When compiled workflows detect a tcp:// value in DOCKER_HOST (set automatically by ARC DinD), a runtime probe activates ARC DinD handling:

  • Sysroot staging — system binaries (/usr, /lib, /bin, /sbin) are copied into a Docker named volume so the Docker daemon can provide them to the agent container without bind-mounting the runner’s filesystem.
  • Workspace mount — the checked-out repository at GITHUB_WORKSPACE is explicitly mounted into the agent container. Both runner and daemon can see it because ARC DinD shares the /home/runner/_work/ volume.
  • Chroot identity — the runner’s UID/GID and home directory are patched into the AWF config so the agent runs with the correct identity inside the chroot.
  • Artifact consolidation — agent output files are consolidated under ${{ runner.temp }}/gh-aw/ before upload so downstream jobs (detection, safe-outputs) can find them.
  • Network isolation — AWF enforces egress 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. The runner container issues Docker API commands to the DinD sidecar daemon; the daemon creates the networks and manages all traffic enforcement. No host iptables rules are applied from the runner container.

Use versions at or above these minimums:

ComponentMinimum versionWhy
gh-awv0.82.5Includes ARC DinD workspace and detection fixes.
AWF (agentic-workflow-firewall)v0.27.22Includes DinD squid log permission fixes.
ItemRequired?Notes
DinD container modeYesGitHub Copilot coding agent needs a Docker daemon in the runner pod.
NET_ADMIN capabilityNoNot required. AWF enforces egress via Docker network topology (network isolation mode), not host iptables. The DinD sidecar daemon manages all network enforcement internally.
ghcr.io/actions/actions-runner:latestRecommendedUse the official runner image, or a compatible custom image with equivalent runner requirements.
Runner userYesNon-root runner users are supported. sudo must remain available on the runner container for the Copilot CLI install script (binary installation and file ownership operations).
DinD sidecar privilegeYesARC DinD mode configures a privileged sidecar for Docker daemon operation.
Shared work volume (/home/runner/_work)YesRunner and Docker daemon share this volume in ARC DinD mode, so workspace mounts work without host path translation.
Specific Kubernetes distributionNoAny conformant cluster works (for example minikube, EKS, AKS, or GKE).
Specific namespace namesNoarc-system and arc-runners are conventions only.

If you previously used custom bootstrap actions, copilot shims, /etc pre-seeding, XDG environment overrides, or manual DOCKER_HOST / MCP_GATEWAY_DOMAIN settings to run on ARC DinD, remove them when adopting runner.topology: arc-dind. The compiler now handles all of these automatically. Leftover workarounds may conflict with the generated workflow steps.

To migrate:

  1. Remove any pre-agent-steps, resources, or safe-outputs.threat-detection.steps blocks that were workarounds for ARC DinD.
  2. Remove manual engine.env overrides for XDG_CACHE_HOME, XDG_CONFIG_HOME, XDG_STATE_HOME, MCP_GATEWAY_DOMAIN, MCP_GATEWAY_PORT, and DOCKER_HOST.
  3. Remove sandbox.agent.mounts entries that staged files for the DinD daemon.
  4. Add runner.topology: arc-dind to frontmatter.
  5. Run gh aw compile and commit the updated lock file.
  • allowPrivilegeEscalation: false is not supported. The Copilot CLI install script uses sudo. Clusters that enforce no-new-privileges via PodSecurity Admission or OPA policies will fail at the install step.
  • MCP gateway Docker socket access — on runners where DOCKER_HOST is a TCP endpoint and no Unix socket exists at /var/run/docker.sock, the MCP gateway may fail to connect to the Docker daemon (Docker daemon is not accessible). As a workaround, expose the DinD sidecar’s Unix socket on the runner container at /var/run/docker.sock via a shared volume or symlink. See #44251 for tracking.

The agent sees no files and exits with a no-op message. This was fixed in gh-aw v0.82.5. Upgrade and recompile:

Terminal window
gh aw upgrade
gh aw compile

Detection job fails with spawn /usr/local/bin/copilot ENOENT

Section titled “Detection job fails with spawn /usr/local/bin/copilot ENOENT”

The threat-detection job can’t find the Copilot binary. This was fixed in gh-aw v0.82.5 (#44445). The fix is the same — upgrade and recompile.

The runner pod’s security context has allowPrivilegeEscalation: false. Remove that constraint or adjust your PodSecurity policy to allow privilege escalation in the runner container.

Docker daemon is not accessible in MCP gateway

Section titled “Docker daemon is not accessible in MCP gateway”

The MCP gateway can’t reach the Docker socket. Ensure a Unix socket is available at /var/run/docker.sock on the runner container. For DinD setups where the daemon only exposes a TCP endpoint, share the sidecar’s socket file via a volume:

# In your custom runner pod template
volumes:
- name: dind-sock
emptyDir: {}
# Mount in both runner and DinD sidecar containers
volumeMounts:
- name: dind-sock
mountPath: /var/run