In a previous post, we installed Weaveworks Flux. Flux synchronizes the contents of a git repository with your Kubernetes cluster. Flux can easily be installed via a Helm chart. As an example, we installed Traefik by adding the following yaml to the synced repository:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: traefik namespace: default annotations: fluxcd.io/ignore: "false" spec: releaseName: traefik chart: repository: https://kubernetes-charts.storage.googleapis.com/ name: traefik version: 1.78.0 values: serviceType: LoadBalancer rbac: enabled: true dashboard: enabled: true
It does not matter where you put this file because Flux scans the complete repository. I added the file to a folder called traefik.
If you look more closely at the YAML file, you’ll notice its kind is HelmRelease. You need an operator that can handle this type of file, which is this one. In the previous post, we installed the custom resource definition and the operator manually.
Adding a custom application
Now it’s time to add our own application. You do not need to use Helm packages or the Helm operator to install applications. Regular yaml will do just fine.
The application we will deploy needs a Redis backend. Let’s deploy that first. Add the following yaml file to your repository:
--- apiVersion: apps/v1 kind: Deployment metadata: name: redis labels: app: redis spec: selector: matchLabels: app: redis replicas: 1 template: metadata: labels: app: redis spec: containers: - name: redis image: redis resources: requests: cpu: 200m memory: 100Mi ports: - containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis labels: app: redis spec: ports: - port: 6379 targetPort: 6379 selector: app: redis
After committing this file, wait a moment or run fluxctl sync. When you run kubectl get pods for the default namespace, you should see the Redis pod:

Now it’s time to add the application. I will use an image, based on the following code: https://github.com/gbaeke/realtime-go (httponly branch because master contains code to automatically request a certificate with Let’s Encrypt). I pushed the image to Docker Hub as gbaeke/fluxapp:1.0.0. Now let’s deploy the app with the following yaml:
--- apiVersion: apps/v1 kind: Deployment metadata: name: realtime labels: app: realtime spec: selector: matchLabels: app: realtime replicas: 1 template: metadata: labels: app: realtime spec: containers: - name: realtime image: gbaeke/fluxapp:1.0.0 env: - name: REDISHOST value: "redis:6379" resources: requests: cpu: 50m memory: 50Mi limits: cpu: 150m memory: 150Mi ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: realtime labels: app: realtime spec: ports: - port: 80 targetPort: 8080 selector: app: realtime --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: realtime-ingress spec: rules: - host: realtime.IP.xip.io http: paths: - path: / backend: serviceName: realtime servicePort: 80
In the above yaml, replace IP in the Ingress specification to the IP of the external load balancer used by your Ingress Controller. Once you add the yaml to the git repository and you run fluxctl sync the application should be deployed. You see the following page when you browse to http://realtime.IP.xip.io:

Great, v1.0.0 of the app is deployed using the gbaeke/fluxapp:1.0.0 image. But what if I have a new version of the image and the yaml specification does not change? Read on…
Upgrading the application
If you have been following along, you can now run the following command:
fluxctl list-workloads -a
This will list all workloads on the cluster, including the ones that were not installed by Flux. If you check the list, none of the workloads are automated. When a workload is automated, it can automatically upgrade the application when a new image appears. Let’s try to automate the fluxapp. To do so, you can either add annotations to your yaml or use fluxctl. Let’s use the yaml approach by adding the following to our deployment:
annotations: flux.weave.works/automated: "true" flux.weave.works/tag.realtime: semver:~1.0
Note: Flux only works with immutable tags; do not use latest
After committing the file and running fluxctl sync, you can run fluxctl list-workloads -a again. The deployment should now be automated:

Now let’s see what happens when we add a new version of the image with tag 1.0.1. That image uses a different header color to show the difference. Flux monitors the repository for changes. When it detects a new version of the image that matches the semver filter, it will modify the deployment. Let’s check with fluxctl list-workloads -a:

And here’s the new color:

But wait… what about the git repo?
With the configuration of a deploy key, Flux has access to the git repository. When a deployment is automated and the image is changed, that change is also reflected in the git repo:

In the yaml, version 1.0.1 is now used:

What if I don’t like this release? With fluxctl, you can rollback to a previous version like so:

Although this works, the deployment will be updated to 1.0.1 again since it is automated. To avoid that, first lock the deployment (or workload) and then force the release of the old image:
fluxctl lock -w=deployment/realtime fluxctl release -n default --workload=deployment/realtime --update-image=gbaeke/fluxapp:1.0.0 --force
In your yaml, there will be an additional annotation: fluxcd.io/locked: ‘true’ and the image will be set to 1.0.0.
Conclusion
In this post, we looked at deploying and updating an application via Flux automation. You only need a couple of annotations to make this work. This was just a simple example. For an example with dev, staging and production branches and promotion from staging to production, be sure to look at https://github.com/fluxcd/helm-operator-get-started as well.