ArgoCD: A Comprehensive Guide¶
1. What ArgoCD Is¶
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It runs as a set of controllers inside a cluster, continuously compares the desired state (manifests in a Git repo, Helm chart, Kustomize overlay, or plain directory) against the live state of the cluster, and reconciles drift — either automatically or on approval.
Core GitOps principles it enforces:
- Git is the single source of truth for what should be running.
- Declarative — you describe end state, not imperative steps.
- Pulled, not pushed — the cluster pulls changes from Git rather than CI pushing kubectl apply.
- Continuously reconciled — drift is detected and (optionally) auto-corrected.
ArgoCD was originally built by Intuit, is now a CNCF graduated project, and is one of the two dominant GitOps engines alongside Flux.
2. Architecture¶
ArgoCD is composed of several controllers/services, typically deployed via Helm chart or the official install manifests into an argocd namespace:
| Component | Role |
|---|---|
| API Server | gRPC/REST server backing the UI, CLI, and webhook receiver. Handles auth, RBAC enforcement, and application management operations. |
| Repository Server | Clones/caches Git repos and Helm chart repos, renders manifests (via helm template, kustomize build, plain YAML, or a config management plugin), and returns rendered manifests. |
| Application Controller | The reconciliation loop. Watches Application CRs, diffs live vs. desired state using the K8s API, and triggers sync operations. |
| ApplicationSet Controller | Generates Application resources dynamically from generators (list, cluster, git directory/file, matrix, SCM provider, pull request, etc.) — this is how you template many Applications from one spec. |
| Notifications Controller | Sends alerts (Slack, email, webhook, etc.) on sync/health state transitions, defined via subscriptions and triggers. |
| Dex (optional) | OIDC/SSO connector bundled for integrating external identity providers (GitHub, GitLab, Azure AD/Entra ID, LDAP). |
| Redis | Caching layer for the repo server and API server. |
All of this state is expressed as Kubernetes Custom Resources (Application, AppProject, ApplicationSet), so ArgoCD itself is "just" a set of controllers reconciling CRDs — consistent with the Kubernetes operator pattern.
3. Installation¶
Standard non-HA install:
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
For production, use the HA manifests (ha/install.yaml) or the Helm chart (argo/argo-cd), which lets you tune replica counts, resource limits, and Redis HA (Sentinel) via values.yaml. Helm is the more Terraform/GitOps-friendly path since you can pin chart versions and manage config as code.
Access the UI initially via port-forward:
kubectl port-forward svc/argocd-server -n argocd 8080:443
Initial admin password is auto-generated in the argocd-initial-admin-secret.
4. Core Concepts¶
4.1 The Application CRD¶
The fundamental unit. An Application binds a source (Git repo path, Helm chart, or OCI artifact) to a destination (cluster + namespace):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-service
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/repo.git
targetRevision: main
path: k8s/my-service
destination:
server: https://kubernetes.default.svc
namespace: my-service
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
prune: true— deletes resources removed from Git.selfHeal: true— reverts manual/out-of-band cluster changes back to Git state.- Without
automated, sync is manual (via UI/CLI/API) — useful for higher environments where you want a gate.
4.2 AppProject¶
A logical grouping/RBAC boundary. Projects restrict:
- Which repos an Application can source from (sourceRepos).
- Which clusters/namespaces it can deploy to (destinations).
- Which resource kinds are allowed (clusterResourceWhitelist, namespaceResourceBlacklist).
- Role bindings for fine-grained RBAC per team.
This is the primary multi-tenancy mechanism — e.g., a platform-team project can deploy CRDs and cluster-scoped resources, while a dev-team project is locked to namespace-scoped resources in specific namespaces.
4.3 Sync, Health, and Diffing¶
ArgoCD computes a live diff using server-side dry-run/diff logic. Resource health is assessed via built-in health checks (Deployment rollout status, Job completion, PVC bound, etc.) or custom Lua health checks for CRDs that don't have a built-in assessor (common for Argo Rollouts, cert-manager Certificates, etc.).
Sync status: Synced / OutOfSync. Health status: Healthy / Progressing / Degraded / Suspended / Missing / Unknown.
4.4 Sync Waves and Hooks¶
For ordered rollout of interdependent resources (e.g., CRDs before CRs, namespace before workloads, migration Job before Deployment):
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"
Lower waves sync first. Combined with hooks (PreSync, Sync, PostSync, SyncFail) for one-shot Jobs like DB migrations:
metadata:
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
4.5 App of Apps Pattern¶
A parent Application whose source directory contains child Application manifests. This lets you bootstrap an entire cluster's Application set from a single root Application, and manage the app catalog itself as GitOps.
4.6 ApplicationSets¶
The scalable answer to "one Application per environment/cluster/tenant." Generators produce a matrix of parameters that template into many Applications from one ApplicationSet spec. Common generators:
- List — static enumerated values.
- Cluster — one Application per registered cluster (great for fleet rollout of a platform component).
- Git directory/file — one Application per matching path in a repo (self-service: teams add a directory, get an Application).
- Matrix — cartesian product of two generators (e.g., clusters × environments).
- SCM Provider / Pull Request — dynamically generate Applications per repo or per open PR (ephemeral preview environments).
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: services
spec:
generators:
- git:
repoURL: https://github.com/org/repo.git
revision: main
directories:
- path: apps/*
template:
metadata:
name: '{{path.basename}}'
spec:
source:
repoURL: https://github.com/org/repo.git
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
5. Rendering Engines Supported¶
- Plain YAML directories
- Helm (native support — values files, value overrides in the Application spec, Helm hooks translated to sync hooks)
- Kustomize (native — overlays, patches, images, replicas overrides directly in the spec)
- Jsonnet
- Custom Config Management Plugins (CMPs) for anything else (cdk8s, custom templating,
helmfile, etc.), run as sidecars to the repo-server pod.
6. RBAC and SSO¶
- Local
argocd-rbac-cmConfigMap defines policy in Casbin syntax:p, role:dev, applications, sync, project-name/*, allow. - Groups from OIDC claims (via Dex or native OIDC) map to roles — typical pattern is to bind IdP groups (GitHub teams, Azure AD groups) directly rather than managing local users.
AppProject-scoped roles allow project-level self-service RBAC delegated to team leads.
7. Multi-Cluster Management¶
ArgoCD is commonly run as a hub: one management cluster running ArgoCD, registered against N spoke (workload) clusters via argocd cluster add (which creates a Secret holding the target cluster's kubeconfig/service account token). This is ArgoCD's most distinctive architectural strength versus Flux — a single control plane with a UI giving fleet-wide visibility.
Alternative: run ArgoCD per-cluster ("in-cluster only") for stronger blast-radius isolation, at the cost of losing the single-pane-of-glass view (mitigated somewhat by tools like Argo CD Notifications + a shared dashboard, or the newer ApplicationSet cluster generator for fleet-wide app distribution).
8. CLI Essentials¶
argocd login <server>
argocd app list
argocd app get my-service
argocd app sync my-service
argocd app diff my-service
argocd app history my-service
argocd app rollback my-service <revision-id>
argocd repo add https://github.com/org/repo.git --username x --password $TOKEN
argocd cluster add <kube-context-name>
9. Notifications¶
Declarative subscriptions/triggers via annotations or the argocd-notifications-cm/-secret ConfigMaps:
metadata:
annotations:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: platform-alerts
Supports Slack, Teams, email, generic webhooks, PagerDuty, and templated messages referencing the Application object.
10. Progressive Delivery¶
ArgoCD pairs naturally with Argo Rollouts (a separate but related Argo-project CRD/controller) for canary and blue/green deployments with automated analysis (Prometheus/Datadog metric queries gating promotion). ArgoCD deploys the Rollout object; Argo Rollouts drives the progressive traffic shift. The ArgoCD UI has native Rollout visualization built in.
11. Operational Best Practices¶
- Use
selfHealcautiously in shared/dev clusters — it will fight anyone doingkubectl editfor a live debug session; either disable it there or clearly communicate the policy. - Separate config repos from app source repos — decouples app CI (build/test/push image) from CD (bump manifest/tag), often via a bot commit or Image Updater.
- Use ArgoCD Image Updater (separate controller) if you want automated image-tag bumps written back to Git, keeping the "Git is truth" guarantee intact rather than mutating live state out-of-band.
- Pin
targetRevisionto tags/SHAs in production, branches in lower environments. - Use Projects aggressively for blast-radius control, even in single-tenant clusters — it's cheap insurance.
- Resource exclusions (
resource.exclusionsinargocd-cm) to keep the controller from watching noisy, high-churn resources it doesn't need to (e.g.,Endpoints,EndpointSlices) — meaningfully reduces controller CPU/memory at scale. - Sharding: at fleet scale (100s of clusters/Applications), enable Application Controller sharding (
ARGOCD_CONTROLLER_REPLICAS+ shard annotations) to horizontally scale reconciliation.
12. Troubleshooting¶
| Symptom | Likely Cause |
|---|---|
App stuck Progressing |
Health check never resolves — check for a bad readiness probe, or a CRD lacking a custom Lua health check (defaults to Healthy immediately, or Progressing forever depending on config). |
OutOfSync immediately after sync |
Diff normalization issue — a mutating webhook or defaulting controller is rewriting fields post-apply. Add an ignoreDifferences rule. |
| Repo server OOMKilled | Large Helm charts/monorepos on every refresh; increase repo-server resources or enable manifest caching / shallow clones. |
| Sync hangs on hook | PreSync Job never completes — check hook Job logs directly with kubectl logs, ArgoCD just reports the wait. |
| Cluster credentials rejected | Rotated service account token/kubeconfig not updated via argocd cluster add --upsert. |
13. Where ArgoCD Excels¶
- Strong UI/UX and fleet-wide visibility for platform teams supporting many app teams.
- Native, opinionated multi-cluster hub model.
- Rich RBAC/multi-tenancy primitives out of the box (Projects).
- ApplicationSets for large-scale, self-service app onboarding.
- Tight integration with Argo Rollouts for progressive delivery.