Flux, the GitOps tool for Kubernetes, can automate deployments when you merge a pull request.

Here’s how it works:

Imagine you have a Kubernetes cluster and your application code lives in a Git repository. You also have a separate Git repository for your Kubernetes manifests (Deployments, Services, etc.), managed by Flux.

When you want to deploy a new version of your app, you’d typically:

  1. Update your application code.
  2. Push changes to your app’s Git repo.
  3. Manually update the image tag in your Kubernetes manifest files in the manifest Git repo.
  4. Commit and push the manifest changes.
  5. Flux, watching the manifest repo, detects the change and applies it to your cluster.

This process works, but it’s manual. We want to automate step 3 and 4.

The Goal: Merge a PR in the application repo, and have Flux automatically update the manifest repo with the new image tag, triggering a deployment.

The Solution: Image Update Automation

Flux achieves this using a component called ImageUpdateAutomation. This component watches your application’s container image registry (like Docker Hub, GHCR, ECR) for new image tags. When it finds a new tag that matches a pattern, it can:

  1. Commit a change to your manifest Git repository.
  2. Create a Pull Request in your manifest Git repository with the updated image tag.

This second option is generally preferred for better control and review.

Let’s walk through a setup:

Prerequisites:

  • A Kubernetes cluster with Flux installed.
  • Your application code in a Git repository (e.g., my-app-code).
  • Your Kubernetes manifests (Deployment, Service, etc.) in a separate Git repository (e.g., my-app-manifests).
  • Flux is already configured to sync your my-app-manifests repository.
  • Your application is building and pushing container images to a registry (e.g., ghcr.io/myorg/my-app).

Step 1: Define the Image Repository in Flux

First, you need to tell Flux about the container image repository your application uses. Create a ImageRepository custom resource.

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: my-app-images
  namespace: flux-system
spec:
  image: ghcr.io/myorg/my-app # The full image name in your registry
  interval: 5m               # How often Flux checks for new images
  secretRef:                 # Optional: if your registry requires authentication
    name: registry-credentials

Apply this to your cluster: kubectl apply -f image-repo.yaml

Step 2: Define the Image Policy

Next, define an ImagePolicy that specifies which image tags Flux should consider. This is where you can filter by semver, alphabetical order, etc.

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: my-app-policy
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: my-app-images  # Matches the ImageRepository name above
  policy:
    semver:             # Filter by semantic versioning
      range: ">=1.0.0"  # Accept any tag that's 1.0.0 or higher

Apply this: kubectl apply -f image-policy.yaml

Flux will now regularly check ghcr.io/myorg/my-app for new tags matching ^v?\d+\.\d+\.\d+$ (by default, it looks for semver tags) and will identify the latest matching tag.

Step 3: Configure Image Update Automation

Now, create the ImageUpdateAutomation resource. This is the core piece that orchestrates the Git commit or PR.

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
  name: my-app-auto-update
  namespace: flux-system
spec:
  interval: 10m # How often to check for image policy changes and update Git
  sourceRef:
    kind: GitRepository
    name: flux-manifests # The name of your GitRepository CR for your manifests repo
  git:
    checkout: {} # This tells it to checkout the default branch
    commit:
      author:
        name: Flux Automation
        email: flux@example.com
      messageTemplate: |

        chore(deps): update image to {{range .ImageUpdates}}{{.Repository}}@{{.Digest}}{{end}}

    push:
      branch: main # The branch to push to in your manifests repo
  update:
    strategy: Setters # Use Kubernetes setters for easy updates

Apply this: kubectl apply -f image-update-automation.yaml

Explanation of ImageUpdateAutomation:

  • sourceRef: Points to the GitRepository resource that Flux uses to sync your manifest files. This is crucial.

  • git.checkout: Specifies how to get the current state of the manifest repository.

  • git.commit: Configures the author and message for new commits. {{range .ImageUpdates}}{{.Repository}}@{{.Digest}}{{end}} is a template that will be filled with the updated image details.

  • git.push.branch: The branch in your manifest repository that Flux will push changes to.

  • update.strategy: Setters: This is a powerful mechanism. Flux will look for specific annotations in your manifest files that mark them for update. The most common is fluxcd.io/sync-image:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-app
      namespace: default
      annotations:
        fluxcd.io/sync-image: ghcr.io/myorg/my-app # Flux will update this value
    spec:
      template:
        spec:
          containers:
          - name: my-app
            image: ghcr.io/myorg/my-app:v1.0.0 # This initial value will be updated
    

    When Flux finds a new image tag via ImagePolicy, it will find this Deployment, update the fluxcd.io/sync-image annotation, and then update the spec.template.spec.containers[0].image field to match the new tag.

Step 4: Configure Git Credentials for the Manifest Repo

Flux needs credentials to push to your manifest repository. This is usually done via a Secret and a GitRepository resource. If you already have Flux syncing your manifests, you likely have this set up. Ensure the GitRepository resource for flux-manifests has credentials that allow pushing.

How it all ties together:

  1. You build and push a new image to ghcr.io/myorg/my-app with tag v1.1.0.
  2. Flux’s ImageRepository (my-app-images) detects the new tag.
  3. Flux’s ImagePolicy (my-app-policy) evaluates the new tag and determines v1.1.0 is the latest matching tag.
  4. The ImageUpdateAutomation (my-app-auto-update) checks the ImagePolicy periodically.
  5. When it sees a new latest image, it checks out your manifest repository (flux-manifests).
  6. It finds the Deployment with the fluxcd.io/sync-image annotation.
  7. It updates the spec.template.spec.containers[0].image field in the manifest files to ghcr.io/myorg/my-app:v1.1.0.
  8. It commits these changes to the main branch of your manifest repository.
  9. Flux’s main sync controller, watching flux-manifests, detects the new commit and applies the updated Deployment to your Kubernetes cluster.

Alternative: Creating a Pull Request

Instead of directly pushing to main, you can configure ImageUpdateAutomation to create a Pull Request. This is safer and allows for review. You’d add a pullRequestMapping section:

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
  name: my-app-auto-update
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: flux-manifests
  git:
    checkout: {}
    commit:
      author:
        name: Flux Automation
        email: flux@example.com
      messageTemplate: |

        chore(deps): update image to {{range .ImageUpdates}}{{.Repository}}@{{.Digest}}{{end}}

    # Remove the push section if you want to create PRs
  update:
    strategy: Setters
  # Add this section to create a Pull Request
  pullRequestMapping:
    github:
      # This is how Flux will authenticate with GitHub to create PRs
      # It needs a GitHub Personal Access Token (PAT) with repo scope
      # stored in a secret.
      tokenRef:
        name: github-token # A secret containing your GitHub PAT
    # Define the base branch and branch prefix for new branches
    branch:

      name: "{{ .Image.Repository }}-{{ .Image.Tag }}" # e.g., my-app-v1.1.0

    pr:
      author:
        name: Flux Automation
        email: flux@example.com

      title: "chore(deps): update image to {{range .ImageUpdates}}{{.Repository}}@{{.Digest}}{{end}}"

      body: |

        This PR updates the image for {{range .ImageUpdates}}{{.Repository}} to {{.Tag}}{{end}}.


        Triggered by Flux Image Update Automation.

For this to work, you’ll need a Secret named github-token in the flux-system namespace containing a token key with your GitHub PAT. This PAT needs repo scope.

With pullRequestMapping configured, Flux will create a new branch (e.g., my-app-v1.1.0), commit the changes to it, and then open a Pull Request against your main branch.

The next step after this is to integrate this with your CI/CD pipeline to run tests on the PR before merging.

Want structured learning?

Take the full Flux course →