GitHub App Adoption

GitHub App Adoption

GloriousFlywheel uses a GitHub App to authenticate ARC (Actions Runner Controller) with GitHub. This guide explains how to install the app on your organization and configure runner access.

What is GloriousFlywheel?

GloriousFlywheel is a GitHub App (ID 2953466) that enables self-hosted GitHub Actions runners via ARC. It listens for workflow_job webhook events and scales runner pods on demand.

Install on a New Organization

  1. Navigate to the GitHub App settings page
  2. Click Install and select the target organization
  3. Choose All repositories or select specific repos
  4. Approve the installation

Required Permissions

Permission Scope Access
Self-hosted runners Organization Read & Write
Metadata Repository Read-only
Actions Repository Read-only

Webhook Events

The app subscribes to workflow_job events. These trigger runner pod creation when a job matches a self-hosted runner label.

Kubernetes Secret Setup

ARC authenticates using a Kubernetes secret containing the GitHub App credentials. This secret must exist in both namespaces:

  • arc-systems — used by the ARC controller for API authentication
  • arc-runners — used by runner scale sets for registration
# Create the secret in both namespaces
for ns in arc-systems arc-runners; do
  kubectl create secret generic github-app-secret \
    --namespace="$ns" \
    --from-literal=github_app_id=2953466 \
    --from-literal=github_app_installation_id=<INSTALLATION_ID> \
    --from-file=github_app_pem=<PATH_TO_PEM_FILE>
done

Runner Group Configuration

The default runner group must allow public repositories if you want self-hosted runners available to public repos:

gh api -X PATCH /orgs/<ORG>/actions/runner-groups/1 \
  -f allows_public_repositories=true

Without this, workflows in public repos will fail to match self-hosted runner labels.

Deploy the ARC Stack

export KUBECONFIG=~/.kube/kubeconfig-honey.yaml
export KUBE_CONTEXT=honey
cp tofu/stacks/arc-runners/terraform.tfvars.example tofu/stacks/arc-runners/dev.tfvars

ENV=dev just tofu-preflight arc-runners
ENV=dev just tofu-init arc-runners
ENV=dev just tofu-plan arc-runners
ENV=dev just tofu-apply arc-runners

This deploys:

  • ARC controller in arc-systems
  • Three runner scale sets (gh-nix, gh-docker, gh-dind) in arc-runners
  • Optional additive scale sets from dev-extra-runner-sets.tfvars, including the current heavy Nix canary tinyland-nix-heavy

For the current Tinyland rollout, honey is the only physical cluster target. The Civo cluster has been decommissioned (April 2026).

Composite Actions

GloriousFlywheel provides composite actions that auto-configure cache endpoints on self-hosted runners:

Action Description
setup-flywheel Detect runner environment and configure cache/runtime hints
nix-job Bootstrap Nix explicitly and run a Nix job with cache hints
docker-job Standard CI job with Bazel cache

Usage

jobs:
  build:
    runs-on: tinyland-nix
    steps:
      - uses: actions/checkout@v4
      - uses: tinyland-inc/GloriousFlywheel/.github/actions/nix-job@main
        with:
          command: nix build .#default
          push-cache: "true"

setup-flywheel configures cache/runtime hints on self-hosted runners. It does not install Nix by itself. Use nix-job or run DeterminateSystems/determinate-nix-action@v3 before raw nix commands.

Current scaling boundary:

  • ARC can scale the number of runner pods up and down
  • ARC does not automatically increase the CPU or memory limit of one runner pod
  • the baseline tinyland-nix lane is still an 8Gi memory-limit runner

Workflow Examples

Simple Nix Build

name: Build
on: [push, pull_request]

jobs:
  build:
    runs-on: tinyland-nix
    steps:
      - uses: actions/checkout@v4
      - uses: DeterminateSystems/determinate-nix-action@v3
      - uses: tinyland-inc/GloriousFlywheel/.github/actions/setup-flywheel@main
      - run: nix build

Docker Build

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: tinyland-docker
    steps:
      - uses: actions/checkout@v4
      - run: make test

  build-image:
    runs-on: tinyland-dind
    needs: test
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t myapp .

Multi-Org / Cross-Repo Runners

By default, ARC runner scale sets register at the organization level — every repo in that org can use runs-on: tinyland-nix. But personal repos or repos in other orgs can’t reach those runners.

The extra_runner_sets variable lets you deploy additional scale sets scoped to a different org or a single repository, all on the same cluster and sharing the same ARC controller.

How githubConfigUrl Scoping Works

URL pattern Scope
https://github.com/ORG All repos in the org
https://github.com/OWNER/REPO Single repository only

Steps

  1. Install GloriousFlywheel on the target GitHub account/org (Settings → Applications → Install).

  2. Create a K8s secret for the new installation:

    for ns in arc-systems arc-runners; do
      kubectl create secret generic github-app-secret-chapel \
        --namespace="$ns" \
        --from-literal=github_app_id=2953466 \
        --from-literal=github_app_installation_id=<NEW_INSTALLATION_ID> \
        --from-file=github_app_pem=<PATH_TO_PEM_FILE>
    done
  3. Add an extra_runner_sets entry in your tfvars:

    extra_runner_sets = {
      chapel-nix = {
        github_config_url    = "https://github.com/Jesssullivan/chapel"
        github_config_secret = "github-app-secret-chapel"
        runner_label         = "chapel-nix"
        runner_labels        = ["self-hosted", "nix", "linux"]
        runner_type          = "nix"
        max_runners          = 3
        cpu_limit            = "4"
        memory_limit         = "8Gi"
      }
    }

    For a heavier Nix lane, use the same pattern with a distinct label and a larger static envelope:

    extra_runner_sets = {
      tinyland-nix-heavy = {
        github_config_url    = "https://github.com/tinyland-inc"
        github_config_secret = "github-app-secret"
        runner_label         = "tinyland-nix-heavy"
        runner_labels        = ["self-hosted", "nix", "linux"]
        runner_type          = "nix"
        max_runners          = 2
        cpu_limit            = "8"
        memory_limit         = "16Gi"
      }
    }

    This is the current GloriousFlywheel-style answer for memory-heavy Nix jobs. ARC will scale the number of tinyland-nix-heavy pods, but it will not resize tinyland-nix itself on demand.

    For browser-driven repos that already queue on a repo-owned label contract, keep the workflow labels stable and swap the backing infrastructure instead:

    extra_runner_sets = {
      massageithaca-browser = {
        github_config_url    = "https://github.com/Jesssullivan/MassageIthaca"
        github_config_secret = "github-app-secret-massageithaca"
        runner_label         = "massageithaca"
        runner_labels        = ["self-hosted", "Linux", "X64", "honey"]
        runner_type          = "docker"
        runner_image         = "ghcr.io/tinyland-inc/actions-runner-browser:latest"
        max_runners          = 3
      }
    }

    That pattern preserves a repo’s current runs-on contract while replacing long-lived pet runners with ARC-managed pods. If the existing GitHub App or PAT secret is scoped too narrowly for the new repo, create a dedicated secret first instead of reusing a secret that only covers a different repository.

    Important boundary:

    • prefer a dedicated GitHub App installation and secret for active repo-scoped lanes on Jesssullivan/* or other non-org surfaces
    • treat PAT-backed secrets as a temporary compatibility bridge only
    • a shared PAT-backed secret burns one user’s REST core budget, so one hot repo or one noisy automation loop can strand every repo-scoped lane that reuses that PAT
    • if listener logs show failed to get runner registration token with API rate limit exceeded for user ID ..., the fix is app auth, not more runner capacity
  4. Apply — the new scale set appears in arc-runners alongside the existing ones:

    ENV=dev just tofu-plan arc-runners
    ENV=dev just tofu-apply arc-runners
  5. Use the label in a workflow:

    jobs:
      build:
        runs-on: tinyland-nix-heavy
        steps:
          - uses: actions/checkout@v4
          - uses: DeterminateSystems/determinate-nix-action@v3
          - uses: tinyland-inc/GloriousFlywheel/.github/actions/setup-flywheel@main
          - run: nix build

See Also

GloriousFlywheel