Automate Image Updates with Flux Image Automation
Let Flux detect new container image tags and commit them back to Git.
In most GitOps setups, your CI pipeline builds an image and then commits the new tag to the configuration repository. Flux offers an alternative: two optional controllers watch your container registry, pick the newest tag that matches a rule, and commit the change to Git themselves. The cluster still only changes because of a Git commit, so you keep the audit trail. This guide uses the public podinfo image.
How the pieces fit
Three resources work together:
ImageRepositoryscans a registry for the tags of one image.ImagePolicychooses one tag from that list, for example the highest semantic version in a range.ImageUpdateAutomationedits the YAML in your Git repository where a marker says to, then commits and pushes.
All three use the image.toolkit.fluxcd.io/v1 API. The older v1beta2 version was removed from the CRDs in Flux 2.9, so migrate any old manifests before you upgrade.
Install the extra controllers
The image controllers are not part of a default install. Bootstrap again, adding them, and allow Flux to push to the repository:
flux bootstrap github \
--owner=<your-github-user> \
--repository=fleet-infra \
--branch=main \
--path=clusters/dev \
--personal \
--components-extra=image-reflector-controller,image-automation-controller \
--read-write-key
Bootstrap is safe to repeat. It commits the new controller manifests and installs them. --read-write-key makes the deploy key writable so the automation can push. Verify with kubectl get pods -n flux-system.
Scan the registry
apps/podinfo/image-repository.yaml, in the same folder as your deployment:
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: podinfo
namespace: flux-system
spec:
image: ghcr.io/stefanprodan/podinfo
interval: 5m
For a private registry, add secretRef pointing at a Docker registry secret, created with kubectl create secret docker-registry or flux create secret oci.
Choose a tag with a policy
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: podinfo
namespace: flux-system
spec:
imageRepositoryRef:
name: podinfo
policy:
semver:
range: 6.x
semver with range: 6.x selects the highest 6.x version. Other policy types are numerical (highest number) and alphabetical, which suit tags such as build numbers or dates. For rules based on tag names, add filterTags with a regular expression to the policy.
Mark the line to update
Flux updates the exact lines you mark with a comment. In apps/podinfo/deployment.yaml:
spec:
containers:
- name: podinfo
image: ghcr.io/stefanprodan/podinfo:6.5.0 # {"$imagepolicy": "flux-system:podinfo"}
The marker is namespace:policy-name. By default it replaces the whole image reference. To update only the tag, use {"$imagepolicy": "flux-system:podinfo:tag"}, and for Helm values or Kustomize images entries the same marker works on the line that holds the tag. Anything without a marker is left alone.
Tell Flux how to commit
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 30m
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
name: fluxcdbot
email: fluxcdbot@users.noreply.github.com
messageTemplate: |
Automated image update
{{range .Changed.Changes}}
- {{.OldValue}} -> {{.NewValue}}
{{end}}
push:
branch: main
update:
path: ./apps
strategy: Setters
The parts that matter:
sourceRefis the Git repository to commit to. It must be writable, hence--read-write-key.checkout.ref.branchis the branch it starts from, andpush.branchis where the commit goes.update.pathlimits which folders are scanned for markers.messageTemplatecontrols the commit message so the history is readable.
Pushing straight to main makes everything fully automatic. While you build trust, push to a separate branch instead (for example push.branch: flux-image-updates), then merge a pull request when you are happy. The change reaches the cluster only after the merge.
Watch it work
Commit and push these files, then check each stage:
flux get image repository
flux get image policy
flux get image update
The repository shows how many tags were scanned, the policy shows the tag it selected, and the update automation shows the last commit it pushed. If the selected tag is newer than the one in your YAML, a commit appears in Git, and Flux then applies it like any other change. To debug, use kubectl describe on each object: a wrong marker, a policy range that matches nothing, or a read-only deploy key are the usual culprits.
Which approach to pick
Use CI-driven commits if one pipeline already owns promotion and you want a single place that decides what ships. Use Flux image automation when you want the cluster configuration repository to follow a registry on its own, for example keeping a dev environment on the latest patch release. The two can coexist.
Popular Guides
-
Push vs Pull: How Flux Delivers to Kubernetes Why pull-based, Git-driven delivery changes how you deploy, secure, and recover clusters. -
Deploy Your First Application with a GitRepository and Kustomization Point Flux at manifests in Git using a Kustomization resource and watch it sync. -
Control Sync Behavior: Intervals, Prune, Drift Correction, and Rollback Decide when Flux applies changes, what it deletes, how to pause it, and how to undo a bad deploy. -
Configure Multi-Cluster with Flux Manage several clusters from one repository, with shared infrastructure and per-cluster differences. -
Flux CD vs Argo CD: Choosing a GitOps Tool Compare architecture, UI, Helm, secrets, tenancy, and image automation to pick the right tool.