Skip to content

GitHub Actions

GitHub Actions let you control the full deploy pipeline: validate config, build your image, push to registry, then notify Holden. This ensures Holden pulls the image you just built, not the previous one.

Native GitHub webhooks fire immediately on push—before your image is built. With GitHub Actions, you control when to notify Holden:

  1. Validate your config
  2. Build your Docker image
  3. Push to registry
  4. Then trigger Holden
.github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate Holden config
uses: holden-run/actions/validate@main
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/myorg/myapp:latest
- name: Deploy to Holden
uses: holden-run/actions/deploy@main
with:
webhook-url: ${{ secrets.HOLDEN_WEBHOOK_URL }}
app-id: my-app
webhook-secret: ${{ secrets.HOLDEN_WEBHOOK_SECRET }}

Validates your holden.yml before building. Catches config errors early, before wasting time on a Docker build.

- uses: holden-run/actions/validate@main

Uses the Holden CLI under the hood. Fails the workflow if validation fails.

Calls Holden’s webhook endpoint with proper HMAC signature.

- uses: holden-run/actions/deploy@main
with:
webhook-url: ${{ secrets.HOLDEN_WEBHOOK_URL }}
app-id: my-app
webhook-secret: ${{ secrets.HOLDEN_WEBHOOK_SECRET }}
InputRequiredDescription
webhook-urlYesWebhook base URL (store in GitHub Secrets as HOLDEN_WEBHOOK_URL)
app-idYesApp ID as registered in Holden
webhook-secretYesSecret for HMAC signature (store in GitHub Secrets as HOLDEN_WEBHOOK_SECRET)

Use the official docker/build-push-action to build and push your images. It supports multi-platform builds, layer caching, build arguments, and everything else you’d expect.

The complete example above uses GitHub Container Registry (ghcr.io). For other registries, swap out the login step:

Docker Hub
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Self-hosted registry
- uses: docker/login-action@v3
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}