GitOps in Practice: Argo CD vs. Flux (A Real Workflow and the Pitfalls Nobody Warns You About)
Stop running kubectl apply by hand. Here's how two CNCF-graduated tools make Git the single source of truth for your cluster, and how to choose between them.
The 2 AM problem
It’s 2 AM. Production is misbehaving. Someone ran a quick kubectl edit deployment last week to "temporarily" bump the memory limit. Nobody wrote it down, and nobody remembers. Now your Git repo says one thing, your cluster says another, and you're debugging a system that no longer matches its own documentation.
This is configuration drift, and it’s the exact problem GitOps was built to kill.
In this post I’ll cover:
- What GitOps really is (without the buzzwords)
- How Argo CD and Flux differ in philosophy and architecture
- A real end-to-end workflow you can copy
- The pitfalls that trip up almost every team
- A simple guide for choosing between them
What is GitOps, really?
GitOps is an operating model built on four ideas:
- Declarative: you describe the desired state of your system (Kubernetes YAML, Helm charts, Kustomize overlays).
- Versioned: that desired state lives in Git, so every change has an author, a diff, and a history.
- Pulled automatically: an agent inside the cluster pulls the desired state from Git, instead of your CI pipeline pushing to the cluster.
- Continuously reconciled: the agent constantly compares the live state to Git and corrects any drift.
The pull model is the big shift. In traditional CI/CD, your pipeline holds cluster credentials and pushes changes in. With GitOps, the cluster reaches out to Git. Your CI system never needs admin access to production, which is a big security win.
Git is the source of truth. The cluster is just a reflection of it.
Meet the contenders
Both tools are CNCF graduated projects, so both are mature and production-proven. They solve the same core problem in quite different ways.
Argo CD: the visual powerhouse
Argo CD is built around the concept of an Application: a link between a Git path and a destination cluster and namespace. Its standout feature is a polished web UI that shows your resources as a live graph, with health and sync status at a glance.
Key traits:
- Rich UI and CLI, plus built-in SSO/RBAC
- Manages multiple clusters from one control plane
- ApplicationSets for generating apps at scale
- Easy to demo, easy for teams to adopt quickly
Flux: the Kubernetes-native toolkit
Flux is built as a set of small, focused controllers (the GitOps Toolkit) that follow Kubernetes conventions. There’s no big central app. Everything is a Custom Resource, so Flux feels like a natural extension of Kubernetes itself.
Key traits:
- Modular controllers: source, kustomize, helm, notification, and image automation
- CLI-first and lightweight, with no built-in UI
- Strong support for image automation (auto-updating tags in Git)
- Excellent for platform teams who want everything as code
A real workflow: from commit to cluster
Let’s build a realistic setup. First, a repository structure that scales:
gitops-repo/
├── apps/
│ ├── base/
│ │ └── web-app/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ └── overlays/
│ ├── staging/
│ │ └── kustomization.yaml
│ └── production/
│ └── kustomization.yaml
└── clusters/
├── staging/
└── production/The pattern is base + overlays. Shared config lives in base, and environment-specific differences (replicas, resource limits, hostnames) live in the overlays.
The end-to-end flow
- A developer opens a pull request with a code change.
- CI runs tests, builds the container image, and pushes it to a registry.
- CI (or an automation tool) updates the image tag in the GitOps repo via a PR.
- The PR is reviewed and merged, and that review is your change-approval process.
- Argo CD or Flux detects the new commit and reconciles the cluster.
- If anyone changes the cluster manually, the tool reverts it back to Git.
Notice that CI never touches the cluster. It only writes to Git.
Setting it up with Argo CD
Install it, then define an Application:
yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/gitops-repo
targetRevision: main
path: apps/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: web-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueThe two flags that matter most:
selfHeal: truereverts manual changes made in the cluster.prune: truedeletes resources that you've removed from Git.
Setting it up with Flux
Bootstrap Flux into your cluster and point it at your repo:
Get Mohammedyasik R Applogiq’s stories in your inbox
Join Medium for free to get updates from this writer.
bash
flux bootstrap github \
--owner=your-org \
--repository=gitops-repo \
--branch=main \
--path=clusters/production \
--personalThen define a source and a Kustomization:
yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: gitops-repo
namespace: flux-system
spec:
interval: 1m
url: https://github.com/your-org/gitops-repo
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: web-app
namespace: flux-system
spec:
interval: 5m
path: ./apps/overlays/production
prune: true
sourceRef:
kind: GitRepository
name: gitops-repoFlux also manages itself from Git during bootstrap, so even the GitOps tool is version-controlled.
Argo CD vs. Flux: the honest comparison
User interface
- Argo CD: excellent built-in UI, great for visibility and onboarding.
- Flux: no official UI. You work through the CLI,
kubectl, and dashboards like Grafana.
Architecture
- Argo CD: a more centralized application with its own API server, repo server, and controller.
- Flux: a set of independent Kubernetes-native controllers.
Multi-cluster
- Argo CD: one instance can manage many clusters, which is convenient but makes it a critical central point.
- Flux: typically runs in each cluster, which is more decentralized.
Access control
- Argo CD: built-in RBAC and SSO integration.
- Flux: leans on native Kubernetes RBAC and multi-tenancy patterns.
Image automation
- Flux: built-in image reflector and automation controllers.
- Argo CD: handled by the separate Argo CD Image Updater project.
Learning curve
- Argo CD: easier to get started, thanks to the UI.
- Flux: steeper at first, but very consistent once you know Kubernetes CRDs.
The pitfalls (learn from other people’s pain)
1. Putting secrets in Git in plain text
GitOps says “everything in Git,” but that does not include raw secrets. Use Sealed Secrets, SOPS, or the External Secrets Operator with Vault or a cloud secret manager. Decide this on day one, not after your first leak.
2. One giant repository for everything
Dumping every app, environment, and team into one repo leads to noisy history and permission headaches. Separate app code from deployment config, and consider splitting by team or environment as you grow.
3. Fighting the autoscaler
If your Deployment has replicas: 3 in Git and the HPA scales it to 8, the GitOps tool may keep "fixing" it back to 3. Remove the replicas field from the manifest, or configure the tool to ignore that field.
4. Turning on prune without understanding it
Auto-pruning is powerful. A bad merge or a mistaken path change can delete real resources, including things like PersistentVolumeClaims. Test it in staging first and protect critical resources.
5. Treating Git merges as instant deploys
Reconciliation isn’t instant. Flux polls on an interval, and Argo CD polls Git periodically. If you need speed, configure webhooks so Git pushes trigger a sync right away.
6. Skipping progressive delivery
GitOps tells you what to deploy, not how safely. For canary and blue-green rollouts, pair it with Argo Rollouts or Flagger.
7. Ignoring drift alerts
A sync failure that nobody sees is worse than no GitOps at all. Wire up notifications (Slack, Teams, email) so failed reconciliations are loud.
8. Bootstrapping order problems
CRDs must exist before the resources that use them. Use dependency ordering (dependsOn in Flux, sync waves in Argo CD) to avoid mysterious failures on fresh clusters.
So which one should you choose?
Choose Argo CD if:
- You want a rich UI and quick team adoption
- You manage many clusters from one place
- You need built-in SSO and fine-grained RBAC
- Your teams include people who are less comfortable with the CLI
Choose Flux if:
- You prefer a lightweight, modular, Kubernetes-native approach
- You’re a platform team building on top of GitOps
- You want built-in image update automation
- You favor a decentralized, per-cluster model
And remember: plenty of successful companies use one or the other, and some use both. The tool matters much less than the discipline: everything in Git, reviewed by humans, reconciled by machines.
Final thoughts
GitOps isn’t about picking the trendiest tool. It’s about making deployments boring, auditable, and reversible. When something breaks, you don’t SSH into servers or guess what changed. You look at the Git history and run git revert.
Whether you go with Argo CD’s dashboard or Flux’s composable controllers, you’ll be trading late-night firefighting for calm, predictable releases.
Pick one, start small with a single non-critical app, and let Git be your source of truth. Your future self at 2 AM will thank you.




