Image automation with public registries
Section 6, Lecture 2, from the Flux CD course by DevCloudLab.
What you’ll learn
- Enable Flux’s image-reflector and image-automation controllers with
--components-extraon bootstrap - Create an ImageRepository that scans a public container registry for available tags
- Write an ImagePolicy that selects the latest tag inside a semantic-versioning range
- Mark a Deployment’s image line so Flux knows which policy governs it, then generate an ImageUpdateAutomation that commits the resolved tag back to Git
- Verify the full loop end to end, from a new tag in the registry to an updated Deployment in the cluster
Overview
Flux’s image automation watches a container registry, decides which tag is “latest” according to a policy you define, and pushes that decision into your Deployment manifest as a Git commit, with no kubectl set image and no one editing YAML by hand. Three resources do the work:
- ImageRepository scans the registry and records which tags exist.
- ImagePolicy filters those tags down to the one that matches your rule.
- ImageUpdateAutomation rewrites the marked image line in Git and pushes the commit.
This lecture builds all three against a public registry (GitHub Container Registry, in this case), so there is no authentication to configure. The next lecture adds the Secret and secretRef a private registry needs; everything else carries over unchanged.
Enable the Two Extra Controllers
The image-reflector-controller and image-automation-controller are not part of a default flux bootstrap. Skip this step and every command below fails silently or reports resources that never become ready. Nothing in this lecture works without it.
Bootstrapping also needs to know your Git token has write access, since the automation pushes commits. If your cluster was bootstrapped without --read-write-key, delete the Secret Flux stores its token in so bootstrap recreates it correctly:
kubectl delete secret flux-system -n flux-system
flux bootstrap is idempotent: running it again against a cluster that is already bootstrapped does not break anything, it simply reconciles to the same desired state, now including the extra components:
flux bootstrap github \
--owner=<your-github-username> \
--repository=<your-repository> \
--path=clusters/staging \
--personal \
--read-write-key \
--components-extra=image-reflector-controller,image-automation-controller
GITHUB_TOKEN must be exported in your shell before you run this. Pull afterward, since bootstrap may have committed changes on the remote:
git pull
Confirm the two new controllers are running:
kubectl get pods -n flux-system
You should see an image-reflector-controller-... pod and an image-automation-controller-... pod alongside the existing Flux controllers.
Deploy the Application You’ll Automate
This lecture automates podinfo, pulled from ghcr.io/stefanprodan/podinfo (GitHub’s public container registry, no credentials required). Add a Deployment for it under your tenant path (here, tenants/base/dev/sync.yaml), pinned to an old tag on purpose:
# tenants/base/dev/sync.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
namespace: apps
spec:
selector:
matchLabels:
app: podinfo
template:
metadata:
labels:
app: podinfo
spec:
containers:
- name: podinfod
image: ghcr.io/stefanprodan/podinfo:5.0.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9898
protocol: TCP
Commit, push, and reconcile the chain of Kustomizations that leads to it:
git add tenants/base/dev/sync.yaml
git commit -m "Deploy podinfo 5.0.0 for image automation"
git push
flux reconcile kustomization flux-system --with-source
flux reconcile kustomization tenants --with-source
flux reconcile kustomization dev --with-source
Confirm the pod is running and note the current image tag. You’ll compare it after automation runs:
kubectl get pods -n apps | grep podinfo
kubectl get deployment/podinfo -n apps -o yaml | grep 'image:'
Create an ImageRepository
An ImageRepository tells Flux which registry image to scan and how often. Generate one with the Flux CLI and export it to a file instead of applying it directly, so it goes through Git like everything else Flux manages:
flux create image repository podinfo \
--image=ghcr.io/stefanprodan/podinfo \
--interval=5m \
--export > podinfo-registry.yaml
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: podinfo
namespace: flux-system
spec:
image: ghcr.io/stefanprodan/podinfo
interval: 5m
No secretRef: the registry is public, so the image-reflector-controller needs no credentials to list its tags. Commit the file to a path your Flux Kustomization reconciles, then reconcile:
git add podinfo-registry.yaml
git commit -m "Add ImageRepository for podinfo"
git push
flux reconcile kustomization flux-system --with-source
Check that the scan succeeded:
flux get image repository podinfo
kubectl get imagerepository -n flux-system
A Ready=True status with a tag count means Flux successfully connected to ghcr.io and listed the available tags. You can see the same list yourself by visiting the registry page for ghcr.io/stefanprodan/podinfo in a browser.
Create an ImagePolicy
An ImageRepository only lists tags. It does not decide which one is “latest.” That’s the job of an ImagePolicy. Suppose your application isn’t ready for podinfo’s next major version: you want Flux to track the newest tag from 5.0.0 up to, but excluding, 6.0.0.
flux create image policy podinfo \
--image-ref=podinfo \
--select-semver='>=5.0.0 <6.0.0' \
--export > podinfo-policy.yaml
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImagePolicy
metadata:
name: podinfo
namespace: flux-system
spec:
imageRepositoryRef:
name: podinfo
policy:
semver:
range: '>=5.0.0 <6.0.0'
imageRepositoryRef links the policy to the ImageRepository you already created. The policy filters that repository’s tag list, it doesn’t scan the registry itself. Commit and reconcile:
git add podinfo-policy.yaml
git commit -m "Add ImagePolicy for podinfo"
git push
flux reconcile kustomization flux-system --with-source
flux get image policy podinfo
The output shows the tag the policy resolved to. If podinfo’s registry has moved past 6.0.0, the policy still reports the highest 5.x.x release. The constraint is doing exactly what it’s for.
Mark the Deployment for Automatic Updates
Flux now knows which tag is “latest” according to your policy, but it doesn’t yet know which line in your manifests to update. You tell it with a marker comment next to the image field:
image: ghcr.io/stefanprodan/podinfo:5.0.0 # {"$imagepolicy": "flux-system:podinfo"}
The marker format is {"$imagepolicy": "namespace:policy-name"}. It must be a YAML comment (the leading #), since the Kubernetes API doesn’t understand this string and would reject it as a real field. flux-system:podinfo names the namespace and the ImagePolicy you created above.
Edit tenants/base/dev/sync.yaml to add the marker, then commit:
git add tenants/base/dev/sync.yaml
git commit -m "Mark podinfo image for automatic updates"
git push
Create an ImageUpdateAutomation
The last resource, ImageUpdateAutomation, is what actually watches marked manifests, rewrites the tag, and commits the change to Git.
flux create image update flux-system \
--interval=30m \
--git-repo-ref=flux-system \
--git-repo-path="./tenants/base/dev" \
--checkout-branch=main \
--push-branch=main \
--author-name=fluxcdbot \
--author-email=fluxcdbot@users.noreply.github.com \
--commit-template='' \
--export > flux-system-automation.yaml
Since this video was recorded: the video’s commit template uses ``. The
.Updatedfield was deprecated in image-automation-controller v0.38.0 and removed in v1.0.0 (thev1API, shipped in Flux 2.7). An automation that uses it is marked Stalled with the messagetemplate uses removed '.Updated' field. Please use '.Changed' instead.The command above uses the current.Changed.Changesfield, where each entry carries.OldValue,.NewValueand.Setter.
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:
email: fluxcdbot@users.noreply.github.com
name: fluxcdbot
messageTemplate: ''
push:
branch: main
update:
path: ./tenants/base/dev
strategy: Setters
git-repo-path (update.path in the YAML) is the directory Flux scans for marker comments. It must contain the manifest you just edited. author-name is deliberately not your own username: a distinct identity like fluxcdbot makes it obvious in git log which commits were made by a human and which were made by the automation.
Commit and reconcile:
git add flux-system-automation.yaml
git commit -m "Add ImageUpdateAutomation for podinfo"
git push
flux reconcile kustomization flux-system --with-source
Verify the Loop End to End
Give the automation a moment, then pull:
git pull
git log --oneline -3
You should see a new commit authored by fluxcdbot. Confirm the manifest itself changed:
cat tenants/base/dev/sync.yaml
The image tag should now be the highest 5.x.x release the ImagePolicy resolved, no longer 5.0.0. That commit is in Git, not yet in the cluster; bring it down immediately instead of waiting for the Kustomization’s own interval:
flux reconcile kustomization flux-system --with-source
Check the running Deployment:
kubectl get deployment/podinfo -n apps -o yaml | grep 'image:'
From here, the loop runs on its own. Whenever a new tag lands in ghcr.io/stefanprodan/podinfo that fits the policy’s semver range, the image-reflector-controller finds it on its next scan, the ImagePolicy resolves it as the new latest, the ImageUpdateAutomation commits the change, and the existing Flux/Kustomization reconciliation applies it. All of it happens as ordinary GitOps commits, with no one ever hand-editing a tag or using the latest label as a stand-in for version tracking.
Further reading
- Flux Image Automation Documentation
- ImageRepository Reference
- ImagePolicy Reference
- ImageUpdateAutomation Reference
- ImageUpdateAutomation commit message template
- Flux bootstrap github command
Built by DevCloudLab
Hands-on cloud-native courses: Kubernetes, GitOps, CI/CD and the cloud.
Visit DevCloudLab.com →
