Argo CD¶
Argo CD is a GitOps continuous delivery tool for Kubernetes. It watches Git repositories and continuously reconciles the cluster state toward what's declared in those repositories.
The core GitOps contract: the cluster is always a function of Git. No manual kubectl apply. No undocumented hotfixes. Every change is a commit, every deployment is a merge.
Architecture¶
flowchart TD
Git[(Git repository\n source of truth)] --> RepoServer[repo-server\nclones + renders manifests]
RepoServer --> Controller[application-controller\ndiff + sync logic]
Controller --> K8s[Kubernetes API\ntarget cluster]
K8s --> Controller
User --> APIServer[argocd-server\nUI + CLI + API]
APIServer --> Controller
Redis[(Redis\ncache + queue)] --> Controller
Redis --> APIServer
Dex[dex\nSSO provider] --> APIServer
application-controller: the reconciliation heart. Compares desired state (from repo-server) with live state (from Kubernetes API). Fires syncs when they drift. Runs as a StatefulSet - each shard owns a partition of Applications.
repo-server: clones repositories, renders manifests (plain YAML, Helm, Kustomize, Jsonnet), and caches results. Stateless and horizontally scalable.
argocd-server: the API and UI gateway. Handles authentication, RBAC, webhook ingestion from Git hosts.
dex: bundled OIDC provider for SSO integration (GitHub, Google, LDAP, SAML).
Core concepts¶
Application: the fundamental unit. Maps a source (Git repo + path + revision) to a destination (cluster + namespace).
AppProject: groups Applications and enforces constraints - which repos are allowed as sources, which clusters and namespaces are allowed as destinations, which resource kinds can be deployed.
Sync: the act of making the live cluster state match the desired state in Git. Can be manual or automatic.
Application definition¶
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service
namespace: argocd
spec:
project: platform
source:
repoURL: https://github.com/myorg/k8s-config
targetRevision: main
path: apps/api-service/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # delete resources removed from Git
selfHeal: true # re-sync if cluster state drifts from Git
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- ApplyOutOfSyncOnly=true # only apply changed resources, not the full set
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
prune: true - without this, resources removed from Git are left orphaned in the cluster. Enable it for fully automated environments; leave it off if you have manually-managed resources that Argo CD should ignore.
selfHeal: true - Argo CD watches the cluster for drift and re-applies the desired state automatically. Useful for enforcing immutability against unauthorized kubectl changes.
AppProject¶
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: platform
namespace: argocd
spec:
description: Platform team workloads
sourceRepos:
- https://github.com/myorg/k8s-config
- https://charts.bitnami.com/bitnami
destinations:
- namespace: "production"
server: https://kubernetes.default.svc
- namespace: "staging"
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ""
kind: Namespace
namespaceResourceBlacklist:
- group: ""
kind: ResourceQuota
roles:
- name: app-developer
policies:
- p, proj:platform:app-developer, applications, sync, platform/*, allow
- p, proj:platform:app-developer, applications, get, platform/*, allow
groups:
- myorg:platform-developers
App of Apps pattern¶
The App of Apps pattern uses a root Application that manages a directory of other Application manifests. When you add a new service, you commit its Application YAML and Argo CD self-registers it.
k8s-config/
└── apps/
├── root-app.yaml
└── applications/
├── api-service.yaml
├── worker.yaml
└── database.yaml
# root-app.yaml - applied once manually to bootstrap
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
source:
repoURL: https://github.com/myorg/k8s-config
path: apps/applications
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
ApplicationSet¶
ApplicationSet generates Applications from templates and generators. Use it to manage many clusters or environments without copy-pasting Application YAML.
List generator¶
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: microservices
namespace: argocd
spec:
generators:
- list:
elements:
- service: api
namespace: production
- service: worker
namespace: production
template:
metadata:
name: "{{service}}"
spec:
project: platform
source:
repoURL: https://github.com/myorg/k8s-config
path: "apps/{{service}}/overlays/production"
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: "{{namespace}}"
syncPolicy:
automated:
prune: true
selfHeal: true
Git directory generator¶
Generate one Application per directory:
generators:
- git:
repoURL: https://github.com/myorg/k8s-config
revision: main
directories:
- path: "apps/services/*"
Cluster generator¶
Deploy the same application to all matching clusters:
Adding a new cluster with the environment: production label automatically triggers a new Application without any manifest changes.
Sync waves and hooks¶
Sync waves control ordering within a single sync operation. Lower waves deploy first.
Typical wave ordering:
-2: CRDs, Namespaces-1: RBAC, ServiceAccounts, Secrets (via ExternalSecrets)0: Deployments, Services (default)1: post-deployment migration Jobs2: smoke test Jobs
Hooks run Jobs at defined sync lifecycle points and are garbage-collected by Argo CD after completion:
metadata:
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
Available hooks: PreSync, Sync, PostSync, SyncFail, Skip.
Multi-cluster management¶
argocd cluster add production-us-east --name production-us-east
argocd cluster add production-eu-west --name production-eu-west
argocd cluster list
Cluster credentials are stored as Secrets in the argocd namespace. For external clusters, Argo CD uses a dedicated ServiceAccount and short-lived tokens. Use the --service-account flag to specify a pre-created ServiceAccount with scoped RBAC instead of cluster-admin.
RBAC¶
Argo CD's RBAC uses Casbin policies. Define in argocd-rbac-cm:
p, role:readonly, applications, get, */*, allow
p, role:readonly, projects, get, *, allow
p, role:deployer, applications, sync, platform/*, allow
p, role:deployer, applications, get, platform/*, allow
g, myorg:platform-leads, role:admin
g, myorg:developers, role:deployer
Scope roles to AppProjects using proj:platform:role-name pattern for fine-grained control.
Argo Rollouts¶
Argo Rollouts extends Argo CD with progressive delivery - canary and blue-green deployments backed by automated analysis:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- analysis:
templates:
- templateName: success-rate
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
canaryService: api-canary
stableService: api-stable
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
successCondition: result[0] >= 0.95
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{job="api",status!~"5.."}[5m]))
/ sum(rate(http_requests_total{job="api"}[5m]))
If the AnalysisRun fails, Rollouts aborts and rolls back to stable automatically. The Rollout replaces Deployment - manage it through Argo CD like any other resource.
Production patterns¶
Separate config from code: application code repo triggers CI, which builds and pushes the image, then opens a PR against the ops config repo to update the image tag. Argo CD deploys on merge. The audit trail lives in Git, not CI logs.
Protect main: selfHeal: true enforces Git as the source of truth. Combined with branch protection on main, it means no one can make lasting cluster changes via kubectl.
Image updater: the Argo CD Image Updater watches container registries and automatically commits image tag updates to Git when new images are pushed. Useful for CD pipelines where you don't want CI to directly touch the config repo.
Notification controller: send Slack or PagerDuty alerts on sync failure, health degradation, or app creation. Configure via ConfigMap argocd-notifications-cm.
Drift detection: argocd app diff <app> shows what would change if you sync. Use argocd app list --sync-status OutOfSync to catch drift across all applications.