Runner groups
A runner group starts a fresh runner for each run. Set one up with GitHub Actions or run one in your Kubernetes cluster.
GitHub Actions
GantryCD can dispatch a workflow in a GitHub repository. GitHub then provides the runner.
This runner group needs its own GitHub App. A source control integration is a different purpose with different access, and GantryCD can dispatch runners from a GitHub repository even when your stacks come from somewhere else.
-
Create a GitHub App with these repository permissions:
Permission Access Why Metadata Read Always. GitHub adds this permission. Actions Read and write Write to dispatch the workflow, read to follow the run it created. Nothing else is needed: no event subscriptions, no webhook, no callback URL, and no user authorization. Without Actions: read and write, every dispatch fails with
403 Resource not accessible by integration. -
Note the App ID, generate a private key, and install the App on the repository that will hold the workflow.
-
Expand the workflow template below and copy it to
.github/workflows/gantrycd-runner.yamlin that repository. -
In GantryCD, open Runner groups → Create Runner Group and choose GitHub Actions.
-
Enter:
- the repository as
owner/name; - the workflow file and ref;
- the App ID and private key;
- the maximum number of concurrent runs.
- the repository as
Workflow template
name: GantryCD Runner
run-name: gantrycd-${{ inputs.correlation-id }}
on:
workflow_dispatch:
inputs:
assignment-token:
required: true
gantrycd-url:
required: true
correlation-id:
required: true
runner-version:
required: true
env:
# renovate: datasource=github-releases depName=tofuutils/tenv
TENV_VERSION: v4.12.2
jobs:
run:
# Change this label to choose another GitHub runner.
runs-on: ubuntu-slim
timeout-minutes: 75
steps:
- name: Resolve architecture and tool paths
env:
HOST_ARCH: ${{ runner.arch }}
run: |
set -euo pipefail
case "${HOST_ARCH}" in
X64) arch=amd64; tenv_arch=x86_64 ;;
ARM64) arch=arm64; tenv_arch=arm64 ;;
*) echo "unsupported runner.arch: ${HOST_ARCH}" >&2; exit 1 ;;
esac
TOOLS_DIR="${HOME}/.cache/gantrycd-tools"
mkdir -p "${TOOLS_DIR}/tenv"
printf '%s\n' "${TOOLS_DIR}/tenv" >> "${GITHUB_PATH}"
{
echo "ARCH=${arch}"
# Not TENV_ARCH: tenv reads that name as its own arch override and
# would then look for a tofu asset called linux_x86_64.
echo "TENV_PKG_ARCH=${tenv_arch}"
echo "TOOLS_DIR=${TOOLS_DIR}"
echo "TENV_ROOT=${HOME}/.tenv"
} >> "${GITHUB_ENV}"
- name: Install cosign
run: |
set -euo pipefail
curl -fsSL -o cosign \
"https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-${ARCH}"
sudo install -m 0755 cosign /usr/local/bin/cosign
rm cosign
- name: Download runner binary
env:
RUNNER_VERSION: ${{ inputs.runner-version }}
run: |
set -euo pipefail
curl -fsSL -o runner.tar.gz \
"https://artifacts-axj4rmt8.gantrycd.io/releases/${RUNNER_VERSION}/runner-${RUNNER_VERSION}-linux-${ARCH}.tar.gz"
tar -xzf runner.tar.gz
chmod +x runner
- name: Cache the OpenTofu runtime
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/gantrycd-tools/tenv
~/.tenv
key: ${{ runner.os }}-${{ runner.arch }}-tofu-runtime-${{ env.TENV_VERSION }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-tofu-runtime-${{ env.TENV_VERSION }}-
- name: Install tenv
run: |
set -euo pipefail
if [ -x "${TOOLS_DIR}/tenv/tenv" ]; then
echo "tenv ${TENV_VERSION} restored from cache"
else
curl -fsSL "https://github.com/tofuutils/tenv/releases/download/${TENV_VERSION}/tenv_${TENV_VERSION}_Linux_${TENV_PKG_ARCH}.tar.gz" \
| tar -xz -C "${TOOLS_DIR}/tenv"
fi
command -v tenv
- name: Run the ephemeral runner
env:
BACKEND_URL: ${{ inputs.gantrycd-url }}
EPHEMERAL_RUNNER_JWT: ${{ inputs.assignment-token }}
run: ./runner
The template accepts the assignment from GantryCD, downloads the matching
runner version, and starts it. Keep its four workflow inputs and its run-name
unchanged.
Keep the template current
The workflow pins the tools it installs, so those pins go stale. Let Renovate raise the pull requests:
-
The
actions/cachedigest and its# v6.1.0comment are updated by Renovate’s built-in GitHub Actions support, with no extra configuration. -
TENV_VERSIONcarries a# renovate: datasource=github-releases depName=tofuutils/tenvannotation. Comments like it are read by thecustomManagers:githubActionsVersionspreset, whichconfig:recommendeddoes not include. Extend it in the repository’srenovate.jsonand every*_VERSIONpin you annotate the same way is covered:{ "extends": ["config:recommended", "customManagers:githubActionsVersions"] }
The runner version is not a pin: GantryCD passes it as a workflow input so the runner always matches the backend release.
GitHub Actions receives no runner spec overrides from a stack. The runner label
(runs-on), container image, and other compute options are set in the workflow.
Self-hosted with Helm
The runner group Helm chart runs a launcher in your cluster. The launcher creates one short-lived Kubernetes Pod for each run.
First, open Runner groups → Create Runner Group, choose Self-hosted, and save the group ID and private key. The private key is shown only once.
Create a namespace and store the key:
kubectl create namespace acme
kubectl -n acme create secret generic acme-runners-key \
--from-file=private-key=/path/to/private-key.pem
Install one runner group in that namespace:
helm install acme-runners \
oci://ghcr.io/gantrycd/helm/gantrycd-runner-group-kubernetes \
--version vX.Y.Z \
--namespace acme \
--set groupID=<group-id> \
--set backendURL=http://gantrycd.gantrycd.svc:8080 \
--set privateKey.existingSecret=acme-runners-key
Use a values file for shared settings:
maxConcurrent: 4
ephemeralRunner:
serviceAccount: gantrycd-runner
serviceAccountCreate: true
cpu: "1"
memory: 1Gi
cpu and memory set both the request and limit for every runner Pod. A stack
can override each value separately.
The backend URL must be reachable from the cluster. The service account is the identity used by the short-lived runner Pods, so attach the cloud access they need to it.
View all chart values for your version:
helm show values \
oci://ghcr.io/gantrycd/helm/gantrycd-runner-group-kubernetes \
--version vX.Y.Z
Use runner group selectors to route stacks and pass runner spec overrides supported by the selected group.