GitHub Actions
At a glance — the backend-dispatched, zero-infrastructure option. GantryCD calls
workflow_dispatchin your repo for each run, and the workflow drives the rest of the protocol. There’s no launcher process for you to host.
Configure
Set up once:
- Create a GitHub App and install it on the repo you’ll dispatch to. Note its
App ID and generate a private key (
.pem) — you’ll register both on the runner group. Grant exactly one permission: Actions: Read and write. - Commit the runner workflow to
.github/workflows/gantrycd-runner.yaml— copy it verbatim from the Workflow template. It pins the runner action and needs no edits across GantryCD upgrades, but it must declare all threeworkflow_dispatchinputs the dispatcher sends —assignment-token,gantrycd-url, andcorrelation-id— and echo the last into the run name asrun-name: gantrycd-${{ inputs.correlation-id }}. A workflow missingcorrelation-idis rejected by GitHub on every dispatch — see Troubleshooting. - Create a
github-actionsrunner group (under Org settings → Runner groups) pointing at the repo, the workflow file, and the App’s ID + private key. The signing keypair the runner uses is generated and held by GantryCD — you never see it.
Permissions
The runner group needs one fine-grained repository permission on its GitHub App:
| Permission | Access | Used for |
|---|---|---|
| Actions | Read and write | Read to track and re-discover dispatched runs; write to call workflow_dispatch. |
You can reuse your source-control GitHub App — just add Actions: Read and write to it — or use a dedicated App. Either way, register the App ID + private key on the runner group itself: runner-group credentials are stored separately from the SCM integration’s, even when it’s literally the same App.
A launcher driving this from outside the backend uses these env vars:
| Variable | Required | Notes |
|---|---|---|
GITHUB_TOKEN | Yes | token used to dispatch workflows |
GITHUB_OWNER | Yes | owner / org |
GITHUB_REPO | Yes | repository name |
GITHUB_WORKFLOW_FILE | No | workflow filename; defaults to gantrycd-runner.yaml |
GITHUB_API_URL | No | https://api.github.com |
How it behaves
- GantryCD holds the runner signing key server-side (you never see it), mints a
per-run token, and calls
workflow_dispatch. The workflow reads the GantryCD URL from the dispatch input, so the same file survives backend URL changes. - The group shows connected while GantryCD’s dispatcher is healthy.
Note: these runners run off your network. Point GantryCD’s storage at a publicly reachable endpoint so they can upload state and logs — see Storage.
Run a prebuilt binary
There’s a workflow template that downloads a prebuilt runner from a tagged
gantrycd release, installs tenv, and runs the binary directly. See
Workflow template: prebuilt binary.
Troubleshooting
Dispatch fails with 422 Unexpected inputs provided (GitHub reports
correlation-id as an unexpected input). Your committed workflow predates the
correlation-id input. GantryCD sends correlation-id — an opaque
<group-id>/<run-id> — on every workflow_dispatch, so the workflow has to
declare it as an input (alongside assignment-token and gantrycd-url) or GitHub
rejects the call before the run ever starts.
correlation-id is how GantryCD finds the run it started: the list-runs API returns
a run’s run-name but not its dispatch inputs, so the workflow surfaces it via
run-name: gantrycd-${{ inputs.correlation-id }}. That run-name lets the dispatcher
both poll a run to completion and re-discover in-flight runs after a backend
restart — which is what makes restarting the backend safe (it re-attaches to
runs already in progress instead of orphaning them). The runner action itself never
reads correlation-id; it exists purely for this correlation.
Fix: re-copy the current
Workflow template
(or the prebuilt-binary variant).
Both declare all three inputs and set run-name.
Internals
The dispatcher loop, the workflow template, and the App setup are in GitHub-Actions runner groups.