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:
- Update your application code.
- Push changes to your app’s Git repo.
- Manually update the image tag in your Kubernetes manifest files in the manifest Git repo.
- Commit and push the manifest changes.
- 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:
- Commit a change to your manifest Git repository.
- 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-manifestsrepository. - 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 theGitRepositoryresource 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 isfluxcd.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 updatedWhen Flux finds a new image tag via
ImagePolicy, it will find thisDeployment, update thefluxcd.io/sync-imageannotation, and then update thespec.template.spec.containers[0].imagefield 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:
- You build and push a new image to
ghcr.io/myorg/my-appwith tagv1.1.0. - Flux’s
ImageRepository(my-app-images) detects the new tag. - Flux’s
ImagePolicy(my-app-policy) evaluates the new tag and determinesv1.1.0is the latest matching tag. - The
ImageUpdateAutomation(my-app-auto-update) checks theImagePolicyperiodically. - When it sees a new latest image, it checks out your manifest repository (
flux-manifests). - It finds the
Deploymentwith thefluxcd.io/sync-imageannotation. - It updates the
spec.template.spec.containers[0].imagefield in the manifest files toghcr.io/myorg/my-app:v1.1.0. - It commits these changes to the
mainbranch of your manifest repository. - 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.