Introduction
In the fast-paced world of cloud-native development, keeping your Kubernetes deployments up-to-date with the latest container images is a constant challenge. Manually updating image tags in YAML manifests, committing changes to Git, and pushing them through a CI/CD pipeline can be tedious, error-prone, and slow. This process often introduces friction, especially when dealing with frequent image builds, security patches, or hotfixes. Imagine a scenario where a critical vulnerability is discovered, and you need to roll out new images across dozens of services immediately – the manual approach quickly becomes a bottleneck.
Enter GitOps and tools like Flux CD. Flux CD brings the power of GitOps to Kubernetes, ensuring your cluster state always matches the configuration defined in your Git repository. While Flux excels at synchronizing manifest changes, its true power for continuous delivery is unlocked through its image automation capabilities. This feature allows Flux to monitor container registries, detect new image tags, automatically update your Kubernetes manifests in Git, and then apply those changes to your cluster – all without human intervention. This tutorial will guide you through setting up Flux CD image automation to achieve true continuous delivery, making your deployments self-updating and your operations more efficient.
TL;DR Box
Automate Kubernetes deployment image updates using Flux CD’s Image Automation controller. Flux monitors container registries, updates Git manifests with new image tags, and applies changes to your cluster, ensuring deployments are always running the latest versions without manual intervention.
- Install Flux CD:
flux bootstrap git --url=<repo-url> --branch=<branch> --path=<path-to-k8s-manifests> - Define ImageRepository: Tells Flux where to find images.
- Define ImagePolicy: Specifies how to select the latest image tag.
- Annotate Deployment: Instructs Flux to update the image tag in your manifest.
- Commit and Push: Flux will now monitor and update your deployments automatically.
Prerequisites
To follow this guide, you’ll need the following:
- A running Kubernetes cluster (v1.20 or later). You can use Minikube, Kind, or a cloud-managed cluster (EKS, GKE, AKS).
kubectlinstalled and configured to connect to your cluster. Refer to the official Kubernetes documentation for installation instructions.gitinstalled.- A Git repository (GitHub, GitLab, Bitbucket, Azure DevOps, etc.) to store your Kubernetes manifests. This repository will be used by Flux.
- A container registry (Docker Hub, AWS ECR, GCP Container Registry, Azure Container Registry, etc.) with at least one image available that you can push new tags to.
- The Flux CLI installed. Follow the official Flux CD installation guide.
- Basic understanding of Kubernetes Deployments, Services, and GitOps principles.
Step-by-Step Guide to Flux CD Image Automation
Step 1: Bootstrap Flux CD to Your Cluster
First, we need to install Flux CD on your Kubernetes cluster and configure it to synchronize with your Git repository. This process is called bootstrapping. Flux will install its controllers (Source, Kustomize, Helm, Notification, and Image Automation) into your cluster and set up the necessary Git repository synchronization.
Choose an empty Git repository or a specific branch/directory within an existing one. Flux will manage everything in that path. For this guide, we’ll assume you have a GitHub repository named flux-image-automation-demo.
Before bootstrapping, ensure you’ve authenticated the Flux CLI with your Git provider. For GitHub, you might need a Personal Access Token (PAT) with repo scope. Set it as an environment variable or provide it directly.
# Replace with your GitHub username and repository name
export GITHUB_USER="your-github-username"
export GITHUB_REPO="flux-image-automation-demo"
export GITHUB_TOKEN="ghp_YOUR_GITHUB_PAT" # Or use flux bootstrap --token-auth
# Bootstrap Flux CD
flux bootstrap github \
--owner="$GITHUB_USER" \
--repository="$GITHUB_REPO" \
--branch=main \
--path=./clusters/my-cluster \
--personal
This command will:
- Install Flux controllers into a
flux-systemnamespace. - Create a
GitRepositorycustom resource pointing to your specified Git repository. - Create a
Kustomizationcustom resource to apply the manifests from the./clusters/my-clusterpath within your repository. - Generate a deploy key and add it to your GitHub repository (if
--personalis used and you have the PAT).
Verify
After a few minutes, all Flux controllers should be running, and your repository should be synchronized.
flux check --pre
flux check
kubectl get pods -n flux-system
Expected output:
# flux check --pre output will show all prerequisites are met
â–ş checking prerequisites
âś” Kubernetes 1.27.3 >=1.20.0
âś” prerequisites checks passed
# flux check output will show all Flux components are healthy
â–ş checking toolkit components
âś” all components are healthy
# kubectl get pods -n flux-system
NAME READY STATUS RESTARTS AGE
helm-controller-74c65f97b6-j2d8x 1/1 Running 0 5m
image-automation-controller-7b568779b-2lq7c 1/1 Running 0 5m
image-reflector-controller-58474d75d-6h82g 1/1 Running 0 5m
kustomize-controller-59d4f9b87b-h4bqs 1/1 Running 0 5m
notification-controller-584444585c-l6v9l 1/1 Running 0 5m
source-controller-596956799-l7b7d 1/1 Running 0 5m
Step 2: Create a Sample Deployment
Now, let’s create a simple NGINX deployment that we want Flux to automatically update. We’ll use a placeholder image tag initially. Create a new directory in your Git repository, for example, ./apps/nginx, and add the following files.
This deployment will use a generic NGINX image. We’ll specifically target the image field for automation. Note the # {"$imagepolicy": "flux-system:nginx-policy"} comment. This is a special marker that the Image Automation controller looks for to know where to inject the updated image tag. Without it, Flux won’t know which field to modify.
# ./apps/nginx/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- name: nginx
image: nginx:1.21.6 # {"$imagepolicy": "flux-system:nginx-policy"}
ports:
- containerPort: 80
# ./apps/nginx/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
Commit these files to your Git repository and push them. Then, tell Flux to synchronize this new application by adding a Kustomization resource in your clusters/my-cluster path.
# ./clusters/my-cluster/nginx-app.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: nginx-app
namespace: flux-system
spec:
interval: 1m0s
path: ./apps/nginx
prune: true
sourceRef:
kind: GitRepository
name: flux-system
targetNamespace: default
git add .
git commit -m "Add nginx deployment and kustomization"
git push origin main
Verify
Flux should detect the new Kustomization and deploy the NGINX application. This might take a minute or two.
flux get kustomizations -n flux-system
kubectl get deployment nginx-app -n default
Expected output:
# flux get kustomizations -n flux-system
NAME REVISION SUSPENDED READY MESSAGE LAST APPLY
flux-system main/c0a1b2c3 False True Applied revision: main/c0a1b2c3 1m
nginx-app main/d4e5f6g7 False True Applied revision: main/d4e5f6g7 30s
# kubectl get deployment nginx-app -n default
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-app 1/1 1 1 45s
Step 3: Define ImageRepository
The ImageRepository custom resource tells Flux where to find the container images. It specifies the registry and the image name Flux should monitor. Flux’s Image Reflector controller will periodically scan this repository for new tags.
Create this manifest in your Git repository, for example, in ./clusters/my-cluster/image-automation.yaml.
# ./clusters/my-cluster/image-automation.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
name: nginx
namespace: flux-system
spec:
image: nginx
interval: 1m0s # How often to scan the registry
This configuration tells Flux to monitor the nginx image in Docker Hub (which is the default if no registry is specified). If you’re using a private registry, you’ll need to specify the full image path (e.g., myregistry.com/myorg/nginx) and create an ImagePullSecret for Flux to authenticate. For more advanced networking configurations or private registry access, you might explore solutions like Cilium WireGuard Encryption if your registry is in a separate network segment.
git add .
git commit -m "Add ImageRepository for nginx"
git push origin main
Verify
Check if the ImageRepository is created and has successfully scanned for images.
flux get imagerepositories -n flux-system
Expected output (tags will vary):
NAME IMAGE LAST SCAN LAST SUCCESS TAGS READY MESSAGE
nginx nginx 2023-10-27T10:00:00Z 2023-10-27T10:00:00Z 1300 True successful scan, found 1300 tags
Step 4: Define ImagePolicy
The ImagePolicy custom resource defines the rules for selecting the “latest” image tag from the tags discovered by the ImageRepository. This is crucial for determining which new image version Flux should apply. You can specify various policies, such as semantic versioning, alphabetical sorting, or even regular expressions.
Continue adding to ./clusters/my-cluster/image-automation.yaml:
# ./clusters/my-cluster/image-automation.yaml (append to existing file)
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: nginx-policy
namespace: flux-system
spec:
imageRepositoryRef:
name: nginx
filterTags:
pattern: '^1\.21\.(?P<version>[0-9]+)$' # Match tags like 1.21.x
policy:
semver:
range: '1.21.x' # Select the highest semantic version within 1.21.x
In this example, we’re using a semantic versioning policy to select the highest patch version within the 1.21.x range. This is a common and recommended approach for production environments to ensure stability while still getting updates. If you wanted the absolute latest tag, you could use alphabetical: {} or numerical: {} with a different pattern. For more complex scenarios, refer to the Flux Image Automation documentation.
git add .
git commit -m "Add ImagePolicy for nginx"
git push origin main
Verify
Check if the ImagePolicy has been created and has resolved a latest image tag.
flux get imagepolicies -n flux-system
Expected output (resolved image will vary):
NAME IMAGE LAST UPDATE RESOLVED IMAGE READY MESSAGE
nginx-policy nginx 2023-10-27T10:05:00Z nginx:1.21.6 True successfully resolved image 'nginx:1.21.6'
Step 5: Configure ImageUpdateAutomation
The ImageUpdateAutomation custom resource is the heart of this process. It orchestrates the entire automation flow: it watches ImagePolicy resources, detects when a new image is resolved, finds the corresponding marker in your Git repository, updates the manifest, commits the change, and pushes it back to Git. This is where the magic happens, effectively closing the GitOps loop.
Add this to your ./clusters/my-cluster/image-automation.yaml file:
# ./clusters/my-cluster/image-automation.yaml (append to existing file)
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcd@example.com
name: fluxcd
messageTemplate: '{{ .Updated.String }}' # Commit message template
interval: 1m0s # How often to check for updates and commit
sourceRef:
kind: GitRepository
name: flux-system
update:
# Path to the directory containing the manifests that Flux should update
path: ./apps/nginx
strategy: Set
Key parameters:
git.checkout.ref.branch: The branch Flux should update.git.commit.messageTemplate: Defines the commit message when Flux pushes changes.{{ .Updated.String }}is a common choice, showing the updated image.interval: How often the automation runs.sourceRef: Points to theGitRepositorycontaining your manifests.update.path: Crucially, this is the path within your Git repository where Flux will look for files to update. This should correspond to the path where yourdeployment.yaml(with the# {"$imagepolicy": "flux-system:nginx-policy"}marker) resides.
git add .
git commit -m "Add ImageUpdateAutomation"
git push origin main
Verify
Check if the ImageUpdateAutomation resource is ready.
flux get imageupdateautomations -n flux-system
Expected output:
NAME LAST RUN LAST COMMIT LAST PUSH READY MESSAGE
flux-system 2023-10-27T10:10:00Z main/a1b2c3d4 2023-10-27T10:10:00Z True Automation run succeeded
Step 6: Trigger an Image Update
Now, let’s simulate a new image being published. We’ll push a new tag for our NGINX image to Docker Hub. Since we’re using the public NGINX image, we can’t push to it directly. Instead, we’ll manually change the ImageRepository to point to a different, higher version of NGINX, or you can use your own image where you control the tags.
For demonstration, let’s pretend a new nginx:1.21.7 exists. We will temporarily modify our ImagePolicy to only filter for 1.21.7 to force Flux to pick it up, then revert it. In a real scenario, you’d just push a new image tag to your private registry.
Edit ./clusters/my-cluster/image-automation.yaml to temporarily change the filter pattern in nginx-policy:
# ./clusters/my-cluster/image-automation.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: nginx-policy
namespace: flux-system
spec:
imageRepositoryRef:
name: nginx
filterTags:
# Temporarily change pattern to force a specific tag
pattern: '^1\.21\.7$'
policy:
semver:
range: '1.21.x'
git add .
git commit -m "Force NGINX 1.21.7 for demo"
git push origin main
Alternatively, if you have your own image, you would simply build and push a new tag:
docker build -t myregistry.com/myorg/myapp:1.0.1 .
docker push myregistry.com/myorg/myapp:1.0.1
Verify
Watch your Git repository for a new commit from Flux. Then, check your deployment.
# Check Git history (e.g., on GitHub website or with git log)
# You should see a commit like: "nginx:1.21.6@sha256:..." or "nginx:1.21.7@sha256:..."
# Check the deployment image
kubectl get deployment nginx-app -n default -o jsonpath='{.spec.template.spec.containers[0].image}'
Expected output (example, will vary based on resolved image):
nginx:1.21.7
You can also check the Flux logs to see the automation in action:
kubectl logs -f -n flux-system deploy/image-automation-controller
You should see log entries indicating that the controller is detecting changes, updating manifests, and committing them to Git.
Once you’ve verified the update, remember to revert the filterTags in your ImagePolicy back to the original pattern if you want to resume monitoring the broader 1.21.x series, then commit and push again.
Production Considerations
- Image Pull Secrets: For private registries, ensure Flux has access to the necessary
ImagePullSecrets. These secrets should be referenced by the workload (Deployment, StatefulSet) and potentially by the FluxImageRepositoryif the image reflector needs to authenticate to scan tags. - Tagging Strategy: Carefully consider your image tagging strategy. Semantic versioning (e.g.,
1.2.3) is highly recommended. Avoid using mutable tags likelatestin production, as they can lead to unpredictable deployments. Flux’sImagePolicyworks best with well-defined, immutable tags. - Granular Automation: You can have multiple
ImageRepositoryandImagePolicyresources for different images or different update strategies (e.g., critical security patches vs. minor feature updates). - Commit Message Templates: Use descriptive commit message templates in
ImageUpdateAutomationto easily track automated updates in your Git history. Including the old and new image tags is very helpful. - Rate Limiting: Be mindful of Docker Hub’s rate limits if you’re pulling public images frequently. For production, consider using a caching proxy or mirroring images to a private registry.
- Monitoring and Alerting: Set up monitoring for your Flux controllers, especially the Image Reflector and Image Automation controllers. Alerts for failed scans or automation runs are crucial. You can leverage tools like Prometheus and Grafana for this. For advanced observability, consider using eBPF Observability with Hubble to monitor network interactions related to image pulls.
- Rollback Strategy: While Flux automates updates, ensure you have a clear rollback strategy. If an automated image update introduces a bug, you’ll need a way to quickly revert to a previous working version by reverting the commit in Git. Flux will then synchronize back to the reverted state.
- Integration with CI/CD: Image automation complements your CI/CD pipeline. Your CI pipeline should build and push new images with unique tags. Flux then takes over for deployment. Consider integrating Sigstore and Kyverno to ensure only signed and trusted images are deployed.
- Resource Limits: Ensure Flux controllers have appropriate CPU and memory limits/requests in the
flux-systemnamespace to prevent resource exhaustion.
Troubleshooting
-
Issue: Flux controllers are not running or are in a CrashLoopBackOff state.
Solution: Check the logs of the problematic controller pod in the
flux-systemnamespace. Common issues include incorrect Git repository URL, invalid SSH keys, or network connectivity problems. Ensure your Git PAT or SSH key is correctly configured and has the necessary permissions.kubectl logs -f -n flux-system deploy/source-controller kubectl describe pod -n flux-system <pod-name> -
Issue:
ImageRepositorystatus showsFalseor “failed scan”.Solution: This usually means Flux cannot access the container registry or cannot find the specified image. Verify the
imagefield in yourImageRepositoryis correct. If it’s a private registry, ensure Flux has the correctImagePullSecretconfigured and mounted, and that the secret is in the same namespace as theImageRepository(flux-systemby default).flux get imagerepositories -n flux-system -o yaml kubectl logs -f -n flux-system deploy/image-reflector-controller -
Issue:
ImagePolicyis not resolving a new image tag.Solution: Check the
filterTagsandpolicysections of yourImagePolicy. The pattern might not be matching the new image tags, or the policy might not be selecting the desired tag. Test your regex pattern with the actual image tags available in your registry. Ensure theImageRepositoryRefpoints to the correctImageRepository.flux get imagepolicies -n flux-system -o yaml -
Issue: Deployment image is not updating in the cluster, but Flux shows a successful automation run.
Solution: This indicates that Flux successfully updated the manifest in Git, but the
KustomizationorHelmReleaseresponsible for applying that manifest to the cluster is not picking up the change. Verify theintervalof yourKustomizationand ensure itssourceRefis correctly pointing to the Git repository. Also, check thepathin theKustomizationto ensure it covers the updated manifest.flux get kustomizations -n flux-system flux reconcile kustomization <kustomization-name> -n flux-system --with-source -
Issue: Flux is making commits to Git, but the image tag is not changing in the YAML.
Solution: The most common reason is a missing or incorrect image automation marker. Ensure your deployment’s image field has the exact comment:
# {"$imagepolicy": "flux-system:<your-image-policy-name>"}. Also, double-check that theImageUpdateAutomation‘supdate.pathcorrectly points to the directory containing the manifest to be updated.# Check your deployment.yaml file content in Git # Check the ImageUpdateAutomation resource flux get imageupdateautomations -n flux-system -o yaml -
Issue: Flux is updating the image in Git, but it’s not the latest version or the desired version.
Solution: Review your
ImagePolicycarefully. ThefilterTagspattern or thepolicy(e.g.,semver.range) might be too restrictive or selecting an unexpected tag. Ensure your image tags follow a consistent format that the policy can correctly interpret. You might need to adjust the regex or semantic version range.flux get imagepolicies -n flux-system -o yaml
FAQ Section
-
What is the difference between Flux CD and Argo CD?
Both Flux CD and Argo CD are popular GitOps tools for Kubernetes. While they share the core principle of GitOps (desired state in Git, actual state in cluster), they have different architectures and feature sets. Flux CD is built on a set of independent controllers, while Argo CD is a single application. Flux tends to be more declarative and Git-centric, often preferring Kustomize, whereas Argo CD has a richer UI and supports various templating tools out-of-the-box. For more insights into CI/CD and deployment strategies, consider how tools like Kubernetes Gateway API can enhance traffic management for these deployments.
-
Can Flux CD automate updates for Helm charts?
Yes, Flux CD can automate updates for Helm charts. For Helm releases, instead of annotating a Deployment, you would update the
ImagePolicyreference directly within yourHelmReleaseresource. Flux’s Helm controller will then render the chart with the new image tag. You can find more details in the Flux CD Helm documentation. -
How does Flux handle rollbacks if a new image causes issues?
Flux CD itself doesn’t automatically roll back deployments based on application health. Its primary function is to synchronize the cluster state with Git. If a new image causes an issue, the standard GitOps practice is to revert the commit in your Git repository that introduced the problematic image tag. Flux will then detect this reversion and apply the previous, working state to your cluster. This maintains Git as the single source of truth.
-
Is it safe to use
latesttag with Flux Image Automation?While technically possible to configure
ImagePolicyto select thelatesttag, it is generally discouraged, especially in production. Thelatesttag is mutable and can change without warning, making deployments unpredictable and rollbacks difficult. It’s best practice to use immutable, versioned tags (e.g.,1.0.0,v2.1-rc1) and leverage Flux’s semantic versioning or alphabetical policies to manage updates safely. For advanced scaling and cost optimization with such deployments, consider exploring tools like Karpenter for Kubernetes cost optimization. -
How can I use Flux Image Automation with private container registries?
To use private registries, you need to provide Flux with credentials. This is typically done by creating a Kubernetes
Secretof typekubernetes.io/dockerconfigjsoncontaining your registry credentials. This secret should be in theflux-systemnamespace. You then reference this secret in yourImageRepositoryresource using thesecretReffield. For example:apiVersion: image.toolkit.fluxcd.io/v1beta1 kind: ImageRepository metadata: name: my-private-app namespace: flux-system spec: image: myregistry.com/myorg/myapp interval: 1m0s secretRef: name: my-registry-secret
Cleanup Commands
To remove Flux CD and all its components from your cluster, as well as the resources created during this tutorial, follow these steps:
# Remove Flux CD from the cluster
flux uninstall --namespace=flux-system --keep-namespace=false
# Remove the manifests from your Git repository (optional, but good practice)
# Navigate to your Git repository root
# git rm -rf clusters/my-cluster apps/nginx
# git commit -m "Remove Flux CD and demo app"
# git push origin main
# Delete the Kubernetes namespace for the application if not default
# kubectl delete namespace default # BE CAREFUL: This deletes everything in default namespace
Confirm Flux components are gone:
kubectl get ns flux-system
# Should show "NotFound"
Next Steps / Further Reading
- Explore more advanced Flux CD features, such as multi-tenancy, secrets management with SOPS, and notifications. The Flux CD documentation on repository structure is an excellent resource.
- Deep dive into Kubernetes concepts to better understand the underlying platform.
- Learn about GitOps principles and how they can transform your development and operations workflows.
- Investigate how Flux CD integrates with other CNCF projects like Prometheus for monitoring or OPA/Kyverno for policy enforcement. For instance, combining image automation with policies to ensure compliance is a powerful pattern, as seen in Securing Container Supply Chains with Sigstore and Kyverno.
- Consider using a service mesh like Istio Ambient Mesh to manage traffic, security, and observability for your automatically updated applications.
Conclusion
Flux CD’s image automation capabilities are a game-changer for continuous delivery in Kubernetes. By leveraging Git as the single source of truth and automating the process of detecting new images, updating manifests, and applying changes, Flux significantly reduces operational overhead and speeds up the delivery of new features and critical updates. This guide has walked you through the complete setup, from bootstrapping Flux to triggering your first automated image update. Embrace this powerful GitOps pattern to build a more efficient, reliable, and scalable cloud-native platform.