Skip to content

Commit c8f6736

Browse files
CopilotlpcoxCopilot
authored
Add ARC DinD how-to guide for running GitHub Copilot coding agent on self-hosted runners (#44561)
* Initial plan * docs: add ARC DinD guide for Copilot coding agent Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * docs: correct ARC DinD requirements and reference behavior Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * docs: address ARC DinD technical review feedback Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * docs: simplify frontmatter and add GitHub App auth option - Remove sandbox.agent.sudo: false from frontmatter example — it's the default and including it causes confusion about what's required - Show both PAT and GitHub App auth options for runner registration - Link to ARC authentication docs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: add compile step, how-it-works, migration, limitations, troubleshooting - Add 'gh aw compile' step after frontmatter changes - Add 'How it works' section explaining sysroot staging, workspace mount, chroot identity, and artifact consolidation - Add migration guide for users upgrading from manual workarounds - Add known limitations (no-new-privileges, MCP gateway socket) - Add troubleshooting section for common ARC DinD errors - Fix duplicate section numbering Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Co-authored-by: Landon Cox <landon.cox@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 4ce5526 commit c8f6736

3 files changed

Lines changed: 194 additions & 5 deletions

File tree

docs/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ export default defineConfig({
318318
{ label: 'Upgrading Workflows', link: '/guides/upgrading/' },
319319
{ label: 'Using MCPs', link: '/guides/mcps/' },
320320
{ label: 'Network Configuration', link: '/guides/network-configuration/' },
321+
{ label: 'ARC DinD for Copilot Coding Agent', link: '/guides/arc-dind-copilot-agent/' },
321322
{ label: 'OpenTelemetry', link: '/guides/open-telemetry/' },
322323
{ label: 'GitHub Actions Primer', link: '/guides/github-actions-primer/' },
323324
{ label: 'Using at Scale in Organizations', link: '/guides/using-at-scale/' },
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
---
2+
title: How to run GitHub Copilot coding agent on ARC with Docker-in-Docker
3+
description: Configure Actions Runner Controller with Docker-in-Docker so GitHub Copilot coding agent can run on self-hosted Kubernetes runners.
4+
sidebar:
5+
order: 440
6+
---
7+
8+
Use this guide to run GitHub Copilot coding agent on an [Actions Runner Controller (ARC)](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller) runner scale set with Docker-in-Docker (DinD).
9+
10+
## Prerequisites
11+
12+
Before starting, confirm you have a Kubernetes cluster, `helm` and `kubectl` installed, and credentials for runner registration (a GitHub PAT or GitHub App credentials).
13+
14+
> [!IMPORTANT]
15+
> DinD (`containerMode.type="dind"`) is required for GitHub Copilot coding agent on ARC. Kubernetes mode (`containerMode.type="kubernetes"`) is not supported for this setup.
16+
17+
## 1. Install the ARC controller
18+
19+
```bash
20+
helm install arc \
21+
--namespace "arc-system" --create-namespace \
22+
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
23+
```
24+
25+
## 2. Create the runner namespace and auth secret
26+
27+
Create the namespace and a Kubernetes secret with your runner registration credentials. You can use either a GitHub PAT or GitHub App credentials:
28+
29+
```bash
30+
kubectl create ns arc-runners
31+
32+
# Option A: Personal access token
33+
kubectl create secret generic arc-runner-secret \
34+
--namespace=arc-runners \
35+
--from-literal=github_token=<YOUR_PAT>
36+
37+
# Option B: GitHub App (recommended for production)
38+
kubectl create secret generic arc-runner-secret \
39+
--namespace=arc-runners \
40+
--from-literal=github_app_id=<APP_ID> \
41+
--from-literal=github_app_installation_id=<INSTALL_ID> \
42+
--from-literal=github_app_private_key=<PRIVATE_KEY>
43+
```
44+
45+
See [Authenticating to the GitHub API](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api) for details on each option.
46+
47+
## 3. Install a runner scale set in DinD mode
48+
49+
```bash
50+
helm install "arc-runner-set" \
51+
--namespace "arc-runners" --create-namespace \
52+
--set githubConfigUrl="https://github.com/<OWNER>/<REPO>" \
53+
--set githubConfigSecret="arc-runner-secret" \
54+
--set containerMode.type="dind" \
55+
--set-json 'template.spec.containers=[{
56+
"name": "runner",
57+
"image": "ghcr.io/actions/actions-runner:latest",
58+
"command": ["/home/runner/run.sh"],
59+
"securityContext": {
60+
"capabilities": {
61+
"add": ["NET_ADMIN"]
62+
}
63+
}
64+
}]' \
65+
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set
66+
```
67+
68+
`NET_ADMIN` is required on the **runner container** so AWF can apply host-level `iptables` rules to the `DOCKER-USER` chain for egress filtering.
69+
70+
When `containerMode.type="dind"` is enabled, ARC configures the DinD sidecar in privileged mode by default so the Docker daemon can run. If you use a custom pod template, ensure you do not remove that privileged setting.
71+
72+
## 4. Verify the runner is online
73+
74+
Open `https://github.com/<OWNER>/<REPO>/settings/actions/runners` (or the organization-level runners page) and confirm the `arc-runner-set` runner is online.
75+
76+
## 5. Target the runner set from a workflow
77+
78+
Set your workflow frontmatter to use the runner scale set label and ARC DinD topology:
79+
80+
```aw
81+
---
82+
on: issues
83+
runs-on: arc-runner-set
84+
runner:
85+
topology: arc-dind
86+
---
87+
```
88+
89+
`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.
90+
91+
After editing the frontmatter, recompile the lock file:
92+
93+
```bash
94+
gh aw compile
95+
```
96+
97+
Commit both the `.md` workflow file and the generated `.lock.yml` file.
98+
99+
## 6. How it works
100+
101+
When compiled workflows detect a `tcp://` value in `DOCKER_HOST` (set automatically by ARC DinD), a runtime probe activates ARC DinD handling:
102+
103+
- **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.
104+
- **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.
105+
- **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.
106+
- **Artifact consolidation** — agent output files are consolidated under `${{ runner.temp }}/gh-aw/` before upload so downstream jobs (detection, safe-outputs) can find them.
107+
108+
## 7. Required versions
109+
110+
Use versions at or above these minimums:
111+
112+
| Component | Minimum version | Why |
113+
| --- | --- | --- |
114+
| `gh-aw` | `v0.82.5` | Includes ARC DinD workspace and detection fixes. |
115+
| AWF (`agentic-workflow-firewall`) | `v0.27.22` | Includes DinD squid log permission fixes. |
116+
117+
## Required and optional configuration
118+
119+
| Item | Required? | Notes |
120+
| --- | --- | --- |
121+
| DinD container mode | **Yes** | GitHub Copilot coding agent needs a Docker daemon in the runner pod. |
122+
| `NET_ADMIN` capability | **Yes** | Required on the runner container so AWF can manage host-level `DOCKER-USER` `iptables` rules. |
123+
| `ghcr.io/actions/actions-runner:latest` | Recommended | Use the official runner image, or a compatible custom image with equivalent runner requirements. |
124+
| Runner user | **Yes** | Non-root runner users are supported, but `sudo` must remain available on the runner host for AWF setup operations. |
125+
| DinD sidecar privilege | **Yes** | ARC DinD mode configures a privileged sidecar for Docker daemon operation. |
126+
| Shared work volume (`/home/runner/_work`) | **Yes** | Runner and Docker daemon share this volume in ARC DinD mode, so workspace mounts work without host path translation. |
127+
| Specific Kubernetes distribution | **No** | Any conformant cluster works (for example minikube, EKS, AKS, or GKE). |
128+
| Specific namespace names | **No** | `arc-system` and `arc-runners` are conventions only. |
129+
130+
## Upgrading from manual workarounds
131+
132+
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.
133+
134+
To migrate:
135+
136+
1. Remove any `pre-agent-steps`, `resources`, or `safe-outputs.threat-detection.steps` blocks that were workarounds for ARC DinD.
137+
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`.
138+
3. Remove `sandbox.agent.mounts` entries that staged files for the DinD daemon.
139+
4. Add `runner.topology: arc-dind` to frontmatter.
140+
5. Run `gh aw compile` and commit the updated lock file.
141+
142+
## Known limitations
143+
144+
- **`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.
145+
- **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](https://github.com/github/gh-aw/issues/44251) for tracking.
146+
147+
## Troubleshooting
148+
149+
### Agent reports empty workspace
150+
151+
The agent sees no files and exits with a no-op message. This was fixed in gh-aw v0.82.5. Upgrade and recompile:
152+
153+
```bash
154+
gh aw upgrade
155+
gh aw compile
156+
```
157+
158+
### Detection job fails with `spawn /usr/local/bin/copilot ENOENT`
159+
160+
The threat-detection job can't find the Copilot binary. This was fixed in gh-aw v0.82.5 ([#44445](https://github.com/github/gh-aw/pull/44445)). The fix is the same — upgrade and recompile.
161+
162+
### `sudo: The "no new privileges" flag is set`
163+
164+
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.
165+
166+
### `Docker daemon is not accessible` in MCP gateway
167+
168+
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:
169+
170+
```yaml
171+
# In your custom runner pod template
172+
volumes:
173+
- name: dind-sock
174+
emptyDir: {}
175+
# Mount in both runner and DinD sidecar containers
176+
volumeMounts:
177+
- name: dind-sock
178+
mountPath: /var/run
179+
```
180+
181+
## Related documentation
182+
183+
- [Self-Hosted Runners](/gh-aw/reference/self-hosted-runners/)
184+
- [ARC Helm charts](https://github.com/actions/actions-runner-controller/tree/master/charts)

docs/src/content/docs/reference/self-hosted-runners.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ For these reasons, a non-sudo mode is not supported, including ARC configuration
1919

2020
## ARC with Docker-in-Docker (DinD)
2121

22-
Actions Runner Controller (ARC) deployments that use a Docker-in-Docker sidecar split the runner container and the Docker daemon container across separate filesystems, so bind mounts constructed from the runner's perspective fail inside the daemon.
22+
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](/gh-aw/guides/arc-dind-copilot-agent/).
2323

24-
`gh aw compile` emits a runtime probe in generated workflows that inspects `DOCKER_HOST` and appends `--docker-host-path-prefix /tmp/gh-aw` to the AWF invocation when the value matches `tcp://localhost:<port>` or `tcp://127.0.0.1:<port>`. No workflow-level configuration is required.
24+
Actions Runner Controller (ARC) deployments that use a Docker-in-Docker sidecar split the runner container and the Docker daemon container across separate filesystems.
2525

26-
The probe is gated on AWF `v0.25.43` or newer. Workflows pinned to an older AWF version, or running on GitHub-hosted runners (where `DOCKER_HOST` is unset or points at a Unix socket), are unaffected.
26+
Set `runner.topology: arc-dind` in workflow frontmatter for this environment.
27+
Compiled workflows emit a runtime probe that inspects `DOCKER_HOST`.
28+
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.
29+
30+
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`.
2731

2832
## runs-on formats
2933

@@ -171,8 +175,8 @@ A working Docker daemon is required. The MCP gateway and sandbox run as containe
171175

172176
### Filesystem
173177

174-
- **Use `RUNNER_TEMP` for 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 `/tmp` because it can persist across jobs. The `/tmp/gh-aw` prefix is reserved for gh-aw/AWF ARC DinD path rewriting. `actions/setup` resets `/tmp/gh-aw` at job start, and your normal runner `/tmp` cleanup policy should handle stale data from interrupted jobs.
175-
- **No root or sudo assumption.** The runner user may not have root or `sudo` access (except for the initial iptables setup, which requires `sudo`). Tool installs, file operations, and sandbox setup should work as the unprivileged runner user.
178+
- **Use `RUNNER_TEMP` for 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 `/tmp` because it can persist across jobs.
179+
- **No root assumption.** Tool installs, file operations, and sandbox setup should run as the unprivileged runner user, but host-level AWF setup requires `sudo` support on the runner host.
176180
- **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.
177181
- **No hardcoded `HOME` paths.** The runner's home directory may not be `/home/runner`. Use `$HOME` or `$RUNNER_TEMP` instead of hardcoded paths.
178182

0 commit comments

Comments
 (0)