< Back Post-Image

Automatic image update in Git with FluxCD

It’s been a while since we talked about Gitops in this blog. It’s a topic we had broadly covert and you can find our articles here :

Quick reminder about Gitops

Gitops is a set of tools and practices to deploy your applications and your infrastructure. Anything defined as code really. This code is stored in a Git repository and acts as your single source of truth. Gitops mechanisms will ensure that the real state (the one in your Kubernetes cluster for example) matches your desired state (the one defined in Git) and will reconcile both at all time.

Why is it good ?

First, you don’t need to operate your cluster with kubectl, the Gitops agent (we’ll talk about implementations later) will automatically apply your changes as soon as they are committed to git. Git will be your only tool to interact with Kubernetes. Code review, PR, MR etc, will allow you to precisely control what is applied to your cluster.

These are the 4 principles of Gitops:

  • Your system is defined declaratively
  • The desired state of your system is stored in Git which is your single source of truth
  • Changes are applied as soon as they are validated
  • A process of reconciliation ensure the synchronisation between real state and desired state

The two leading Gitops solutions are FluxCD and ArgoCD. Both are members of the CNCF. FluxCD follows the specifications of the Gitops toolkit while ArgoCD follows those of the Gitops Engine.

As we already talked about Gitops (in this article and the previous ones), we won’t come back on those principles and we are going to present a new feature in FluxCD v2 : the image update feature.

This feature allows Flux to track the changes of your container image into a container registry and apply those changes into your Kubernetes cluster.

Didn’t it already exist ?

Yes indeed. This feature exists in FluxCD v1 but was removed in FluxCD v2.

Tout à fait, mais dans FluxCD v1, depuis la v2 cette fonction avait disparu mais faisait parti des priorités sur la roadmap.

This feature, like the one in FluxCD v1, will track new versions of your image and will update your desired state in Git with the new image. You can decide to track every new images or only the ones matching a semver rule or a regex. Once your desired state is updated, FluxCD reconciliation process will trigger an update on your Kubernetes cluster.

One could consider that this feature is out of the scope of Gitops since your container registry can be considered as a second source of truth. This is why you won’t find this feature with ArgoCD.

This feature is now back in FlucCD v2 in alpha.

Let’s play !

Let’s bootstrap a Flux environment then we will update it to enable the image update feature.

You will need a GitHub personnal token with all repo access permissions:


$ curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
$ flux check --pre
$ flux bootstrap github --owner=rguichard --repository=fluxcd-demo --path=clusters/fluxcd-demo --personal
► connecting to github.com
✔ repository cloned
✚ generating manifests
✔ components are up to date
► installing components in flux-system namespace
namespace/flux-system created
customresourcedefinition.apiextensions.k8s.io/alerts.notification.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/buckets.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/gitrepositories.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/helmcharts.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/helmreleases.helm.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/helmrepositories.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/kustomizations.kustomize.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/providers.notification.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/receivers.notification.toolkit.fluxcd.io created
serviceaccount/helm-controller created
serviceaccount/kustomize-controller created
serviceaccount/notification-controller created
serviceaccount/source-controller created
clusterrole.rbac.authorization.k8s.io/crd-controller-flux-system created
clusterrolebinding.rbac.authorization.k8s.io/cluster-reconciler-flux-system created
clusterrolebinding.rbac.authorization.k8s.io/crd-controller-flux-system created
service/notification-controller created
service/source-controller created
service/webhook-receiver created
deployment.apps/helm-controller created
deployment.apps/kustomize-controller created
deployment.apps/notification-controller created
deployment.apps/source-controller created
networkpolicy.networking.k8s.io/allow-scraping created
networkpolicy.networking.k8s.io/allow-webhooks created
networkpolicy.networking.k8s.io/deny-ingress created
Waiting for deployment "source-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "source-controller" successfully rolled out
deployment "kustomize-controller" successfully rolled out
Waiting for deployment "helm-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "helm-controller" successfully rolled out
Waiting for deployment "notification-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "notification-controller" successfully rolled out
✔ install completed
► configuring deploy key
✔ deploy key configured
► generating sync manifests
► applying sync manifests
◎ waiting for cluster sync
✔ bootstrap finished

Unlike Flux v1 which was composed of one controller, Flux v2 uses the Gitops toolkit which contains many controllers and can be configure using CRD:

  • Source Controller
  • Kustomize Controller
  • Helm Controller
  • Notification Controller
  • Image Automation Controller


All these components are now installed and you can commit your Kubernetes resources into the Git repository Flux created for you (fluxcd-demo in my case). Reconciliation process kicks in every minute.

Image automation

Image Automation controller isn’t installed by default (since it’s an alpha feature) et you need to reconfigure Flux. The flux bootstrap command is idempotent and you can run it again with the new parameters:


We are going to create three versions of our image.

$ docker tag particule/helloworld particule/helloworld:v1.0.0
$ docker tag particule/helloworld particule/helloworld:v1.1.0
$ docker tag particule/helloworld particule/helloworld:v2.0.0

Use the CRD ImageRepository to track your image:

apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImageRepository
  name: helloworld
  namespace: flux-system
  image: particule/helloworld
  interval: 1m0s

Then, use the CRD ImagePolicy to specify which version you want to track.

apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImagePolicy
  name: helloworld
  namespace: flux-system
    name: helloworld
      range: '>= 1.0.0 <2.0.0'

I choose to track every versions between 1.0.0 and 2.0.0. Only particule/helloworld:1.0.0 and particule/helloworld:1.1.0 should be concerned by the update, particule/helloworld:2.0.0 shouldn’t.

Those CRD can set the tracking up but they will not trigger an update into the Git repository.

You can check that the tracking works as expected:

$ flux get image policy helloworld
NAME      	READY	MESSAGE                                                       	LATEST IMAGE
helloworld	True 	Latest image tag for 'particule/helloworld' resolved to: 1.1.0	particule/helloworld:1.1.0
$ flux get image repository helloworld
NAME      	READY	MESSAGE                       	LAST SCAN           	SUSPENDED
helloworld	True 	successful scan, found 11 tags	2021-02-03T23:45:03Z	False

We see the last image I push and match my semver rule : 1.1.0.

Let’s configure Flux to update our Git repository when a new version is found by our ImagePolicy. Flux needs write permission to your Git repository to trigger the reconciliation process.

Two things here. First create the new resource ImageUpdateAutomation then modify your application to tell Flux which image would need to be updated. To do so, you need to add a marker to the container spec: # {"$imagepolicy": "POLICY_NAMESPACE:POLICY_NAME"}.

  - name: container
    image: particule/helloworld:1.0.0 # {"$imagepolicy": "flux-system:helloworld"}
apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImageUpdateAutomation
  name: helloworld
  namespace: flux-system
    branch: main
      name: helloworld
    authorEmail: fluxcdbot@particule.io
    authorName: fluxcdbot
    messageTemplate: 'update image'
  interval: 1m0s
    strategy: Setters

Check that the Deploy Key in your Git repository has write permission. If that is not the case, you can get the public key used and reconfigure your Github repository:

$ kubectl -n flux-system get secret flux-system -o json | jq '.data."identity.pub"' -r | base64 -d

Apply, wait and watch:

$ kubectl get deploy helloworld -o json | jq -r '.spec.template.spec.containers[0].image'

Our Deployment has been updated with the new image and our Git repository has been also updated.

commit 75ba609fa4fc7b6bf9c3eb742acf1743a1cb4d7b (HEAD -> main, origin/main, origin/HEAD)
Author: fluxcdbot <fluxcdbot@particule.io>
Date:   Thu Feb 4 09:27:40 2021 +0000

    update image

diff --git a/clusters/fluxcd-demo/fullapp.yaml b/clusters/fluxcd-demo/fullapp.yaml
index 636eda1..b1b224d 100644
--- a/clusters/fluxcd-demo/fullapp.yaml
+++ b/clusters/fluxcd-demo/fullapp.yaml
@@ -1,4 +1,3 @@
 apiVersion: apps/v1
 kind: Deployment
@@ -16,10 +15,10 @@ spec:
         app: helloworld
-        image: particule/helloworld # {"$imagepolicy": "flux-system:helloworld"}
+        image: particule/helloworld:1.1.0 # {"$imagepolicy": "flux-system:helloworld"}

At the end we can push a new image 2.0.0 and see that it would not trigger an update since it doesn’t match our ImagePolicy.

$ docker push particule/helloworld:2.0.0


Yes, this is deja-vu. Nevertheless, it’s back ! Maybe that was one of the reason keeping you from using FluxCD v2 ?

L’équipe Particule