A Kubernetes-native webhook service that automatically builds and deploys container images from GitHub repositories.
- GitHub Webhook Integration: Automatically triggered by push, pull request, and tag events
- Zero Configuration: Automatically detects build strategy (Dockerfile, Go, Node.js)
- Repository Configuration: Optional
.build-app.yamlfor build customization (docs) - Secure: GitHub webhook signature validation
- BuildKit-based Builds: Builds container images using Docker's modern BuildKit engine
- GitOps Integration: Automatically updates deployment manifests
- GitHub Status Updates: Real-time build status feedback
- Image Tagging: Smart tagging strategy (main-{sha}, pr-{num}-{sha}, v1.2.3)
GitHub Webhook → Build App → BuildKit Job → GHCR → GitOps Repo
↓
GitHub Commit Status
- Receive GitHub webhook (push/PR/tag) → validate signature
- Clone repository → detect build strategy (Dockerfile exists? go.mod? package.json?)
- Create ephemeral K8s Job running BuildKit to build container
- Push to GHCR as
ghcr.io/{org}/{repo}:{tag}(tags:main-{sha},pr-{num}-{sha},v1.2.3) - Update GitOps repo: modify
apps/{org}/{repo}/deployment.yamlimage field - Update GitHub commit status (pending→success/failure)
The service automatically detects the appropriate build strategy:
- Dockerfile exists: Use existing Dockerfile
- go.mod exists: Generate Go multi-stage Dockerfile
- package.json exists: Generate Node.js multi-stage Dockerfile
- Otherwise: Fail with error message
You can customize the build process by creating a .build-app.yaml file at the root of your repository. This is optional - if not present, the service will use default behavior.
# Build arguments passed to Docker/BuildKit
buildArgs:
VERSION: "1.0.0"
BUILD_DATE: "2024-01-01"
# Environment variables set during build
env:
NODE_ENV: "production"
DEBUG: "false"
# Custom Dockerfile path (relative to repo root)
# Default: Dockerfile
dockerfile: docker/Dockerfile.prod
# Build context path (relative to repo root)
# Default: . (repo root)
context: src
# Target stage in multi-stage Dockerfile
target: production
# Target platform (e.g., linux/amd64, linux/arm64)
platform: linux/amd64See the examples/ directory for complete configuration examples:
- Basic: Simple build args and environment variables
- Advanced: Custom paths, targets, and platforms
- Monorepo: Building from a subdirectory
- Multi-stage: Building specific stages
To use build arguments, reference them in your Dockerfile:
ARG VERSION
ARG BUILD_DATE
FROM node:18
LABEL version=${VERSION}
LABEL build_date=${BUILD_DATE}
# ... rest of your Dockerfile- Kubernetes cluster (1.24+)
- kubectl configured
- GitHub personal access token with repo permissions
- GitHub Container Registry (GHCR) access token
-
Create namespace:
kubectl create namespace build-app
-
Create secrets:
# GitHub webhook secret kubectl create secret generic build-app-secrets \ --from-literal=WEBHOOK_SECRET=your-webhook-secret \ --from-literal=GITHUB_TOKEN=your-github-token \ --from-literal=GITOPS_TOKEN=your-gitops-token \ -n build-app # Docker registry credentials for BuildKit kubectl create secret docker-registry docker-config \ --docker-server=ghcr.io \ --docker-username=your-github-username \ --docker-password=your-github-token \ -n build-app
-
Update ConfigMap in
deploy/kubernetes/deployment.yaml:data: GITOPS_REPO: "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/your-org/gitops-repo.git"
-
Deploy:
kubectl apply -f deploy/kubernetes/deployment.yaml kubectl apply -f deploy/kubernetes/ingress.yaml
-
Configure GitHub webhook:
- Go to repository Settings → Webhooks → Add webhook
- Payload URL:
https://your-domain/webhook - Content type:
application/json - Secret: Use the same value as
WEBHOOK_SECRET - Events: Push, Pull requests, Create (for tags)
Environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
| PORT | No | 8080 | HTTP server port |
| WEBHOOK_SECRET | Yes | - | GitHub webhook secret for signature validation |
| GITHUB_TOKEN | Yes | - | GitHub personal access token |
| GITOPS_REPO | No | - | GitOps repository URL (e.g., https://github.com/org/gitops) |
| GITOPS_TOKEN | No | - | Token for GitOps repository (if different from GITHUB_TOKEN) |
| KUBE_NAMESPACE | No | default | Kubernetes namespace for build jobs |
| CONTAINER_REGISTRY | No | ghcr.io | Container registry URL |
The service supports OpenTelemetry for observability (metrics, traces, and logs):
| Variable | Required | Default | Description |
|---|---|---|---|
| OTEL_SERVICE_NAME | No | build-app | Service name for telemetry |
| SERVICE_VERSION | No | 1.0.0 | Service version |
| OTEL_EXPORTER_OTLP_ENDPOINT | No | - | OTLP endpoint URL (e.g., localhost:4318) |
| OTEL_TRACES_ENABLED | No | true | Enable/disable distributed tracing |
| OTEL_METRICS_ENABLED | No | true | Enable/disable metrics collection |
build.duration- Histogram of container build durations (seconds)build.count- Counter of builds executed (by status: success/failure, type)webhook.count- Counter of webhooks processed (by event type, status)http.server.duration- HTTP request durations (via middleware)
Distributed traces are collected for:
- Webhook request handling
- Repository cloning
- Build execution
- GitHub API calls
- GitOps updates
To send telemetry to an OTLP collector:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "otel-collector.monitoring.svc.cluster.local:4318"
- name: OTEL_SERVICE_NAME
value: "build-app"
- name: SERVICE_VERSION
value: "1.0.0"
- name: OTEL_TRACES_ENABLED
value: "true"
- name: OTEL_METRICS_ENABLED
value: "true"When OTEL_EXPORTER_OTLP_ENDPOINT is not set, the service runs with no-op telemetry (no overhead).
- Push to main/master:
main-{short-sha}(e.g.,main-abc1234) - Push to other branches:
{branch}-{short-sha}(e.g.,develop-abc1234) - Pull requests:
pr-{number}-{short-sha}(e.g.,pr-42-abc1234) - Tags:
{tag}(e.g.,v1.2.3)
go build -o build-app .export WEBHOOK_SECRET=test-secret
export GITHUB_TOKEN=your-token
export PORT=8080
./build-appdocker build -t build-app:latest .Send a test webhook:
curl -X POST http://localhost:8080/webhook \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-H "X-Hub-Signature-256: sha256=$(echo -n '{}' | openssl dgst -sha256 -hmac 'test-secret' | cut -d' ' -f2)" \
-d '{}'- Webhook signatures are validated using HMAC-SHA256
- Kubernetes RBAC restricts service account permissions
- Secrets are stored in Kubernetes secrets
- BuildKit requires privileged access for container builds
kubectl logs -f deployment/build-app -n build-appkubectl get jobs -n build-app
kubectl logs job/build-<repo>-<tag>-<timestamp> -n build-app -c buildkit- Webhook signature validation fails: Ensure
WEBHOOK_SECRETmatches GitHub webhook secret - Build job fails: Check BuildKit logs and ensure Docker registry credentials are correct
- GitOps update fails: Verify
GITOPS_TOKENhas write access to GitOps repository
- Configuration Guide - Customize builds with
.build-app.yaml - Setup Guide - Detailed deployment instructions
- Contributing - Development guidelines
MIT