Git, CI/CD & GitOpsLXXVIII · FluxImage
Image automation and update — scanning registries, filtering tags, writing back to Git
What you'll learn
- Describe what image-automation-controller does and the three CRDs it owns
- Configure an ImageRepository to scan a registry for image tags
- Configure an ImagePolicy to filter tags by semver, regex, or timestamp
- Configure an ImageUpdateAutomation to write the filtered tag back to Git
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Image automation is the part of Flux that turns “the image changed” into a Git commit. Without it, a new image pushed to a registry is invisible until a human opens a PR. With it, the controller scans the registry, picks the tag that matches a policy, writes the tag into the manifests, and lets the Kustomization reconcile the change.
What image-automation-controller does
The controller is two controllers under one name. The
image-reflector-controller ships with the default Flux
install and owns ImageRepository and ImagePolicy. The
image-automation-controller is opt-in and owns
ImageUpdateAutomation. Together they form a three-step loop:
flowchart LR
REG["Container registry"] --> IRC["image-reflector-controller"]
IRC --> IR["ImageRepository"]
IR --> IP["ImagePolicy"]
IP --> IUA["image-automation-controller"]
IUA -->|"git push"| GIT["Git repository"]
GIT --> KC["kustomize-controller"]
KC --> K8s["Cluster"]
- Scan.
ImageRepositorydeclares a registry and an interval. The reflector polls, lists tags, writes them tostatus.tags. - Filter.
ImagePolicyreferences anImageRepositoryand declares a filter rule. The reflector writes the latest matching tag tostatus.latestImage. - Write.
ImageUpdateAutomationreferences one or moreImagePolicyresources and a Git write target. The automation controller clones the repo, replaces image references, commits, and pushes.
The three CRDs
ImageRepository declares what to scan:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: payments-api
namespace: flux-system
spec:
image: ghcr.io/example/payments-api
interval: 5m0s
secretRef:
name: ghcr-creds
The reflector scans the image every five minutes and writes
the list of tags to status.tags. Authentication uses a
kubernetes.io/dockerconfigjson Secret.
ImagePolicy declares what to keep:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: payments-api
namespace: flux-system
spec:
imageRepositoryRef:
name: payments-api
policy:
semver:
range: ">=1.4.0 <2.0.0"
The semver range keeps any 1.x release at or after 1.4.0, up
to but not including 2.0.0. The reflector writes the latest
matching tag to status.latestImage.
ImageUpdateAutomation declares what to do with the
chosen tag:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: payments-api
namespace: flux-system
spec:
interval: 1m0s
sourceRef:
kind: GitRepository
name: apps
git:
checkout:
ref:
branch: main
commit:
author:
name: flux-imagebot
messageTemplate: |
image: update {range .Changed}{.OldValue} -> {.NewValue}{end}
push:
branch: main
update:
path: ./clusters/production
strategy: Setters
The Setters strategy requires the YAML files to declare
kustomize.toolkit.fluxcd.io/images setters on the Kustomize
resources.
Under the hood
The reflector and the automation are decoupled. The reflector runs in every Flux install; the automation is opt-in. Scanning a registry is read-only; writing back to Git is a change that requires deliberate enablement.
The loop in production
sequenceDiagram
participant Dev as Developer
participant Reg as Registry
participant UA as ImageUpdateAutomation
participant Git as Git
participant K8s as Cluster
Dev->>Reg: push payments-api:1.5.0
UA->>Git: clone, find, replace, commit, push
Git-->>K8s: new commit visible
K8s-->>Dev: payments-api:1.5.0 running
The developer only pushes to the registry. The commit, the PR (if configured), the reconcile, and the rollout are all controller-driven. Every deploy is a commit; every commit has a SHA; every SHA has an author.
Production discipline
- Pin to semver ranges, not
latest. A semver range bounded to the major version in use is the rule. - The bot identity is limited to the image update branch.
Branch protection on
mainrequires human review. - The commit message template includes the previous and new
values.
1.4.7 -> 1.5.0is the audit answer.
Cross-course references
- Kubernetes for Production Sysadmins - Parts XX-XXII (Workload Lifecycle) cover the rolling-update behaviour.
- Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXVI (GitOps Repo Architecture) is the repo layout the automation writes back to.
Quiz
Knowledge check · 4 questions
Q1. Which CRD owns the rule that filters a registry's tags down to the one the ImageUpdateAutomation will write back to Git?
Q2. Image automation is enabled by default in every Flux install because the reflector and the writer ship together.
Q3. Name the three CRDs image-automation-controller owns and identify which one writes a new Git commit.
Q4. Diagnose why the image-automation-controller is reporting Ready=False, reason=GitOperationFailed, and recommend the fix.
A team runs `flux get imageupdateautomations` and sees `payments-api` reporting Ready=False, reason=GitOperationFailed. The team's GitHub repo's branch protection requires a status check before merge, and the bot's push is failing the check because no CI workflow runs on bot commits. The automation controller logs show: `error: failed to push commit: pre-receive hook declined`.
Passing score: 75%. Answers are checked in this browser.