Skip to content

Spike: Running Squad's gh-aw workflows on self-hosted / custom runners #1840

Description

@bradygaster

Question

Can GitHub Agentic Workflows (gh-aw) run on a custom/self-hosted runner, and if so, can Squad's gh-aw integration (workflows/squad.md, workflows/squad-implement-worker.md, workflows/shared/squad.md) run there too?

Answer

Yes to both — but Squad's shared bootstrap component has real gaps that need work before it's supported.


Part 1 — gh-aw supports custom runners

Confirmed against githubnext/gh-awdocs/src/content/docs/reference/self-hosted-runners.md and docs/src/content/docs/reference/frontmatter.md.

runs-on: accepts all three GitHub Actions forms:

  • Stringruns-on: self-hosted
  • Arrayruns-on: [self-hosted, linux, x64] (runner must match all labels)
  • Objectruns-on: { group: my-runner-group, labels: [linux, x64] }

Runner scope knobs

Field Controls Default
runs-on: main agent job only ubuntu-latest
runs-on-slim: all framework jobs (activation, pre-activation, safe-outputs, unlock, APM, cache memory) ubuntu-slim
safe-outputs.runs-on: safe-output jobs (takes precedence over runs-on-slim) inherits
safe-outputs.threat-detection.runs-on: threat-detection job agent job's runner
aw.jsonmaintenance.runs_on every job in generated agentics-maintenance.yml ubuntu-slim

Hard requirements

  • Linux only. macos-* ❌ (no nested virtualization for Docker), windows-* ❌.
  • Working Docker daemon. MCP gateway and sandbox run as containers. Runner user must be in the docker group or the socket must be world-readable.
  • Node.js on PATH for framework scripts. gh-aw auto-emits actions/setup-node (Node 24) when it detects a non-standard runner; pin explicitly with runtimes: { node: { version: '24' } }.
  • Egress to api.githubcopilot.com, github.com, ghcr.io, plus anything in network.allowed.
  • ARC/Kubernetes: requires containerMode.type="dind" (type="kubernetes" is unsupported) plus runner.topology: arc-dind in frontmatter. Split-daemon topologies may need GH_AW_DOCKER_SOCK_PATH / GH_AW_DOCKER_SOCK_GID.
  • Rootless pods: if the cluster enforces allowPrivilegeEscalation: false, the Copilot CLI install script needs --rootless so it installs to ~/.local/bin instead of /usr/local/bin.

Air-gapped / private cloud

.github/workflows/aw.json supports action_pins (remap owner/repo@ref → internal mirror) and container_pins (remap image + digest). Applied at compile time and baked into .lock.yml, so workflows never reach public registries at runtime. Neither is available in per-workflow frontmatter.


Part 2 — Squad-specific gaps

Four issues found in Squad's current gh-aw integration:

1. runs-on is not merged from imports

Per gh-aw's imports reference: "runs-on must be set in each workflow — it is not merged from imports."

Squad ships as imports: - shared/squad.md, so runs-on cannot be added to workflows/shared/squad.md. It has to live in each top-level workflow (workflows/squad.md, workflows/squad-implement-worker.md) or in a consumer-authored wrapper.

2. Bootstrap runs on the activation job, not the agent job

workflows/shared/squad.md runs npx --yes @bradygaster/squad-cli@$SQUAD_CLI_VERSION init inside jobs.activation.pre-steps — deliberately, so the npm install happens outside the agent's network firewall.

But activation is a framework job, so it obeys runs-on-slim, not runs-on. Setting runs-on: self-hosted alone leaves the Squad install running on a GitHub-hosted runner. Full isolation requires runs-on-slim: as well — and then the self-hosted runner needs Node on PATH and egress to registry.npmjs.org.

3. Hardcoded artifact action versions break GHES

workflows/shared/squad.md pins actions/upload-artifact@v7.0.1 and actions/download-artifact@v8.0.1 directly in its pre-steps / steps.

gh-aw's GHES compatibility mode ({"ghes": true} in aw.json, or gh aw compile --ghes) downgrades its own artifact steps to upload-artifact@v3.2.2 / download-artifact@v3.1.0, but it does not rewrite action references that Squad hardcoded. On GHES those v4+ actions fail with GHESNotSupportedError, so Squad's squad-state artifact handoff breaks — the agent job never receives .squad/ or .github/agents/squad.agent.md.

This is the most consequential gap and likely needs a fix in the shared component (e.g. aw.json action_pins, or version-selectable artifact steps).

4. Customizations get clobbered on update

gh aw add bradygaster/squad/workflows/squad.md@latest rewrites the source .md, so a hand-added runs-on: is lost on the next update. Consumers need a wrapper workflow instead.


Proposed consumer pattern

A thin wrapper in the consumer repo that remote-imports Squad's shared component, keeping runner config out of the file that gh aw add overwrites:

---
name: Squad
on:
  slash_command:
    name: squad
runs-on: [self-hosted, linux, x64]
runs-on-slim: [self-hosted, linux, x64]
runtimes:
  node:
    version: '24'
imports:
  - bradygaster/squad/workflows/shared/squad.md@<40-char-sha>
safe-outputs:
  runs-on: [self-hosted, linux, x64]
---

Air-gapped consumers additionally add action_pins + container_pins to .github/workflows/aw.json to mirror ghcr.io/github/gh-aw-firewall and the actions/* dependencies.


Validation status

Findings are documentation- and source-validated, not runtime-validated:

  • ✅ gh-aw runner support verified against upstream docs (self-hosted-runners.md, frontmatter.md, imports.md)
  • ✅ Squad's activation-job bootstrap and hardcoded artifact pins read directly from workflows/shared/squad.md
  • ✅ Import-merge limitation confirmed in gh-aw's imports reference
  • ❌ Wrapper workflow has not been compiled with gh aw compile
  • ❌ Nothing executed on an actual self-hosted runner or ARC cluster

Follow-up work

  • Prototype the wrapper workflow and verify it compiles cleanly with gh aw compile
  • Fix the GHES artifact-version gap in workflows/shared/squad.md (blocks GHES/air-gapped adoption)
  • Decide whether Squad's top-level workflows should expose runs-on / runs-on-slim as documented extension points
  • Add a self-hosted-runner section to docs/src/content/docs/guide/gh-aw.md
  • Smoke-test on a real self-hosted Linux runner with Docker (and, separately, on ARC DinD)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    gh-awGitHub Agentic Workflows integrationpriority:p2Next sprintsquadSquad triage inbox — Lead will assign to a membersquad:boosterAssigned to Booster (CI/CD Engineer)triagedIssue has been triagedtype:spikeResearch/investigation — produces a plan, not codewave:3-laterWave 3 — backlog, future

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions