GitOps in Practice: ArgoCD vs Flux for Kubernetes Deployments
Both ArgoCD and Flux do GitOps on Kubernetes. Both watch a Git repository, detect drift from the desired state, and reconcile. Both are CNCF projects with strong production track records. The question teams struggle with is: which one fits their workflow and operational model?
This is a practical comparison based on real deployment patterns — not a sales pitch for either tool.
Architecture at a Glance
ArgoCD runs as a set of microservices in your cluster (api-server, repo-server, application-controller, dex, redis). It ships with a web UI that shows application health, sync status, and resource trees. Operators interact with it via the UI, CLI (argocd), or Kubernetes CRDs.
Flux is a collection of controllers installed via the Flux CLI or Helm (source-controller, kustomize-controller, helm-controller, notification-controller, image-automation-controller). No built-in web UI. Operators interact primarily through Kubernetes CRDs and kubectl.
| Property | ArgoCD | Flux v2 |
|---|---|---|
| UI | Built-in web UI | None (use Grafana dashboards or Weave GitOps) |
| CRD type | Application, AppProject | GitRepository, Kustomization, HelmRelease |
| Config language | App of Apps, ApplicationSet | Kustomize overlays, Flux manifests |
| RBAC model | ArgoCD Projects + RBAC | Kubernetes RBAC native |
| Multi-tenancy | Projects with namespace isolation | Separate controllers per tenant |
| Helm support | Helm chart deployment | Helm controller with full lifecycle |
| Multi-cluster | Hub-and-spoke (central ArgoCD) | Push or pull per-cluster agents |
Installation
ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods
kubectl -n argocd rollout status deploy/argocd-server
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d
Access the UI:
kubectl port-forward svc/argocd-server -n argocd 8080:443
# https://localhost:8080 — admin / <password from above>
Flux v2
# Install Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash
# Bootstrap — creates the flux-system namespace, installs controllers,
# and commits flux manifests to your Git repo
flux bootstrap github \
--owner=myorg \
--repository=fleet-infra \
--branch=main \
--path=clusters/production \
--personal
Flux bootstrap is opinionated — it writes its own manifests into your repo and reconciles from there. This is intentional: Flux itself is managed by GitOps.
Defining Applications
ArgoCD Application
# apps/frontend.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: frontend
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: HEAD
path: apps/frontend
destination:
server: https://kubernetes.default.svc
namespace: frontend
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Apply it:
kubectl apply -f apps/frontend.yaml
# or via CLI:
argocd app create frontend --repo https://github.com/myorg/k8s-manifests \
--path apps/frontend --dest-server https://kubernetes.default.svc \
--dest-namespace frontend --sync-policy automated
Flux Kustomization
Flux uses two layers: a GitRepository source and a Kustomization that consumes it.
# clusters/production/sources/k8s-manifests.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: k8s-manifests
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/k8s-manifests
ref:
branch: main
---
# clusters/production/apps/frontend.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: frontend
namespace: flux-system
spec:
interval: 5m
path: ./apps/frontend
prune: true
sourceRef:
kind: GitRepository
name: k8s-manifests
targetNamespace: frontend
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: frontend
namespace: frontend
Commit both files and Flux reconciles them automatically.
Sync Behavior and Drift Detection
Both tools poll for changes. The key difference is the default reconciliation model:
ArgoCD compares live cluster state against Git and surfaces drift in the UI. Auto-sync is opt-in per application — you can leave an app in manual sync mode and approve deployments through the UI or argocd app sync.
Flux reconciles continuously by default. If something drifts from Git, Flux corrects it on the next interval (default 5 minutes). There is no "approve before sync" concept out of the box.
This difference matters for regulated environments. If your change management process requires human approval before production changes apply, ArgoCD's manual sync mode aligns better. Flux's model requires you to build that gate into your Git workflow (e.g., protected branches with required reviews).
Helm Chart Deployment
ArgoCD with Helm
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.14.0
helm:
values: |
installCRDs: true
replicaCount: 2
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: true
Flux with Helm
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: jetstack
namespace: flux-system
spec:
interval: 12h
url: https://charts.jetstack.io
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: flux-system
spec:
interval: 1h
chart:
spec:
chart: cert-manager
version: "v1.14.0"
sourceRef:
kind: HelmRepository
name: jetstack
targetNamespace: cert-manager
values:
installCRDs: true
replicaCount: 2
Flux's Helm controller handles upgrades, rollbacks, and drift correction at the Helm release level — it actually runs helm upgrade rather than applying manifests directly.
Multi-Cluster Management
ArgoCD Hub-and-Spoke
One central ArgoCD instance manages all clusters. Register remote clusters:
argocd cluster add production-us-east --name production-us-east
argocd cluster add production-eu-west --name production-eu-west
Use ApplicationSet to deploy the same app across all clusters:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: frontend
namespace: argocd
spec:
generators:
- clusters: {}
template:
metadata:
name: "{{name}}-frontend"
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests
path: "apps/frontend/overlays/{{name}}"
targetRevision: HEAD
destination:
server: "{{server}}"
namespace: frontend
Flux Per-Cluster
Flux runs inside each cluster and pulls from Git independently. No central controller. Bootstrap each cluster pointing at a cluster-specific path:
flux bootstrap github \
--owner=myorg \
--repository=fleet-infra \
--path=clusters/production-us-east \
--branch=main
The clusters/production-us-east/ path contains manifests specific to that cluster. Shared infrastructure lives in infrastructure/ and gets included via Kustomize references.
The tradeoff: Flux multi-cluster is more resilient (no single point of failure) but harder to get a unified view across clusters without an external observability layer.
RBAC and Multi-Tenancy
ArgoCD introduces its own RBAC layer through Projects:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-payments
namespace: argocd
spec:
description: Payments team applications
sourceRepos:
- https://github.com/myorg/payments-k8s
destinations:
- namespace: payments-*
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ""
kind: Namespace
Flux relies entirely on native Kubernetes RBAC. Each team's Flux Kustomization runs under a specific service account with limited permissions — no separate abstraction layer.
Decision Framework
Choose ArgoCD when:
- Your team values a visual UI for deployment oversight
- You need manual approval gates before production syncs
- You manage many clusters from a central control plane
- Non-engineers (product managers, release managers) need visibility into deployment state
Choose Flux when:
- You prefer pure Kubernetes-native tooling with no extra abstractions
- You want Flux itself to be GitOps-managed (it bootstraps itself into Git)
- Per-cluster autonomy matters more than central observability
- You use image automation (Flux can automatically update image tags in Git when new images are pushed)
Either works well when:
- You're deploying Helm charts at scale
- You need solid Kustomize support
- You want tight integration with Kubernetes RBAC
Both tools are production-grade. The "right" answer usually comes down to whether your team wants a UI-centric or CLI/GitOps-native workflow — not technical capability.
Was this article helpful?
DevSecOps Lead
Security-first mindset in everything I ship. From zero-trust architectures to supply chain security, I make sure your pipeline doesn't become your weakest link.
Related Articles
ArgoCD Image Updater for Automated Container Deployments
Configure ArgoCD Image Updater to automatically detect and deploy new container images to Kubernetes without manual manifest changes or CI triggers.
Mozilla SOPS: Encrypted Secrets in Git for GitOps Workflows That Don't Leak
Use Mozilla SOPS to encrypt secrets in Git for secure GitOps workflows. Covers AGE, AWS KMS, and ArgoCD integration with real examples.
ArgoCD Application Patterns: App of Apps, ApplicationSets, and Beyond
Practical ArgoCD patterns for managing dozens of applications — from App of Apps to ApplicationSets to multi-cluster rollouts. All in code, obviously.
Flux Notification Controller: Alerting On GitOps Drift And Sync Failures
When your GitOps pipeline breaks silently, you find out the worst way — during an incident. The Flux Notification Controller is your early warning system....
Sealed Secrets In GitOps: Managing Kubernetes Secrets Safely In Git Repositories
Managing Kubernetes secrets in Git is one of those problems that sounds simple until you accidentally push a base64-encoded password to a public repo. Seal...
Helmfile: Manage Multiple Helm Releases Like a Pro
Use Helmfile to declaratively manage multiple Helm releases across environments, with layered values, dependencies, and GitOps-friendly workflows.