The definitive guide to ArgoCD and GitOps. Covers architecture, ApplicationSets, multi-cluster management, sync strategies, when to use ArgoCD, when to avoid it, and production best practices.
GitOps Has Won — And ArgoCD Is Leading the Charge
According to the July 2025 CNCF End User Survey, nearly 60% of Kubernetes clusters now rely on ArgoCD for deployments. The CNCF-graduated project has become the de facto standard for GitOps continuous delivery.
But what makes ArgoCD so dominant? And more importantly — is it right for your team?
What is GitOps?
GitOps is a methodology coined by Weaveworks that uses Git as the single source of truth for infrastructure and application configurations. The name comes from their experience recovering their entire production environment from a catastrophic failure — simply by reapplying their Git-stored configurations.
The Four GitOps Principles
| Principle | Description |
|---|---|
| Declarative | Entire system described as code (YAML manifests) |
| Versioned | All changes tracked in Git with full audit trail |
| Pulled Automatically | GitOps agent pulls and applies changes |
| Continuously Reconciled | Any drift from desired state is automatically corrected |
Pull vs Push: Why Pull Wins
Push-Based (Traditional CI/CD):
1# CI pipeline pushes to cluster - requires distributed credentials2kubectl apply -f manifests/ --kubeconfig=$KUBE_CONFIGPull-Based (GitOps):
1# ArgoCD pulls from Git - no external credentials needed2apiVersion: argoproj.io/v1alpha13kind: Application4metadata:5 name: my-app6spec:7 source:8 repoURL: https://github.com/org/repo.git9 path: manifests/10 destination:11 server: https://kubernetes.default.svcThe pull model is inherently more secure — credentials never leave your cluster.
ArgoCD: The GitOps Powerhouse
ArgoCD is a declarative, GitOps continuous delivery tool that continuously monitors your applications and compares the live state against the desired state in Git. Any deviation triggers an "OutOfSync" status, and ArgoCD can automatically (or manually) reconcile the difference.
CNCF Graduated Status
- Accepted to CNCF: March 2020
- Graduated: December 2022
- Current Version: 3.x (2025-2026)
- Adoption: ~60% of surveyed Kubernetes clusters
Architecture Deep Dive
| Component | Purpose |
|---|---|
| API Server | Exposes REST/gRPC API, serves Web UI, handles RBAC |
| Repo Server | Clones Git repos, generates Kubernetes manifests |
| Application Controller | Watches applications, detects drift, triggers sync |
| Redis | Caches manifests and repo data for performance |
| Dex | SSO integration (OIDC, SAML, LDAP, GitHub, etc.) |
ArgoCD Features That Matter
1. Application CRD: Your Deployment Definition
Everything in ArgoCD starts with the Application custom resource:
1apiVersion: argoproj.io/v1alpha12kind: Application3metadata:4 name: frontend5 namespace: argocd6spec:7 # Which ArgoCD project (for RBAC)8 project: default910 # Where to get manifests11 source:12 repoURL: https://github.com/buun-group/frontend.git13 targetRevision: HEAD14 path: k8s/1516 # Where to deploy17 destination:18 server: https://kubernetes.default.svc19 namespace: frontend2021 # Automation settings22 syncPolicy:23 automated:24 prune: true # Delete resources removed from Git25 selfHeal: true # Revert manual kubectl changes26 syncOptions:27 - CreateNamespace=true2. Sync Policies: Control Your Deployments
1syncPolicy:2 automated:3 prune: true # Remove resources not in Git4 selfHeal: true # Auto-revert manual changes5 allowEmpty: false # Prevent empty deployments67 syncOptions:8 - CreateNamespace=true9 - PrunePropagationPolicy=foreground10 - PruneLast=true11 - ApplyOutOfSyncOnly=true1213 retry:14 limit: 515 backoff:16 duration: 5s17 factor: 218 maxDuration: 3m| Policy | Effect |
|---|---|
prune: true | Deletes resources removed from Git |
selfHeal: true | Reverts manual kubectl changes automatically |
PruneLast: true | Deletes resources only after new ones are healthy |
ApplyOutOfSyncOnly: true | Only syncs changed resources (faster) |
3. ApplicationSets: Scale to Hundreds of Apps
ApplicationSets dynamically generate Applications based on rules:
1apiVersion: argoproj.io/v1alpha12kind: ApplicationSet3metadata:4 name: microservices5 namespace: argocd6spec:7 goTemplate: true8 generators:9 # Generate app for each directory in services/10 - git:11 repoURL: https://github.com/buun-group/gitops.git12 revision: HEAD13 directories:14 - path: services/*1516 template:17 metadata:18 name: '{{.path.basename}}'19 spec:20 project: default21 source:22 repoURL: https://github.com/buun-group/gitops.git23 path: '{{.path.path}}'24 destination:25 server: https://kubernetes.default.svc26 namespace: '{{.path.basename}}'27 syncPolicy:28 automated:29 prune: true30 selfHeal: trueGenerator Types:
| Generator | Use Case |
|---|---|
| Git Directory | App per directory in repo |
| Git File | App per config file (JSON/YAML) |
| Cluster | Deploy to multiple clusters |
| List | Manual list of applications |
| Matrix | Combine multiple generators |
| Pull Request | Preview environments for PRs |
| SCM Provider | Auto-discover repos from GitHub/GitLab org |
4. Multi-Cluster Management
Deploy across clusters from a single ArgoCD instance:
1# Add external cluster2argocd cluster add production-cluster-context34# List managed clusters5argocd cluster list6# SERVER NAME VERSION STATUS7# https://kubernetes.default.svc in-cluster 1.28 Successful8# https://prod.k8s.example.com production 1.28 Successful1# ApplicationSet for multi-cluster deployment2apiVersion: argoproj.io/v1alpha13kind: ApplicationSet4metadata:5 name: multi-cluster-app6spec:7 generators:8 - clusters:9 selector:10 matchLabels:11 tier: production12 template:13 metadata:14 name: 'app-{{.name}}'15 spec:16 destination:17 server: '{{.server}}'18 namespace: my-app5. Resource Hooks: Orchestrate Complex Deployments
Run jobs at specific points in the sync lifecycle:
1# PreSync Hook: Database migration before deployment2apiVersion: batch/v13kind: Job4metadata:5 name: db-migrate6 annotations:7 argocd.argoproj.io/hook: PreSync8 argocd.argoproj.io/hook-delete-policy: HookSucceeded9spec:10 template:11 spec:12 containers:13 - name: migrate14 image: my-app:migrate15 command: ["./migrate.sh"]16 restartPolicy: Never17---18# PostSync Hook: Smoke tests after deployment19apiVersion: batch/v120kind: Job21metadata:22 name: smoke-tests23 annotations:24 argocd.argoproj.io/hook: PostSync25 argocd.argoproj.io/hook-delete-policy: HookSucceeded26spec:27 template:28 spec:29 containers:30 - name: test31 image: my-app:test32 command: ["./smoke-test.sh"]33 restartPolicy: Never| Hook | When It Runs |
|---|---|
| PreSync | Before sync (migrations, backups) |
| Sync | During sync (complex orchestration) |
| PostSync | After successful sync (tests, notifications) |
| SyncFail | If sync fails (alerting) |
| PreDelete | Before app deletion (cleanup) — new in v3.3 |
6. App of Apps Pattern
Bootstrap entire environments with a single Application:
1# Root Application manages all other Applications2apiVersion: argoproj.io/v1alpha13kind: Application4metadata:5 name: production-apps6 namespace: argocd7spec:8 project: default9 source:10 repoURL: https://github.com/buun-group/gitops.git11 path: apps/production/ # Contains Application manifests12 destination:13 server: https://kubernetes.default.svc14 namespace: argocd15 syncPolicy:16 automated:17 prune: true18 selfHeal: true1gitops-repo/2├── apps/3│ └── production/4│ ├── frontend.yaml # Application CRD5│ ├── backend.yaml # Application CRD6│ ├── database.yaml # Application CRD7│ └── monitoring.yaml # Application CRD8└── manifests/9 ├── frontend/10 ├── backend/11 ├── database/12 └── monitoring/One kubectl apply bootstraps your entire environment.
7. RBAC and Multi-Tenancy
ArgoCD Projects enable team isolation:
1apiVersion: argoproj.io/v1alpha12kind: AppProject3metadata:4 name: team-frontend5 namespace: argocd6spec:7 description: Frontend team's project89 # Allowed source repositories10 sourceRepos:11 - 'https://github.com/buun-group/frontend-*'1213 # Allowed deployment targets14 destinations:15 - namespace: 'frontend-*'16 server: https://kubernetes.default.svc1718 # Allowed cluster resources19 clusterResourceWhitelist:20 - group: ''21 kind: Namespace2223 # Team roles24 roles:25 - name: frontend-admin26 policies:27 - p, proj:team-frontend:frontend-admin, applications, *, team-frontend/*, allow28 groups:29 - frontend-team-github-group8. SSO Integration
Connect to your identity provider:
1# argocd-cm ConfigMap2apiVersion: v13kind: ConfigMap4metadata:5 name: argocd-cm6 namespace: argocd7data:8 url: https://argocd.buun.io9 dex.config: |10 connectors:11 - type: github12 id: github13 name: GitHub14 config:15 clientID: $github-client-id16 clientSecret: $github-client-secret17 orgs:18 - name: buun-groupSupported Providers: GitHub, GitLab, Google, Okta, Azure AD, LDAP, SAML 2.0, OIDC
When to Use ArgoCD
Ideal Scenarios
- Kubernetes-native organizations — you're all-in on K8s
- Multi-cluster deployments — dev, staging, production clusters
- Large teams — need RBAC, audit trails, and visibility
- Compliance requirements — audit logs for every change
- Microservices architectures — managing 10+ services
- Platform engineering — building internal developer platforms
- Multi-cloud / hybrid — consistent deployments across providers
When NOT to Use ArgoCD
Overkill Scenarios
- Single app, single environment — too much overhead
- Non-Kubernetes workloads — ArgoCD is K8s-only
- Teams of 1-3 developers — simpler tools suffice
- Rapid prototyping — adds unnecessary complexity
- No Git workflow — GitOps requires Git discipline
ArgoCD Limitations
| Limitation | Workaround |
|---|---|
| CD only (no CI) | Use GitHub Actions, GitLab CI, etc. |
| No security scanning | Add Trivy, Snyk, or Grype |
| No auto rollback | Use Argo Rollouts or external monitoring |
| Kubernetes only | Use Terraform for non-K8s resources |
| Secret management | Use External Secrets, Sealed Secrets, or Vault |
ArgoCD vs Alternatives
ArgoCD vs Flux CD
| Aspect | ArgoCD | Flux CD |
|---|---|---|
| UI | Rich built-in Web UI | No native UI (use Weave GitOps) |
| Architecture | Monolithic application | Modular toolkit |
| Multi-tenancy | Projects and RBAC | Kubernetes-native RBAC |
| Learning curve | Lower (intuitive UI) | Higher (more K8s-native) |
| Image updates | Separate tool (Image Updater) | Built-in |
| Best for | Teams new to GitOps | Kubernetes purists |
Choose ArgoCD if you want a visual dashboard and simpler onboarding.
Choose Flux if you prefer lightweight, Kubernetes-native tooling.
ArgoCD vs Spinnaker
| Aspect | ArgoCD | Spinnaker |
|---|---|---|
| Focus | Kubernetes GitOps | Multi-platform CD |
| Complexity | Moderate | High |
| Resources | Lightweight | Resource-intensive |
| Platforms | Kubernetes only | K8s, VMs, cloud services |
| Deployment strategies | Basic (use Argo Rollouts) | Advanced built-in |
Choose ArgoCD for Kubernetes-focused, simpler operations.
Choose Spinnaker for multi-platform deployments (VMs, cloud services).
Production Best Practices
Repository Structure
1gitops-repo/2├── apps/ # ArgoCD Applications3│ ├── base/4│ └── overlays/5│ ├── dev/6│ ├── staging/7│ └── production/8├── manifests/ # Kubernetes manifests9│ ├── frontend/10│ │ ├── base/11│ │ └── overlays/12│ └── backend/13├── clusters/ # Cluster-specific configs14│ ├── dev-cluster/15│ └── prod-cluster/16└── argocd/ # ArgoCD self-management17 ├── applications/18 └── projects/Anti-Patterns to Avoid:
- ❌ Branches for environments (use directories)
- ❌ Mixing app code with deployment configs
- ❌ Storing secrets in plaintext
Secret Management
ArgoCD doesn't manage secrets — use these approaches:
Option 1: External Secrets Operator
1apiVersion: external-secrets.io/v1beta12kind: ExternalSecret3metadata:4 name: api-keys5spec:6 secretStoreRef:7 name: vault8 kind: ClusterSecretStore9 target:10 name: api-keys11 data:12 - secretKey: api-key13 remoteRef:14 key: secret/my-app/api-keyOption 2: Sealed Secrets
1# Encrypt secret client-side2kubeseal --format yaml < secret.yaml > sealed-secret.yaml3# Commit sealed-secret.yaml to Git (safe!)Sync Windows (Maintenance Windows)
Control when syncs can occur:
1apiVersion: argoproj.io/v1alpha12kind: AppProject3spec:4 syncWindows:5 # Allow syncs only during business hours6 - kind: allow7 schedule: '0 9-17 * * 1-5' # Mon-Fri 9am-5pm8 duration: 8h9 applications:10 - '*'11 # Deny syncs during peak traffic12 - kind: deny13 schedule: '0 12 * * *' # Noon daily14 duration: 2h15 applications:16 - 'production-*'High Availability
For production ArgoCD:
1# Multiple API server replicas2apiVersion: apps/v13kind: Deployment4metadata:5 name: argocd-server6spec:7 replicas: 3 # HA mode8---9# Redis HA with Sentinel10apiVersion: apps/v111kind: StatefulSet12metadata:13 name: argocd-redis-ha14spec:15 replicas: 3Notifications
Alert on sync events:
1apiVersion: v12kind: ConfigMap3metadata:4 name: argocd-notifications-cm5data:6 service.slack: |7 token: $slack-token89 trigger.on-sync-failed: |10 - when: app.status.sync.status == 'Failed'11 send: [app-sync-failed]1213 template.app-sync-failed: |14 slack:15 attachments: |16 [{17 "title": "{{.app.metadata.name}} sync failed",18 "color": "#E96D76",19 "fields": [{20 "title": "Repository",21 "value": "{{.app.spec.source.repoURL}}"22 }]23 }]What's New in ArgoCD 3.x (2025-2026)
| Version | Feature |
|---|---|
| 3.0 | Architecture improvements, security enhancements |
| 3.1 | OCI registry support, CLI plugins |
| 3.2 | PR title filters for ApplicationSets |
| 3.3 | PreDelete hooks, OIDC refresh tokens, enhanced RBAC |
The PreDelete hook in v3.3 was one of the most requested features — finally enabling cleanup logic before application deletion.
Getting Started
Install ArgoCD
1# Create namespace2kubectl create namespace argocd34# Install ArgoCD5kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml67# Get initial admin password8argocd admin initial-password -n argocd910# Access UI (port-forward for testing)11kubectl port-forward svc/argocd-server -n argocd 8080:44312# Open https://localhost:8080Deploy Your First App
1# Login to ArgoCD2argocd login localhost:808034# Create application5argocd app create my-app \6 --repo https://github.com/your-org/your-app.git \7 --path k8s/ \8 --dest-server https://kubernetes.default.svc \9 --dest-namespace my-app1011# Sync application12argocd app sync my-appOr declaratively:
1apiVersion: argoproj.io/v1alpha12kind: Application3metadata:4 name: my-app5 namespace: argocd6spec:7 project: default8 source:9 repoURL: https://github.com/your-org/your-app.git10 path: k8s/11 destination:12 server: https://kubernetes.default.svc13 namespace: my-app14 syncPolicy:15 automated:16 prune: true17 selfHeal: trueThe Verdict
ArgoCD has earned its dominance. For Kubernetes teams wanting GitOps with a visual interface, multi-cluster support, and enterprise features — it's the obvious choice.
But it's not for everyone. If you're running a single app, prototyping, or working outside Kubernetes — simpler tools exist.
The 60% adoption figure isn't accidental. ArgoCD solves real problems for real teams at scale.
Brisbane GitOps Consulting
At Buun Group, we help Queensland organisations implement GitOps with ArgoCD:
- GitOps Strategy — design Git workflows for infrastructure and applications
- ArgoCD Implementation — install, configure, and secure ArgoCD
- Multi-Cluster Setup — manage deployments across environments
- Migration Support — transition from Jenkins/manual deployments to GitOps
- Training — upskill your team on GitOps best practices
We've deployed ArgoCD for Australian enterprises. We know what works.
Ready for GitOps?
Topics
Comments
Sign in to join the conversation
LoginNo comments yet. Be the first to share your thoughts!
Found an issue with this article?
