What is ArgoCD?
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It watches Git repositories and automatically syncs the desired state defined in Git to the actual state running in your cluster. It is a CNCF Graduated project and one of the most widely adopted CD tools in the cloud-native ecosystem.
GitOps Principles (OpenGitOps)
Declarative
Desired state described declaratively in YAML/JSON in Git
Versioned
Git history is the audit trail; rollback = git revert
Automated
Software agents apply desired state automatically
Continuously Reconciled
Agents detect and correct drift from desired state
Push vs Pull
Traditional CI/CD pushes to clusters. ArgoCD agents pull from Git inside the cluster — no external access needed, no exposed cluster credentials.
Continuous Reconciliation
ArgoCD polls Git every 3 minutes by default. GitHub/GitLab webhooks trigger immediate syncs. Drift is detected and corrected automatically.
Multi-Cluster
A single ArgoCD instance can deploy to dozens of clusters. Clusters are registered by kubeconfig credentials stored as Kubernetes Secrets.
Source Types
- Helm charts (v2/v3)
- Kustomize overlays
- Plain directory of YAML
- Jsonnet
- Config Management Plugins (CMP)
Architecture
┌─────────────────────────────────────────────────────────────────────┐ │ ArgoCD Control Plane │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │ │ │ API Server │ │ Repo Server │ │ Application Controller │ │ │ │ gRPC / REST │ │ Git clone │ │ Kubernetes controller │ │ │ │ Web UI │ │ render tmpl │ │ reconciliation loop │ │ │ │ CLI gateway │ │ Helm/Kust │ │ sync / health check │ │ │ └──────┬──────┘ └──────┬──────┘ └──────────┬────────────┘ │ │ │ │ │ │ │ ┌──────▼──────────────────────────────────────────────────────┐ │ │ │ Redis Cache │ │ │ │ (app state, repo cache, cluster cache) │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │ │ │ Dex │ │ Notification│ │ ApplicationSet Ctrl │ │ │ │ OIDC/SSO │ │ Controller │ │ generates Applications│ │ │ └─────────────┘ └─────────────┘ └───────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ▼ kubectl / kubeconfig ▼ watches Git repos ┌─────────────────────┐ ┌────────────────────┐ │ Target Cluster(s) │ │ Git Repository │ │ Deployments, Svcs │ │ Helm / Kustomize │ │ CRDs, ConfigMaps │ │ plain YAML │ └─────────────────────┘ └────────────────────┘
Core Components
API Server
- Exposes gRPC + REST API
- Serves the Web UI
- CLI (
argocd) gateway - Auth/RBAC enforcement
- Webhook receiver
Repo Server
- Clones Git repos (caches locally)
- Renders Helm templates
- Runs Kustomize builds
- Runs Config Management Plugins
- Stateless — horizontally scalable
Application Controller
- Core reconciliation loop
- Compares live vs desired state
- Triggers syncs
- Runs health checks
- Sharded for scale
Dex
- Built-in OIDC provider
- SSO bridge (GitHub, LDAP, SAML)
- Optional — can use external OIDC
Redis
Caches app state, repo content, cluster state. Required for HA. All components share it.
ApplicationSet Controller
- Generates Application CRDs from templates
- Multi-cluster, multi-env automation
- Replaces manual App creation
Core Concepts
Application CRD — The Core Unit
Every deployment is an Application custom resource. It binds a Git source to a Kubernetes destination.
AppProject — Isolation & RBAC Scoping
AppProject controls which Git repos, clusters, and namespaces a set of Applications can use. Essential for multi-team setups.
Sync & Health Status
Every Application has two independent status dimensions. Both must be understood together.
Sync Status
Does the live cluster state match the desired Git state?
| Status | Meaning |
|---|---|
| Synced | Live matches Git exactly |
| OutOfSync | Drift detected — needs sync |
| Unknown | Cannot determine (permission, network) |
Health Status
Is the deployed application actually healthy?
| Status | Meaning |
|---|---|
| Healthy | All resources ready and serving |
| Progressing | Deployment rolling out |
| Degraded | Pod CrashLoopBackOff, etc. |
| Suspended | CronJob, HPA paused |
| Missing | Resource not found in cluster |
Sync Strategies
Manual Sync
ArgoCD detects drift but does not sync automatically. Operator triggers via UI, CLI, or API. Best for production environments requiring human approval.
Automated Sync
ArgoCD automatically syncs when it detects changes. Combine with prune and selfHeal for full GitOps.
Prune
When a resource is removed from Git, ArgoCD deletes it from the cluster. Disabled by default — must be explicitly enabled. Use Prune=false annotation to protect specific resources.
Self Heal
When someone manually edits a cluster resource (via kubectl), ArgoCD detects the drift within minutes and reverts it back to the Git state. Enforces true GitOps.
Sync Options (per Application)
| Option | Effect |
|---|---|
CreateNamespace=true | Creates the destination namespace if it doesn't exist |
ApplyOutOfSyncOnly=true | Only applies resources that have drifted (faster) |
PrunePropagationPolicy=foreground | Wait for child resources to be deleted before parent |
Replace=true | Use kubectl replace instead of apply (for immutable fields) |
ServerSideApply=true | Use server-side apply (recommended for large resources) |
SkipDryRunOnMissingResource=true | Skip dry-run for CRDs not yet installed |
Validate=false | Disable kubectl schema validation |
RespectIgnoreDifferences=true | Honor ignoreDifferences during live state comparison |
Resource Hooks
Hooks are Kubernetes Jobs annotated to run at specific phases of the sync lifecycle. Use them for database migrations, smoke tests, notifications, and cleanup.
Hook Delete Policies
| Policy | When Deleted |
|---|---|
HookSucceeded | After successful completion |
HookFailed | After failure (for debugging) |
BeforeHookCreation | Before next hook run (default) |
Common Hook Use Cases
- PreSync: DB migrations, schema checks
- Sync: Apply resources in specific order
- PostSync: Smoke tests, cache warm-up
- SyncFail: Alert team, rollback notification
- Skip: Exclude resource from sync
App of Apps Pattern
A "root" Application points to a directory of other Application manifests. This enables bootstrapping an entire environment by syncing one app. The root app creates child Applications, which in turn deploy workloads.
Git Repo ├── apps/ ← Root App points here │ ├── api-service.yaml ← Application CRD │ ├── web-frontend.yaml ← Application CRD │ ├── postgres.yaml ← Application CRD │ └── redis.yaml ← Application CRD │ └── services/ ├── api-service/ ← Actual K8s manifests ├── web-frontend/ ├── postgres/ └── redis/ Root App syncs → creates 4 child Applications Each child App syncs → deploys its service
ApplicationSet
ApplicationSet is a higher-level CRD that automatically generates Application objects from a template and one or more generators. It eliminates repetitive Application YAML for multi-env or multi-cluster deployments.
Generators
List Generator
Static list of key-value pairs. Generates one App per item.
generator: list
Cluster Generator
One App per registered cluster. Filter by cluster labels.
generator: clusters
Git Generator
One App per directory or file found in a Git repo path.
generator: git
Matrix Generator
Cartesian product of two generators (e.g. clusters × environments).
generator: matrix
Merge Generator
Merges multiple generator outputs with override logic.
generator: merge
SCM Provider
One App per repo in a GitHub Org, GitLab Group, or Bitbucket.
generator: scmProvider
Pull Request
One App per open PR — preview environments per PR.
generator: pullRequest
Cluster Decision Resource
Integrates with Open Cluster Management (OCM) for dynamic cluster selection.
generator: clusterDecisionResource
Example: Git Directory Generator
Example: Matrix Generator (clusters × environments)
ApplicationSet Policies
true, deleting the ApplicationSet does NOT delete the child Applications or their resources. Safe for production.
goTemplate: true to unlock Go templating syntax (range, if, index) instead of the limited {{param}} substitution.
Multi-Cluster Deployment
ArgoCD manages multiple target clusters from a single control plane. Clusters are registered as Kubernetes Secrets in the argocd namespace.
Register Cluster via CLI
argocd cluster add my-prod-cluster --name production — stores kubeconfig as Secret with label argocd.argoproj.io/secret-type: cluster
In-Cluster vs External
The cluster ArgoCD runs on uses https://kubernetes.default.svc. External clusters use their API server URL. Each gets a ServiceAccount with appropriate RBAC.
Target Cluster in Application
destination.server: https://prod-cluster-api:6443 or use destination.name: production (the cluster alias).
ApplicationSet + Cluster Generator
Use the cluster generator to automatically create one Application per registered cluster. Label clusters (e.g. env=prod) and filter by label.
RBAC & Multi-Tenancy
Built-in Roles
| Role | Permissions |
|---|---|
role:readonly | Read everything, sync nothing |
role:admin | Full access to all apps/projects |
Custom roles defined in argocd-rbac-cm ConfigMap using Casbin policies.
Policy Format (Casbin)
Multi-Tenancy Strategies
Namespace Isolation
Each team gets their own namespace. AppProject restricts which namespaces a team's apps can deploy to. Simple and effective.
Cluster Isolation
Each team or environment gets a dedicated cluster. AppProject restricts which cluster(s) are allowed. Maximum isolation — recommended for production vs dev.
Separate ArgoCD
Each team runs their own ArgoCD instance. Maximum autonomy. Higher operational cost. Common at large enterprise scale.
Secrets Management
Never store plaintext secrets in Git. ArgoCD itself doesn't encrypt secrets — you need an external solution. The three most common patterns:
Sealed Secrets
- CLI encrypts Secret →
SealedSecretCRD - Sealed Secrets controller decrypts in cluster
- Encrypted blob safe to store in Git
- Simple, no external dependency
- Hard to rotate the cluster key
External Secrets Operator
- Syncs secrets FROM an external store TO Kubernetes Secrets
- Supports: AWS SM, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, 1Password
- Secret values never stored in Git
- Automatic refresh interval
Vault + ArgoCD Vault Plugin
- Placeholder in YAML:
<path:secret/data/db#password> - ArgoCD Vault Plugin fetches from Vault during sync
- Works with any Vault-compatible store
- Requires CMP configuration
SOPS
- Encrypts YAML/JSON files using age, GPG, KMS
- Encrypted file stored in Git
- Decrypted at render time via CMP
- Works well with Helm values files
Notifications
Argo CD Notifications watches Application events and sends messages to services like Slack, Microsoft Teams, PagerDuty, email, and more.
Triggers (when to notify)
on-sync-succeededon-sync-failedon-health-degradedon-deployedon-sync-running- Custom triggers with expr conditions
Services (where to send)
Argo Rollouts — Progressive Delivery
Argo Rollouts is a separate Kubernetes controller that extends Deployments with progressive delivery strategies: canary and blue/green. It integrates tightly with ArgoCD for GitOps-driven rollouts.
Canary
- Route X% of traffic to new version
- Pause, promote, or abort manually
- Automated analysis with metrics
- Integrates with Istio, Nginx, ALB
Blue/Green
- Run old (blue) and new (green) in parallel
- Switch traffic instantly when ready
- Instant rollback (switch back)
- No gradual traffic shifting
Analysis
- Query Prometheus, Datadog, CloudWatch
- Pass/fail threshold determines auto-promote or rollback
- Custom metrics (error rate, latency p99, business KPIs)
kind: Deployment with kind: Rollout (same spec structure). ArgoCD detects Rollout resources and shows rollout status in the UI.
ArgoCD vs Flux v2
| Aspect | ArgoCD | Flux v2 |
|---|---|---|
| UI | Rich built-in web UI, app graph visualization | No built-in UI (use Weave GitOps or Flux UI add-on) |
| Model | Application CRD — centralized | Kustomization + HelmRelease — distributed (per-cluster) |
| Multi-Cluster | Hub-spoke: one ArgoCD manages many clusters | Each cluster runs its own Flux controllers |
| Pull Request Envs | ApplicationSet PullRequest generator | Flux ImageUpdateAutomation + Notification Controller |
| Secrets | Plugin-based (ESO, AVP, Sealed Secrets) | Native SOPS support; ESO integration |
| Progressive Delivery | Argo Rollouts (separate) | Flagger (separate) |
| RBAC | Casbin policies, SSO/OIDC via Dex | Kubernetes-native RBAC only |
| Helm Support | First-class (Helm chart as source) | First-class (HelmRelease CRD) |
| Learning Curve | Moderate — rich feature set | Moderate — more Kubernetes-native, less GUI |
| Best For | Teams wanting visibility, UI, multi-cluster hub | Teams wanting minimal footprint, decentralized gitops |
Best Practices
Separate App & Config Repos
Keep application source code in one repo and Kubernetes manifests/Helm values in a separate GitOps repo. CI updates the GitOps repo; ArgoCD watches it.
Branch per Environment
Use main for production, staging for staging. Or use a single branch with environment-specific directories. Avoid long-lived feature branches in the GitOps repo.
Enable Self-Heal
Always enable selfHeal: true in production. This ensures any manual kubectl changes are reverted, enforcing Git as the true source of truth.
Use AppProject Per Team
Give each team their own AppProject to scope which repos, clusters, and namespaces they can use. Prevents accidental cross-team interference.
Never Store Secrets in Git
Use Sealed Secrets, ESO, or SOPS. Audit your repo for committed secrets before adopting ArgoCD. Rotate any secrets that were exposed.
Monitor Sync Status
Set up Notifications to alert on sync failures and health degradation. Export ArgoCD metrics to Prometheus and build Grafana dashboards for fleet health visibility.
Use ApplicationSet
Prefer ApplicationSet over manual App-of-Apps YAML for multi-env/multi-cluster deployments. It is more maintainable, DRY, and supports dynamic cluster registration.
PostSync Health Gates
Add PostSync hooks for smoke tests. Combine with Argo Rollouts Analysis for automated canary promotion. Block promotion if error rate exceeds threshold.