Trigger Flux Reconciliation with Webhook Receivers

A Flux webhook receiver is the most efficient way to trigger a reconciliation. Instead of Flux polling Git for changes, Git can proactively tell Flux when something has changed.

Let’s see it in action. Imagine you have a Git repository with a file named apps/my-app.yaml:

apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  path: ./apps
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-repo
  validation: client

And a GitRepository resource pointing to that repo:

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: flux-repo
  namespace: flux-system
spec:
  interval: 5m
  ref:
    branch: main
  url: ssh://git@github.com/your-org/your-repo.git
  secretRef:
    name: flux-git-key

Normally, Flux would check the GitRepository every 5 minutes. If it detects a change in the main branch, it would then trigger a reconciliation of the Kustomization resource (my-app).

Now, let’s introduce a WebhookReceiver. We’ll create a Receiver resource:

apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Receiver
metadata:
  name: git-webhook
  namespace: flux-system
spec:
  type: github
  events:
    - "ping"
    - "push"
  secretRef:
    name: github-webhook-secret

This Receiver is configured to listen for ping and push events from GitHub. The secretRef points to a Kubernetes Secret containing the webhook secret provided by GitHub.

Next, we associate this Receiver with our GitRepository. We add an accesstoken and webhook field to the GitRepository spec:

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: flux-repo
  namespace: flux-system
spec:
  interval: 5m
  ref:
    branch: main
  url: ssh://git@github.com/your-org/your-repo.git
  secretRef:
    name: flux-git-key
  # New fields below
  accesstoken:
    secretRef:
      name: github-token # A secret containing your GitHub Personal Access Token
  webhook:
    url: http://flux-webhook-receiver.flux-system.svc.cluster.local # The URL of your Receiver service

The accesstoken is for Flux to authenticate with the Git provider (e.g., to fetch the repository). The webhook.url is the crucial part: it’s the address of the Receiver service running within your cluster. Flux will automatically register this webhook URL with your Git provider.

Now, when you push a change to the main branch of your your-repo repository on GitHub, GitHub will send an HTTP POST request to the Receiver service. The Receiver validates the payload using the github-webhook-secret and, if valid, it emits a Kubernetes event. Flux, watching for these events, sees that the GitRepository (flux-repo) has potentially changed and immediately triggers a reconciliation for all Kustomization resources that depend on it, like my-app.

This bypasses the interval specified in the GitRepository and Kustomization resources, making deployments much faster. The reconciliation for my-app will happen as soon as the webhook event is processed, not after a 5-minute or 10-minute wait.

The core problem this solves is the latency inherent in polling. Polling means you wait for the next scheduled check, which can be minutes or even hours depending on your configuration. Webhooks enable a push-based model: changes are communicated immediately, aligning infrastructure deployments with developer workflows.

Internally, the Receiver controller watches for Receiver resources. When it finds one, it configures a webhook on the Git provider (e.g., GitHub, GitLab, Bitbucket) to point to its own service endpoint. This endpoint receives incoming webhook payloads, validates them against the configured secret, and then translates them into Kubernetes events. The Flux controllers (like source-controller and kustomize-controller) are subscribed to these events. When an event related to a GitRepository or HelmRepository is received, they immediately initiate a reconciliation for dependent resources.

A common point of confusion is understanding the interplay between the GitRepository’s interval and the webhook. The interval on GitRepository still matters for how often Flux checks the Git provider if there’s no webhook. However, when a webhook is successfully configured and receiving events, that interval becomes largely irrelevant for triggering new fetches. The webhook event itself is the primary trigger.

The most surprising true thing about Flux webhooks is that they don’t directly trigger a Kustomization reconciliation. They trigger an event that signals a potential change in the source (GitRepository or HelmRepository). It’s the source-controller that then acts on this event, fetches the updated source, and then signals the kustomize-controller (or helm-controller) to reconcile the dependent Kustomization (or HelmRelease). This layered approach decouples source fetching from manifest application.

The next concept to explore is how to use multiple webhook receivers for different event types or Git providers within the same cluster.

Want structured learning?

Take the full Flux course →