Skip to main content

Webhook receivers

By default, Flux polls each GitRepository on its configured interval: to detect new commits. If you want changes to be applied fast - typically for an actively developed repo - you set the interval to a low value at the cost of Flux hitting your repo every minute. You can also use a push based system using Flux' webhook receiver: the Git provider POSTs to a URL on push, Flux verifies the payload, and reconciliation starts within a second or two.

The Haven+ FluxCD reference implementation provides HTTPRoutes for the webhooks as well as example Receiver objects for GitLab and GitHub.

Setting up a Receiver for a new tenant

Copy the Receiver (GitLab or GitHub) and the token Secret from tenants/tenant-demo/ in the gitops-flux repo.

Generating the webhook token

The token is a shared secret between the Git provider and the Flux Receiver. You can create it using:

TOKEN=$(openssl rand -hex 16)
echo "$TOKEN" # e.g. 8f3c2a1b9d4e5f6a7b8c9d0e1f2a3b4c

Keep $TOKEN around — you will paste it into the Git provider's webhook configuration (raw, unencrypted) and into the Kubernetes Secret (encrypted, see below).

Encrypting the token with Sealed Secrets

The token Secret lives in flux-system (alongside the Receivers). It must be encrypted before it is committed to Git. The Haven+ reference implementation uses Sealed Secrets for this.

Use the $TOKEN from the previous step to create a secret.yaml:

apiVersion: v1
kind: Secret
metadata:
name: webhook-token
namespace: flux-system
type: Opaque
stringData:
token: <TOKEN>

Next, use kubeseal to generate a sealed-secret based on the secret above:

kubeseal -f secret.yaml -w flux-webhook-token.sealed.yaml --controller-namespace sealed-secrets -n flux-system

Registering the webhook with the Git provider

After Flux has applied the Receivers, each one publishes a unique webhook path under .status.webhookPath. Combine that with the cluster's flux.<cluster-domain> hostname (configured in infrastructure/flux-webhook-receiver/overlays/<cluster>/patches/http-route.yaml) to get the full URL:

GITLAB_PATH=$(kubectl get receiver [receiver]-gitlab -n flux-system -o jsonpath='{.status.webhookPath}')
GITHUB_PATH=$(kubectl get receiver [receiver]-github -n flux-system -o jsonpath='{.status.webhookPath}')
echo "GitLab webhook URL: https://flux.<cluster-domain>${GITLAB_PATH}"
echo "GitHub webhook URL: https://flux.<cluster-domain>${GITHUB_PATH}"

Then register the webhook at the provider:

  • GitLab — Project → Settings → Webhooks. Paste the URL, set "Secret token" to the raw $TOKEN, enable Push events and Tag push events, leave SSL verification on. See the GitLab webhook documentation for the full set of options and event types.
  • GitHub — Repository → Settings → Webhooks → Add webhook. Paste the URL as Payload URL, content type application/json, set Secret to the raw $TOKEN, and at minimum enable Pushes. See the GitHub webhooks documentation for an overview and Creating webhooks for the step-by-step.

Increase reconciliation intervals

Once the webhooks are working, polling intervals become less important and can be increased to on the following resources:

  • GitRepository — the source the Receiver reconciles. With a webhook firing on every push, the polling interval: can comfortably move from the default 1m to e.g. 1h.
  • Kustomization — when its sourceRef points at the webhook-driven GitRepository, the Kustomization re-applies as soon as the source changes, regardless of its own interval.
  • ImageUpdateAutomation — webhooks from a container registry can drive image automation the same way through a registry-specific Receiver type. With that wired up the IUA interval: becomes a fallback too. Provider support varies, though: GitHub fires a package webhook event on every push to GHCR, so image-push events are available end-to-end. GitLab does not emit project webhooks for its Container Registry — pushes to the GitLab Container Registry don't fire any event in the project webhook event list, so on GitLab-only setups the IUA must keep polling at its interval:.