Skip to content

Support self-hosted runners whose service account/home is not /home/runner #27260

@stefankrzyz

Description

@stefankrzyz

Summary

gh-aw currently assumes GitHub Copilot CLI state and MCP config live under /home/runner/.copilot, and some setup paths also assume ownership should be reset to runner:runner.

That works on GitHub-hosted runners, but it makes self-hosted Linux runners harder to use when the runner service account and home directory do not match the GitHub-hosted default layout.

Today the workaround is to create a compatibility shim that manufactures /home/runner/.copilot and bridges the actual runner account back to that location. It would be much easier to operate gh-aw on self-hosted infrastructure if upstream supported a configurable or runtime-derived Copilot config path instead of hardcoding /home/runner.

Analysis

From tracing the current implementation, the /home/runner assumption appears in multiple places:

Hardcoded path / ownership assumptions

  • actions/setup/sh/install_copilot_cli.sh

    • sets COPILOT_DIR="/home/runner/.copilot"
    • resets ownership with sudo chown -R runner:runner "$COPILOT_DIR"
  • actions/setup/sh/start_mcp_gateway.sh

    • checks for and writes to /home/runner/.copilot
  • actions/setup/js/convert_gateway_config_copilot.cjs

    • writes MCP config to /home/runner/.copilot/mcp-config.json
  • pkg/workflow/copilot_engine_execution.go

    • sets:
      • GH_AW_MCP_CONFIG=/home/runner/.copilot/mcp-config.json
      • XDG_CONFIG_HOME=/home/runner
  • pkg/workflow/copilot_mcp.go

    • emits workflow steps that create /home/runner/.copilot
    • renders MCP config into /home/runner/.copilot/mcp-config.json

There is also mirrored setup logic in github/gh-aw-actions, so any fix here likely needs to stay aligned with the synced action scripts.

Current behavior

On self-hosted runners, gh-aw currently expects:

  • a usable /home/runner/.copilot directory
  • MCP config at /home/runner/.copilot/mcp-config.json
  • ownership repair back to a literal runner:runner

That means environments that use a different service account/home path need extra compatibility work even though the actual requirement is just “Copilot CLI needs a writable config/state directory”.

Expected behavior

gh-aw should work on self-hosted Linux runners without requiring:

  • a literal runner user
  • a literal /home/runner home directory
  • compatibility symlinks just to satisfy internal path assumptions

It should either:

  1. derive the Copilot config/state location from the runtime environment, or
  2. support a documented override while preserving /home/runner as the default for backward compatibility

Proposed implementation plan

Please implement the following changes.

1. Centralize Copilot config path resolution

Create a single source of truth for the Copilot config/state path used by Copilot-related workflow generation and setup scripts.

Suggested behavior:

  • default remains /home/runner/.copilot/mcp-config.json for backward compatibility
  • allow override via environment/config rather than hardcoding everywhere
  • derive the Copilot directory from the config file path instead of duplicating string literals

A good target is to make GH_AW_MCP_CONFIG the canonical config file path and derive:

  • Copilot config dir = dirname(GH_AW_MCP_CONFIG)
  • Copilot home / XDG config root = parent of .copilot

This keeps one canonical value instead of scattering /home/runner/.copilot across Go, shell, and JS.

2. Remove hardcoded runner:runner ownership repair

Update Copilot CLI install/setup scripts so ownership repair uses the current runtime user/group instead of assuming a literal runner account.

For example, the shell logic should use the executing account’s identity rather than:

  • runner:runner

The fix should preserve the original intent:

  • repeated AWF/chroot runs may leave .copilot root-owned
  • the directory should be repaired back to the account that will run Copilot CLI next

3. Update setup scripts to honor the resolved path

Update the following paths to use the centralized resolved value instead of /home/runner/... literals:

  • actions/setup/sh/install_copilot_cli.sh
  • actions/setup/sh/start_mcp_gateway.sh
  • actions/setup/sh/convert_gateway_config_copilot.sh (if still relevant)
  • actions/setup/js/convert_gateway_config_copilot.cjs

These scripts should:

  • write MCP config to the resolved path
  • create the resolved directory if needed
  • avoid checking for /home/runner/.copilot directly when determining Copilot mode

4. Update workflow generation in Go

Update the Copilot workflow generation logic so it does not bake /home/runner into generated workflows unnecessarily.

Relevant areas:

  • pkg/workflow/copilot_engine_execution.go
  • pkg/workflow/copilot_mcp.go

Goals:

  • preserve today’s default behavior for existing users
  • allow self-hosted environments to override without patching generated workflows by hand
  • avoid setting XDG_CONFIG_HOME=/home/runner unconditionally if an override is present

5. Add regression tests

Please add tests covering both the default path and an overridden/non-default path.

Suggested coverage:

Go tests

  • generated workflow still defaults to /home/runner/... when no override is set
  • generated workflow uses the resolved/overridden path when provided
  • no tests depend on a literal runner user

Script tests

  • config conversion writes to the resolved location
  • ownership repair targets the current runtime identity rather than runner:runner
  • scripts work when the Copilot directory is not under /home/runner

6. Update docs

Please update the relevant documentation to explain self-hosted runner behavior and any new override/default behavior.

Suggested docs areas:

  • self-hosted runner guidance
  • Copilot/MCP troubleshooting docs
  • any docs that currently present /home/runner/.copilot/mcp-config.json as the only supported location

The docs should make it clear that:

  • /home/runner remains the default for backward compatibility if that is retained
  • self-hosted users can use a different runtime account/home layout
  • a compatibility shim should not be required when the path is configured correctly

7. Keep gh-aw-actions sync in mind

Because the setup scripts are mirrored/synced into github/gh-aw-actions, please ensure the final implementation path accounts for that sync process so the released action behavior stays consistent with gh-aw.

Acceptance criteria

This issue is complete when all of the following are true:

  • gh-aw no longer requires a literal runner user for Copilot setup
  • gh-aw no longer requires /home/runner to exist on self-hosted runners
  • Copilot MCP config path can be overridden or derived from runtime configuration
  • default behavior remains backward compatible for existing GitHub-hosted usage
  • tests cover both default and non-default path/user scenarios
  • docs explain the behavior clearly for self-hosted runners

Non-goals

This issue is not asking to change the default GitHub-hosted runner behavior.

It is specifically asking to remove the hard requirement on the GitHub-hosted-style user/home layout so self-hosted runners can work without compatibility shims.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions