Skip to main content
The GitHub Actions job agent triggers workflow dispatch events to execute your deployments. This is ideal for teams already using GitHub Actions for CI/CD.

How It Works

  1. Ctrlplane creates a job and dispatches it to GitHub
  2. GitHub triggers your workflow with workflow_dispatch
  3. Your workflow fetches job context (version, environment, resource)
  4. Your workflow executes the deployment
  5. GitHub sends a workflow_run webhook event to Ctrlplane as the run progresses
  6. Ctrlplane automatically updates the job status (in progress, successful, failure, etc.)

Prerequisites

  • A GitHub App registered in your organization (see GitHub App Setup)
  • The GitHub App installed on the repositories you want to dispatch workflows to
  • A workflow file with workflow_dispatch trigger in each target repository
  • Ctrlplane’s workspace engine configured with the GitHub App credentials (see Server Configuration)

GitHub App Setup

Ctrlplane authenticates with GitHub using a GitHub App. The workspace engine generates a JWT from the App’s private key and exchanges it for a short-lived installation token to dispatch workflows.

Creating a GitHub App

  1. Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App
  2. Fill in the required fields:
    • GitHub App name: e.g. ctrlplane-bot
    • Homepage URL: your Ctrlplane instance URL
  3. Under Webhook:
    • Webhook URL: https://<your-ctrlplane-domain>/api/github/webhook
    • Webhook secret: generate a strong secret (e.g. openssl rand -hex 32)
    • Subscribe to the Workflow runs event
  4. Under Permissions, grant the following Repository permissions:
    • Actions: Read and write (required to dispatch workflow events)
    • Contents: Read-only (required to access workflow files)
    • Metadata: Read-only (granted by default)
  5. Click Create GitHub App

Gathering Credentials

After creating the App, collect the following values:
CredentialWhere to find it
App IDGitHub App settings page → App ID (numeric)
Private KeyGitHub App settings page → Private keysGenerate a private key (PEM file)
Client IDGitHub App settings page → Client ID
Client SecretGitHub App settings page → Generate a new client secret
Webhook SecretThe secret you entered when creating the App (step 3 above)

Installing the App

  1. From the GitHub App settings page, click Install App in the left sidebar
  2. Choose the organization or account to install on
  3. Select the repositories the App should have access to
  4. Note the Installation ID — you can find it in the URL after installation: https://github.com/settings/installations/INSTALLATION_ID
You can also retrieve it via the GitHub API:
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://api.github.com/orgs/YOUR_ORG/installations

Server Configuration

The workspace engine (and the API server) need the GitHub App credentials to authenticate with the GitHub API. These are provided via environment variables.

Environment Variables

VariableServiceRequiredDescription
GITHUB_BOT_APP_IDWorkspace EngineYesThe numeric App ID from your GitHub App settings
GITHUB_BOT_PRIVATE_KEYWorkspace EngineYesThe PEM-encoded private key generated for the GitHub App
GITHUB_WEBHOOK_SECRETAPI ServerYesThe webhook secret used to verify incoming GitHub webhook payloads
The workspace engine uses GITHUB_BOT_APP_ID and GITHUB_BOT_PRIVATE_KEY to generate JWTs for authenticating with the GitHub API and dispatching workflows. If either is missing, dispatching will fail with a GitHub bot not configured error. The API server uses GITHUB_WEBHOOK_SECRET to verify incoming workflow_run webhook events from GitHub. When configured, Ctrlplane automatically updates job status as workflows progress — no manual status reporting is needed in your workflow.

Helm Chart Configuration

When deploying with the Ctrlplane Helm chart, configure the GitHub App credentials in your values.yaml under global.integrations.github.bot:
global:
  integrations:
    github:
      url: "https://github.com"
      bot:
        appId: "123456"
        privateKey: |
          -----BEGIN RSA PRIVATE KEY-----
          ...
          -----END RSA PRIVATE KEY-----
        webhookSecret: "your-webhook-secret"
All bot values support either inline strings or Kubernetes valueFrom references for secrets:
global:
  integrations:
    github:
      url: "https://github.com"
      bot:
        appId:
          valueFrom:
            secretKeyRef:
              name: github-bot-secret
              key: app-id
        privateKey:
          valueFrom:
            secretKeyRef:
              name: github-bot-secret
              key: private-key
        webhookSecret:
          valueFrom:
            secretKeyRef:
              name: github-bot-secret
              key: webhook-secret
The full set of Helm values for the GitHub bot:
Helm ValueEnv VariableRequired for ActionsDescription
global.integrations.github.bot.appIdGITHUB_BOT_APP_IDYesGitHub App numeric ID
global.integrations.github.bot.privateKeyGITHUB_BOT_PRIVATE_KEYYesPEM private key
global.integrations.github.bot.nameGITHUB_BOT_NAMENoDisplay name for the bot
global.integrations.github.bot.clientIdGITHUB_BOT_CLIENT_IDNoGitHub App client ID
global.integrations.github.bot.clientSecretGITHUB_BOT_CLIENT_SECRETNoGitHub App client secret
global.integrations.github.bot.webhookSecretGITHUB_WEBHOOK_SECRETYesWebhook secret for verifying incoming GitHub events

Docker / Manual Deployment

When running the workspace engine directly, pass the environment variables:
docker run -d \
  --name ctrlplane-workspace-engine \
  -e GITHUB_BOT_APP_ID=123456 \
  -e GITHUB_BOT_PRIVATE_KEY="$(cat private-key.pem)" \
  # ... other env vars ...
  ctrlplane/workspace-engine:latest

Configuration

Job Agent Setup

Create a job agent with type github-app:
type: JobAgent
name: github-actions
agentType: github-app

Deployment Configuration

Configure the deployment to use GitHub Actions:
type: Deployment
name: api-service
jobAgent: github-actions
jobAgentConfig:
  installationId: 12345678
  owner: your-org
  repo: your-repo
  workflowId: 12345678
  ref: main # optional, defaults to main
FieldRequiredDescription
installationIdYesGitHub App installation ID
ownerYesRepository owner (org or user)
repoYesRepository name
workflowIdYesWorkflow ID (numeric)
refNoGit ref to run workflow on (default: main)

Finding Your Installation ID

The installation ID is the numeric ID assigned when the GitHub App is installed on an organization or user account. You can find it:
  1. From the URL: after installing the App, the URL will be https://github.com/settings/installations/INSTALLATION_ID
  2. From the API:
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://api.github.com/orgs/YOUR_ORG/installations
Look for the id field in the response.

Finding Your Workflow ID

Use the GitHub API to find your workflow ID:
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://api.github.com/repos/OWNER/REPO/actions/workflows
The id field in each workflow object is the numeric workflow ID you need.

Workflow Setup

Create a workflow file in your repository:
# .github/workflows/deploy.yml
name: Deploy

on:
  workflow_dispatch:
    inputs:
      job_id:
        description: "Ctrlplane Job ID"
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get job context
        uses: ctrlplanedev/get-job-inputs@v1
        id: job
        with:
          job_id: ${{ inputs.job_id }}
          api_key: ${{ secrets.CTRLPLANE_API_KEY }}

      - name: Deploy
        run: |
          echo "Deploying ${{ steps.job.outputs.version_tag }}"
          echo "Environment: ${{ steps.job.outputs.environment_name }}"
          echo "Resource: ${{ steps.job.outputs.resource_identifier }}"

          # Your deployment commands here

Available Job Context

The get-job-inputs action provides these outputs:
OutputDescription
job_idThe Ctrlplane job ID
version_tagVersion tag being deployed
version_nameVersion name
environment_nameTarget environment name
environment_idTarget environment ID
resource_identifierTarget resource identifier
resource_nameTarget resource name
resource_kindTarget resource kind
resource_configResource config (JSON)
deployment_nameDeployment name
deployment_variablesDeployment variables (JSON)

Templating

You can use Go templates in your job agent config to dynamically configure workflows:
jobAgentConfig:
  installationId: "{{.variables.github_installation_id}}"
  owner: "{{.variables.github_org}}"
  repo: "{{.deployment.slug}}"
  workflowId: "{{.variables.workflow_id}}"
  ref: "{{.version.tag}}"

Status Reporting

When the GitHub App webhook is configured (see GitHub App Setup), Ctrlplane automatically receives workflow_run events from GitHub and updates job status without any extra steps in your workflow. The API server listens at /api/github/webhook and maps GitHub workflow run states to Ctrlplane job statuses:
GitHub ConclusionCtrlplane Status
(in progress)in_progress
successsuccessful
failurefailure
cancelledcancelled
action_requiredaction_required
skippedskipped
neutralskipped
Ctrlplane matches the workflow run to a job by extracting the job ID from the workflow run name. The get-job-inputs action includes the job ID in the run name automatically.
For automatic status reporting to work, ensure:
  • The GitHub App webhook URL is set to https://<your-ctrlplane-domain>/api/github/webhook
  • The GITHUB_WEBHOOK_SECRET env var matches the secret configured in the GitHub App
  • The App is subscribed to Workflow runs events

Manual via API (Fallback)

If you are not using webhooks, you can manually report status from the workflow using the Ctrlplane API:
- name: Mark job successful
  if: success()
  run: |
    curl -X PATCH "https://app.ctrlplane.dev/api/v1/jobs/${{ inputs.job_id }}" \
      -H "Authorization: Bearer ${{ secrets.CTRLPLANE_API_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"status": "successful"}'

- name: Mark job failed
  if: failure()
  run: |
    curl -X PATCH "https://app.ctrlplane.dev/api/v1/jobs/${{ inputs.job_id }}" \
      -H "Authorization: Bearer ${{ secrets.CTRLPLANE_API_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"status": "failure"}'

Example: Kubernetes Deployment

name: Deploy to Kubernetes

on:
  workflow_dispatch:
    inputs:
      job_id:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get job context
        uses: ctrlplanedev/get-job-inputs@v1
        id: job
        with:
          job_id: ${{ inputs.job_id }}
          api_key: ${{ secrets.CTRLPLANE_API_KEY }}

      - name: Configure kubectl
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.KUBECONFIG }}

      - name: Deploy
        run: |
          kubectl set image deployment/${{ steps.job.outputs.deployment_name }} \
            app=${{ steps.job.outputs.version_tag }} \
            -n ${{ fromJson(steps.job.outputs.resource_config).namespace }}

Troubleshooting

GitHub bot not configured error

The workspace engine cannot find the App credentials. Verify:
  • GITHUB_BOT_APP_ID is set and is a valid numeric ID
  • GITHUB_BOT_PRIVATE_KEY is set and contains the full PEM-encoded private key
  • If using Helm, check that global.integrations.github.bot.appId and global.integrations.github.bot.privateKey are set in your values

failed to get installation token error

The App credentials are set but the token exchange failed. Check:
  • The App ID matches the GitHub App you created
  • The private key has not been revoked or regenerated
  • The installation ID in your deployment config is correct
  • The GitHub App is still installed on the target organization/account

Workflow not triggered

  • Verify the workflow file has workflow_dispatch as a trigger
  • Ensure the ref in your config points to a branch where the workflow file exists
  • Check that the GitHub App has Actions: Read and write permission on the repository
  • Confirm the workflow ID is correct (use the API to verify)

Job status not updating automatically

  • Verify the GitHub App webhook URL is set to https://<your-ctrlplane-domain>/api/github/webhook
  • Ensure GITHUB_WEBHOOK_SECRET is set on the API server and matches the secret in GitHub App settings
  • Check that the App is subscribed to Workflow runs events
  • Confirm the API server is reachable from GitHub (not behind a firewall)

Authentication errors in the workflow

  • Ensure CTRLPLANE_API_KEY is set as a repository or organization secret in GitHub
  • Verify the API key has permissions to read jobs in your Ctrlplane workspace