GitOps at Scale: Argo CD, Flux, and the Future of Kubernetes Deployment Pipelines
on Gitops, Kubernetes, Argocd, Flux, Devops, Ci/cd, Platform engineering
GitOps at Scale: Argo CD, Flux, and the Future of Kubernetes Deployment Pipelines
GitOps has graduated from buzzword to mainstream practice. In 2026, the question isn’t whether to use GitOps — it’s how to scale it across dozens or hundreds of clusters without creating a maintenance nightmare.
This post dives deep into Argo CD and Flux, compares their design philosophies, and shares patterns for running GitOps at enterprise scale.
Photo by Growtika on Unsplash
What Is GitOps (Really)?
GitOps is a set of practices defined by four principles (from the OpenGitOps specification):
- Declarative: Describe desired state, not imperative steps.
- Versioned and immutable: Git is the single source of truth.
- Pulled automatically: Software agents apply changes (not pushed by CI pipelines).
- Continuously reconciled: Agents detect and correct drift.
The key insight is #3 and #4: pull-based, continuously reconciling agents. This is fundamentally different from helm upgrade in a CI pipeline, even if the YAML comes from Git.
Argo CD: The Application-Centric Approach
Argo CD is the most widely adopted GitOps tool. Its model is built around the Application CRD.
Core Architecture
┌─────────────────────────────────────┐
│ Argo CD │
│ │
│ ┌──────────┐ ┌────────────────┐ │
│ │ API Svr │ │ Repo Server │ │
│ │ (UI/CLI) │ │ (Git rendering)│ │
│ └──────────┘ └────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Application Controller │ │
│ │ (reconcile loop per app) │ │
│ └──────────────────────────────┘ │
└────────────────┬────────────────────┘
│ kubectl apply
┌───────▼────────┐
│ Target Cluster │
└────────────────┘
Defining an Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-api
namespace: argocd
spec:
project: production
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: main
path: apps/my-api/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
revisionHistoryLimit: 10
ApplicationSets: Managing Many Apps at Once
The ApplicationSet controller is how you manage tens or hundreds of applications without copy-pasting Application manifests.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-addons
spec:
generators:
- matrix:
generators:
- clusters:
selector:
matchLabels:
environment: production
- list:
elements:
- app: cert-manager
namespace: cert-manager
- app: external-dns
namespace: external-dns
- app: keda
namespace: keda
template:
metadata:
name: "-"
spec:
source:
repoURL: https://github.com/myorg/cluster-addons
path: ""
targetRevision: main
destination:
server: ""
namespace: ""
syncPolicy:
automated:
prune: true
selfHeal: true
This single ApplicationSet deploys 3 addons across every production cluster automatically — including clusters added in the future.
Flux: The Composable Toolkit Approach
Flux takes a different philosophy: instead of a single opinionated tool, it’s a collection of composable Kubernetes controllers.
Flux’s CRD Ecosystem
| Controller | CRD | Purpose |
|---|---|---|
| Source | GitRepository, HelmRepository, OCIRepository | Watch sources for changes |
| Kustomize | Kustomization | Apply kustomize overlays |
| Helm | HelmRelease | Manage Helm releases |
| Notification | Alert, Provider, Receiver | Event-driven webhooks |
| Image Automation | ImageRepository, ImagePolicy, ImageUpdateAutomation | Auto-update image tags |
A Complete Flux Setup
# 1. Define the source
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
secretRef:
name: git-credentials
---
# 2. Apply a Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: production-apps
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: k8s-manifests
path: ./apps/production
prune: true
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: my-api
namespace: production
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars
Image Automation: Auto-updating Container Images
Flux’s image automation controllers handle a common pattern: automatically updating the image tag in Git when a new image is pushed.
# Watch for new image tags
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-api
namespace: flux-system
spec:
image: registry.mycompany.com/my-api
interval: 1m
---
# Define which tags to select
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-api
namespace: flux-system
spec:
imageRepositoryRef:
name: my-api
policy:
semver:
range: ">=1.0.0"
---
# Write the new tag back to Git
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
name: my-api
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: k8s-manifests
git:
checkout:
ref:
branch: main
commit:
author:
name: Flux Bot
email: flux@mycompany.com
messageTemplate: "chore: update my-api to "
push:
branch: main
update:
strategy: Setters
Argo CD vs Flux: Design Philosophy Comparison
| Dimension | Argo CD | Flux |
|---|---|---|
| Model | Monolith with UI | Composable toolkit |
| UI | Rich web dashboard | CLI-first (optional UI: Weave Gitops) |
| Multi-tenancy | AppProject isolation | Namespace-based RBAC |
| Drift detection | Real-time diff view | Controller reconciliation |
| Image updates | External (Argo Image Updater) | First-class built-in |
| Helm support | Full (app of apps) | HelmRelease CRD |
| Community | CNCF Graduated | CNCF Graduated |
| Learning curve | Lower (opinionated) | Higher (composable) |
When to choose Argo CD: You want a UI, strong multi-tenancy, and an opinionated workflow. Great for platform teams onboarding many app teams.
When to choose Flux: You want fine-grained composability, image automation, and prefer the GitOps Toolkit’s extensible CRD model. Great for infrastructure teams managing cluster-level addons.
In 2026, many organizations run both — Flux for platform/infra concerns, Argo CD for application delivery.
Scaling Patterns
1. Monorepo vs Polyrepo
Monorepo pattern:
k8s-manifests/
├── clusters/
│ ├── production-us-east/
│ ├── production-eu-west/
│ └── staging/
├── apps/
│ ├── api/
│ │ ├── base/
│ │ └── overlays/
│ └── worker/
└── platform/
├── cert-manager/
└── ingress-nginx/
Pros: atomic cross-service changes, single review process.
Cons: Git history noise, RBAC complexity.
Polyrepo pattern: Each team owns their deployment manifests. App teams commit to their own repo; platform team manages cluster-level infrastructure.
Most large organizations land on polyrepo with a platform monorepo — app teams have independent repos, but cluster addons live together.
2. Progressive Delivery with Argo Rollouts
GitOps + progressive delivery is a powerful combination:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-api
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- setWeight: 25
- pause:
untilVerified: true # Requires analysis to pass
- setWeight: 100
analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: my-api-canary
selector:
matchLabels:
app: my-api
template:
# ... pod spec
The image tag is updated in Git → Argo CD detects the change → Argo Rollouts progressively promotes, analyzing metrics at each step.
3. Secrets Management
Secrets in Git need encryption. Two dominant patterns:
Sealed Secrets (Bitnami):
# Encrypt
kubeseal --controller-name sealed-secrets < secret.yaml > sealed-secret.yaml
git add sealed-secret.yaml # Safe to commit
# The controller decrypts it in-cluster
External Secrets Operator (ESO):
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-api-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: my-api-secrets
data:
- secretKey: DATABASE_URL
remoteRef:
key: production/my-api
property: database_url
ESO pulls secrets from AWS Secrets Manager, GCP Secret Manager, Vault, etc., creating native Kubernetes secrets. Nothing sensitive is ever stored in Git.
Observability for GitOps
Track these signals for a healthy GitOps system:
# Argo CD: apps out of sync
sum(argocd_app_info{sync_status="OutOfSync"}) by (project)
# Argo CD: apps degraded
sum(argocd_app_info{health_status="Degraded"}) by (name)
# Flux: reconciliation failures
sum(gotk_reconcile_error_total) by (kind, namespace, name)
# Flux: reconcile duration
histogram_quantile(0.95, gotk_reconcile_duration_seconds_bucket)
Set alerts on sync failures and reconciliation errors — these indicate your desired state isn’t being applied.
Conclusion
GitOps at scale requires deliberate architectural choices: monorepo vs polyrepo, Argo CD vs Flux, Sealed Secrets vs External Secrets. The good news is that both major tools are CNCF-graduated, production-proven, and increasingly feature-converging.
The teams that get GitOps right treat their Git repositories as carefully as their application code — with reviews, branch protection, and automated testing of manifests. The deployment pipeline becomes an audit log: every change is traceable to a commit, a PR, and a human.
That’s the real promise of GitOps — not just automation, but accountability.
이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)
