Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXVIII · FluxImage

Image automation and update — scanning registries, filtering tags, writing back to Git

Advanced⏱ ~26 mingitflux

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

Not yet marked complete on this device.

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"]
  1. Scan. ImageRepository declares a registry and an interval. The reflector polls, lists tags, writes them to status.tags.
  2. Filter. ImagePolicy references an ImageRepository and declares a filter rule. The reflector writes the latest matching tag to status.latestImage.
  3. Write. ImageUpdateAutomation references one or more ImagePolicy resources 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

  1. Pin to semver ranges, not latest. A semver range bounded to the major version in use is the rule.
  2. The bot identity is limited to the image update branch. Branch protection on main requires human review.
  3. The commit message template includes the previous and new values. 1.4.7 -> 1.5.0 is 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

  1. Q1. Which CRD owns the rule that filters a registry's tags down to the one the ImageUpdateAutomation will write back to Git?

  2. Q2. Image automation is enabled by default in every Flux install because the reflector and the writer ship together.

  3. Q3. Name the three CRDs image-automation-controller owns and identify which one writes a new Git commit.

  4. 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.