Unlocking Developer Goals: Troubleshooting Reusable Workflows on Self-Hosted GitHub Runners

In the world of continuous integration and continuous delivery (CI/CD), self-hosted GitHub Actions runners offer immense flexibility, allowing teams to meet specific developer goals by leveraging custom environments and hardware. However, even seasoned developers can encounter perplexing issues that halt progress. A recent discussion on the GitHub Community forum highlighted a common, yet easily overlooked, configuration detail that can prevent reusable workflows from dispatching jobs to self-hosted runners.

The original post by rhod9rz detailed a frustrating scenario: a simple GitHub Action workflow executed perfectly on a self-hosted runner, but when the same job was embedded within a reusable workflow called by another, it would simply hang, displaying the message: Waiting for a runner to pick up this job... This issue was particularly puzzling because the reusable workflow functioned flawlessly when targeting GitHub-hosted runners like ubuntu-latest. The self-hosted runner itself was healthy, actively polling, and successfully running non-reusable workflows, indicating no fundamental problem with the runner setup or network connectivity.

Developer troubleshooting a CI/CD workflow issue with self-hosted runners.
Developer troubleshooting a CI/CD workflow issue with self-hosted runners.

The Problem: Reusable Workflows and Self-Hosted Runner Dispatch

The core of the issue lay in how reusable workflows interact with self-hosted runner pools. Here’s a simplified look at the workflows involved:

Simple Action (Works on Self-Hosted)

name: simple
on: 
  workflow_dispatch: {}
jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Run a one-line script
        run: echo Hello, world!

Reusable Workflow Caller (level-1.yml)

name: level-1
on:
  workflow_dispatch: {}
jobs:
  call_validate:
    uses: ./.github/workflows/level-2.yml
    with:
      message: "hello from caller"

Reusable Workflow (level-2.yml)

name: level-2
on:
  workflow_call:
    inputs:
      message:
        required: true
        type: string
jobs:
  test:
    runs-on: self-hosted # This was the problem line
    # runs-on: ubuntu-latest # This worked
    steps:
      - uses: actions/checkout@v4
      - name: Print message
        run: echo "${{ inputs.message }}"

Despite the job being created and evaluated successfully, and the self-hosted runner being available, the job remained in a perpetual waiting state. No errors appeared in the workflow logs, and the runner logs showed no attempt to receive the job.

Visualizing GitHub Actions runner groups and repository assignments.
Visualizing GitHub Actions runner groups and repository assignments.

The Key Observation and Simple Fix

After a period of head-scratching, the original author, rhod9rz, discovered a remarkably simple solution. The problem wasn't with the workflow syntax, runner health, or GitHub Actions' ability to handle reusable workflows. It was a configuration oversight related to runner groups.

The fix was: "The repo in question hadn't been added to the runner group 🤦‍♂️🤦‍♂️"

This highlights a crucial aspect of managing self-hosted runners, especially when working with organizations and multiple repositories. When you set up self-hosted runners, they are typically assigned to a runner group. This group then needs to be explicitly granted access to specific repositories or the entire organization. If a repository is not part of the runner group's access list, jobs originating from that repository, even if they explicitly request a self-hosted label, will not be dispatched to the runners within that group.

Implications for Developer Productivity and Analytics

This incident underscores how seemingly minor configuration details can significantly impact development analytics and overall developer goals. When workflows are stuck, it leads to:

  • Delayed Feedback: Developers wait longer for CI/CD results, slowing down iteration cycles.
  • Wasted Time: Time spent troubleshooting configuration issues rather than developing features.
  • Resource Underutilization: Self-hosted runners sit idle while jobs wait, negating the investment in custom infrastructure.

Ensuring that your self-hosted runner groups are correctly configured with the appropriate repository access is a fundamental step for efficient CI/CD. This is particularly vital when adopting advanced GitHub Actions features like reusable workflows, which are designed to streamline and standardize your build processes across projects.

Conclusion

The GitHub Community discussion serves as a valuable reminder: when troubleshooting self-hosted runner dispatch issues with reusable workflows, always double-check your runner group assignments. A quick verification of repository access can save hours of debugging, ensuring your CI/CD pipelines run smoothly and contribute effectively to achieving your team's developer goals.

|

Dashboards, alerts, and review-ready summaries built on your GitHub activity.

 Install GitHub App to Start
Dashboard with engineering activity trends