GitOps with ArgoCD and Flux CD Explained

Photo by Unsplash

Photo by Unsplash
GitOps with ArgoCD and Flux CD represents one of the most significant shifts in Kubernetes delivery practices. The core idea is elegant: your Git repository is the single source of truth for the desired state of your cluster. Any change — whether a new deployment, a config map update, or a Helm chart version bump — goes through a pull request, gets reviewed, and is automatically applied to the cluster. No more kubectl apply from a local machine.
GitOps is a set of practices where the entire system state is versioned in Git and an automated operator continuously reconciles the cluster to match it. The four principles defined by OpenGitOps are: declarative configuration, versioned and immutable state, pulled automatically (the cluster pulls from Git, not CI pushes to the cluster), and continuously reconciled. This approach eliminates configuration drift, makes rollbacks as simple as reverting a commit, and creates a full audit trail of every infrastructure change.
ArgoCD is a pull-based continuous delivery tool that runs inside your Kubernetes cluster and watches Git repositories for changes. It ships with a rich web UI and CLI that show you exactly which resources are in sync and which have drifted from the desired state. ArgoCD supports Helm, Kustomize, jsonnet, and plain YAML — making it adaptable to any repo structure.
# ArgoCD Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app-config.git
targetRevision: HEAD
path: k8s/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Remove resources deleted from Git
selfHeal: true # Revert manual changes in cluster
syncOptions:
- CreateNamespace=trueFlux CD takes a more GitOps-native approach: instead of a monolithic operator, it's a collection of Kubernetes controllers — the source controller, kustomize controller, helm controller, notification controller, and image automation controller. Each controller watches its own custom resource and reconciles independently. Flux integrates tightly with Kustomize and supports multi-tenancy through namespace isolation.
In Flux, you define a GitRepository resource that points to your config repo and specifies a polling interval. The Kustomization resource then references that GitRepository, specifies the path to apply, and declares health checks. Flux will continuously sync the cluster to match the manifests in that path, pruning resources that are removed from Git.
# Flux CD — GitRepository + Kustomization
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/my-app-config
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 10m
path: ./k8s/production
prune: true
sourceRef:
kind: GitRepository
name: my-app
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: my-app
namespace: productionUse separate Git repositories for application code and Kubernetes configuration (the 'app-of-apps' pattern in ArgoCD, or separate source repos in Flux). This prevents your CD system from triggering itself when it commits an image tag update back to the config repo.
Both ArgoCD and Flux support automated image tag updates — a critical feature for teams that want fully automated delivery. When a new container image is pushed to a registry, the image automation controller updates the image tag in your Git config repo, which triggers a sync and deploys the new version. This creates a complete, Git-driven delivery loop without any manual steps.
Flux's image automation controllers use two resources: an ImageRepository that polls a container registry on an interval, and an ImagePolicy that selects which tags to promote based on semver ranges, alphabetical ordering, or regex patterns. A third resource, ImageUpdateAutomation, commits the updated tag back to Git — completing the loop.
# Image Updater — auto-update image tags from a registry
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-app
namespace: flux-system
spec:
image: ghcr.io/myorg/my-app
interval: 5m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-app
namespace: flux-system
spec:
imageRepositoryRef:
name: my-app
policy:
semver:
range: ">=1.0.0" # Only promote semver releasesEnabling automated sync with selfHeal and prune on production is powerful but risky. Any accidental Git commit (wrong file, merge conflict resolution, automated PR merge) will immediately change production. Many teams enable auto-sync only for staging and require a manual sync approval step for production using ArgoCD's sync windows or Flux's Kustomization suspend feature.
ArgoCD is the better choice if you want a rich UI, RBAC-controlled multi-cluster management, and a single-pane-of-glass view of all your applications. Flux is the better choice if you prefer a lightweight, controller-based approach that fits naturally into existing Kubernetes tooling, or if you need strong multi-tenancy and GitOps automation at scale. Both are CNCF projects with strong communities — the 'best' choice depends on your team's preferences.
GitOps introduces some new vocabulary alongside familiar Kubernetes terms: GitOps, ArgoCD, Flux CD, Kustomize, and Helm.
To get started with GitOps in production: begin with a single non-critical application, use Kustomize overlays to manage environment differences, set up Slack notifications for sync events and health status changes, and implement branch protection on your config repo so all changes require a PR review. Gradually expand GitOps to cover your entire cluster as your team builds confidence in the workflow.
Sources & Further Reading