Argo CD - GitOps Continuous Delivery for Kubernetes
Who this page is for: in plain English, Argo CD watches a Git repository and keeps your cluster looking exactly like what's committed there. This page is production depth, aimed at readers already comfortable with Pods, Services, and Deployments. New to Kubernetes? Start with the Beginner track.
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.
A note on versions: Argo CD is a CNCF graduated project under the Argo umbrella (Argo CD, Argo Rollouts, Argo Workflows, Argo Events), stewarded by a multi-vendor maintainer group rather than a single company - there is no open-core split to plan around, and commercial distributions (Akuity, Codefresh, Red Hat OpenShift GitOps) package the same upstream. The 3.x line is current as of 2026; because minor releases regularly change defaults around sync behaviour and the repo-server, read the upgrade notes for your specific jump rather than assuming an in-place upgrade is a no-op. Argo Rollouts versions independently of Argo CD - the two are separate installs.
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. As of Argo CD 3.5, the web UI has native ApplicationSet management - list/filter/detail views plus a "Preview Apps" tab that shows which Applications a given template will generate before you commit the generator change, instead of only finding out after the controller reconciles.
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.
Matrix generator¶
The matrix generator combines two (or more) generators into a cross-product - the classic use is fanning a fixed set of applications out across every matching cluster, without hand-writing the combination.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: platform-services-fanout
namespace: argocd
spec:
generators:
- matrix:
generators:
- list:
elements:
- service: api
- service: worker
- service: scheduler
- clusters:
selector:
matchLabels:
environment: production
template:
metadata:
name: "{{service}}-{{name}}"
spec:
project: platform
source:
repoURL: https://github.com/myorg/k8s-config
targetRevision: main
path: "apps/{{service}}/overlays/{{metadata.labels.environment}}"
destination:
server: "{{server}}"
namespace: "{{service}}"
syncPolicy:
automated:
prune: true
selfHeal: true
Three services times however many production clusters match the label selector produces one Application per combination - add a fourth production cluster and you get three new Applications automatically, with zero manifest edits. This is the realistic shape of "deploy N apps to M clusters" in a platform team's actual ApplicationSet, versus the single-generator examples above that only vary one axis at a time.
Sync waves and hooks¶
Sync waves control ordering within a single sync operation. Lower waves deploy first, and Argo CD waits for all resources in a wave to become healthy before proceeding to the next wave.
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.
- PreSync runs before any resources in the sync are applied - database migrations, schema checks, pre-flight validation.
- Sync runs alongside normal resource application, ordered by wave like everything else - rarely used directly since the default apply behavior covers most cases.
- PostSync runs after all resources are synced and healthy - smoke tests, cache warming, sending a deploy notification.
- SyncFail runs only if the sync fails at any point - rollback notifications, cleanup of partially-created state.
Combine waves and hooks for real multi-resource ordering: a PreSync hook at wave -1 runs a migration Job before wave 0 Deployments roll out, and a PostSync hook at wave 2 runs smoke tests only after the Deployments in wave 0 report healthy. Waves order when a resource is applied; hooks additionally tie resource lifecycle to specific sync phases (and get deleted afterward, unlike ordinary waved resources which stay in the cluster as regular objects).
Resource hooks vs Helm hooks¶
When Argo CD deploys a Helm chart, it renders the chart with helm template and applies the output declaratively - it does not invoke helm install/helm upgrade and does not natively execute Helm's own hook lifecycle (pre-install, post-install, pre-upgrade, etc). If a chart you're deploying relies on Helm hooks for correctness (a common pattern in community charts for jobs like schema migrations or cert bootstrapping), Argo CD needs those translated to its own hook annotations, or the chart's hook Jobs will simply be applied as ordinary manifests with no ordering guarantee relative to the rest of the release.
Argo CD does map a subset of Helm hook annotations to its own sync-wave equivalents automatically when it detects helm.sh/hook annotations on rendered resources - but the mapping is approximate (Helm's hook weights and Argo CD's sync waves are different ordering systems), and hook-delete-policy semantics don't line up 1:1 either. For charts with hooks that matter (anything ordering-sensitive), the safer path is to fork/patch the chart's templates to use argocd.argoproj.io/hook and argocd.argoproj.io/sync-wave annotations directly, or to pull the hook resources out of the chart entirely and manage them as separate manifests in the App of Apps tree where you control ordering explicitly.
Diffing and ignoreDifferences¶
Argo CD's OutOfSync status comes from a live diff between the manifest rendered from Git and the live object in the cluster. Any controller that mutates a resource after Argo CD applies it - an admission webhook injecting a sidecar, the HPA changing spec.replicas, a defaulting webhook filling in fields you didn't set - creates a permanent, unresolvable diff: Argo CD wants to revert the field to what's in Git, the other controller immediately changes it back, and (with selfHeal: true) you get a sync loop or a permanently OutOfSync app that never actually needs fixing.
ignoreDifferences tells Argo CD to exclude specific fields from the diff, either at the Application level or the AppProject level:
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA owns this field, not Git
- group: admissionregistration.k8s.io
kind: MutatingWebhookConfiguration
jsonPointers:
- /webhooks/0/clientConfig/caBundle # injected by cert-manager
- group: ""
kind: Service
jsonPointers:
- /spec/clusterIP # server-assigned, not declared in Git
- kind: Deployment
name: api
jqPathExpressions:
- '.spec.template.spec.containers[] | select(.name == "api") | .resources'
jsonPointers handles fixed field paths; jqPathExpressions handles cases where the field to ignore is inside an array and its position isn't stable (a common shape when a sidecar-injecting webhook appends a container to spec.template.spec.containers). There's also a managedFieldsManagers option that ignores any field owned by a specific field manager via server-side apply metadata - useful when you know exactly which controller (by its SSA manager name) is doing the mutating and want to exclude everything it owns rather than enumerating paths by hand.
Common real-world candidates for ignoreDifferences: spec.replicas on anything fronted by an HPA, injected caBundle fields from cert-manager or admission webhooks, metadata.annotations that a service mesh sidecar injector stamps on Pods, and status-adjacent fields that some operators mistakenly place under spec. Resist the urge to reach for ignoreDifferences as a first response to any OutOfSync app, though - it's the right tool for "another controller legitimately owns this field," and the wrong tool for masking a real drift you should instead be fixing at the source.
Multi-tenancy with AppProject¶
AppProject is the primary multi-tenancy boundary in Argo CD - it's how a platform team lets application teams self-serve deployments without giving every team cluster-admin-equivalent reach across the whole fleet.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-payments
namespace: argocd
spec:
description: Payments team -- restricted to their own repos, namespaces, and clusters
sourceRepos:
- https://github.com/myorg/payments-config
destinations:
- namespace: "payments-*"
server: https://kubernetes.default.svc
- namespace: "payments-*"
server: https://staging-cluster.internal
clusterResourceWhitelist: [] # no cluster-scoped resources at all
namespaceResourceWhitelist:
- group: "apps"
kind: Deployment
- group: ""
kind: Service
- group: ""
kind: ConfigMap
- group: ""
kind: Secret
namespaceResourceBlacklist:
- group: ""
kind: ResourceQuota
- group: ""
kind: LimitRange
roles:
- name: deployer
policies:
- p, proj:team-payments:deployer, applications, sync, team-payments/*, allow
- p, proj:team-payments:deployer, applications, get, team-payments/*, allow
groups:
- myorg:payments-team
The four levers that actually enforce tenant isolation:
sourceRepos- the team can only create Applications sourced from repos on this list. Prevents a team from pointing an Application at an arbitrary internal or third-party repo.destinations- restricts which cluster + namespace combinations Applications in this project can target. Thepayments-*namespace glob keeps the team inside their own namespace family even on a shared cluster.clusterResourceWhitelist(empty here) - blocks any cluster-scoped resource (ClusterRole, CustomResourceDefinition, Namespace itself) from being deployed by this project at all. Cluster-scoped resources are the most common privilege-escalation vector in a shared Argo CD instance, so most application teams should have this empty or tightly scoped.namespaceResourceWhitelist/Blacklist- controls which namespaced Kinds the team can deploy. BlockingResourceQuota/LimitRangehere, for instance, stops a team from raising their own resource ceiling from within their own GitOps repo.
Combine AppProject scoping with the Casbin RBAC policies (below) for the full picture: AppProject controls what an Application in this project is allowed to touch; RBAC controls which humans/groups can act on Applications in this project (sync, view, delete). Both are needed - RBAC alone doesn't stop a team with sync rights from deploying a ClusterRoleBinding if the AppProject's clusterResourceWhitelist allows it.
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.
Canary steps with automated analysis¶
The example above runs one AnalysisTemplate at a fixed point in the canary. In practice you usually want analysis running continuously throughout the rollout rather than as a single gate, and you want it comparing the canary against the stable baseline rather than judging the canary against an absolute threshold alone:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 5
- pause: {duration: 2m}
- analysis:
templates:
- templateName: canary-vs-baseline
args:
- name: service-name
value: api-canary
- setWeight: 20
- pause: {duration: 5m}
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
canaryService: api-canary
stableService: api-stable
analysis:
startingStep: 1 # start the background run once step 1 is reached
templates:
- templateName: canary-vs-baseline
args:
- name: service-name
value: api-canary
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: canary-vs-baseline
spec:
args:
- name: service-name
metrics:
- name: error-rate
interval: 1m
count: 5
successCondition: result[0] <= 0.02
failureLimit: 2
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{service="{{args.service-name}}",status=~"5.."}[1m]))
/ sum(rate(http_requests_total{service="{{args.service-name}}"}[1m]))
- name: p99-latency
interval: 1m
count: 5
successCondition: result[0] <= 0.5
failureLimit: 2
provider:
prometheus:
address: http://prometheus:9090
query: |
histogram_quantile(0.99,
sum by (le) (rate(http_request_duration_seconds_bucket{service="{{args.service-name}}"}[1m]))
)
The two analysis blocks in that Rollout are different things, and it's worth being precise about which is which. A step-level analysis: inside steps: is a one-shot gate - it runs at that point in the sequence and the rollout waits on its verdict. strategy.canary.analysis is a background run that stays alive for the rest of the rollout, and startingStep belongs there: it tells the background analysis which step to begin at, so the first small-weight step can bake without alerting on a two-pod sample.
interval/count make this a running check - Rollouts queries Prometheus every minute, five times, and tracks consecutive failures against failureLimit rather than judging on a single sample. Once failureLimit is exceeded, Rollouts aborts the canary, scales it back to zero, and routes all traffic back to stableService automatically - no human has to notice the dashboard and intervene. This is the actual value proposition of progressive delivery over a plain rolling update: a plain Deployment rollout has no concept of "this new version is measurably worse," it just keeps replacing pods.
Blue-green strategy¶
Blue-green skips the gradual weight-shifting of canary and instead runs the full new version alongside the full old version, then cuts traffic over in one step - useful when partial-traffic canarying isn't safe (stateful protocols, schema-sensitive clients) or when you want a trivially fast rollback path.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 10
strategy:
blueGreen:
activeService: api-active
previewService: api-preview
autoPromotionEnabled: false # require manual promotion
prePromotionAnalysis:
templates:
- templateName: canary-vs-baseline
args:
- name: service-name
value: api-preview
scaleDownDelaySeconds: 300 # keep the old version warm for 5m post-promotion
previewService points at the new ReplicaSet before it takes traffic, letting you run smoke tests or manual QA against it. prePromotionAnalysis gates the cutover the same way canary analysis does. scaleDownDelaySeconds keeps the outgoing (old) ReplicaSet running for a window after promotion - if the new version misbehaves once it has full traffic, kubectl argo rollouts undo (or a re-sync to the previous Git revision) switches activeService back to a ReplicaSet that's still warm, rather than a cold start.
Argo CD vs Flux¶
Both are CNCF GitOps controllers reconciling cluster state from Git, but they differ in architecture philosophy and where responsibility sits:
| Argo CD | Flux | |
|---|---|---|
| Architecture | Single UI/API-centric controller set (application-controller, repo-server, argocd-server) with a strong central Application abstraction | A toolkit of small, composable controllers (source, kustomize, helm, notification, image-automation) with no single UI dependency |
| Helm handling | Renders charts via helm template, applies output declaratively; does not run Helm's own release lifecycle or hooks natively |
Runs an actual HelmRelease reconciliation loop via helm-controller, closer to native helm upgrade semantics including hooks |
| Image automation | Separate add-on component (Argo CD Image Updater), not part of core | Built-in first-class controllers (image-reflector-controller, image-automation-controller) that watch registries and commit updates back to Git |
| Multi-tenancy model | AppProject as an explicit, first-class tenancy CRD with source/destination/kind restrictions and scoped RBAC roles | Namespace-scoped Kustomization/HelmRelease objects plus Kubernetes RBAC; less of a dedicated "tenant" abstraction, more conventional K8s-native scoping |
| UI | Full-featured web UI is central to the product experience | No official UI bundled (Weave GitOps and other UIs exist as separate projects) |
| Progressive delivery | Argo Rollouts, a sibling project with tight integration | Flagger, a sibling project with tight integration |
Neither is strictly "better" - Argo CD's centralized Application model and built-in UI make it the more approachable default for teams that want a single pane of glass and are comfortable with a more opinionated, monolithic controller. Flux's toolkit approach and native Helm reconciliation appeal to teams who want composability and who lean heavily on HelmRelease semantics (including hooks) working exactly like helm upgrade would. Some organizations run both: Flux for Helm-heavy platform components where native hook behavior matters, Argo CD for application teams who want the UI and AppProject-based self-service model.
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.
Supply-chain hardening: recent Argo CD releases added internal mTLS between the repo-server and its clients (API server, controllers), with self-signed certs generated in memory when you haven't supplied your own, so there's no filesystem cert-management burden to adopt it. Separately - and this one has been available far longer - Argo CD can require that every commit it deploys from carries a valid GPG signature: you upload trusted public keys to the cluster and list them in the AppProject's signatureKeys. An unsigned commit, or one signed by a key outside that list, is rejected before sync, so a compromised or unsigned commit in an otherwise-trusted repo can't reach the cluster. See GPG verification for the setup.
Common production mistakes¶
selfHeal: truewithoutignoreDifferencesfor HPA/webhook-mutated fields. This produces permanent sync loops - Argo CD reverts a field, the other controller changes it back, repeat. SetignoreDifferencesforspec.replicason anything HPA-managed before enabling selfHeal.- Reaching for
ignoreDifferencesto mask real drift. It's for fields another controller legitimately owns, not a blanket fix for "this app is always OutOfSync." Investigate why a field diverges before excluding it from the diff. - Empty or overly broad
clusterResourceWhiteliston a shared AppProject. Letting application teams deploy ClusterRole/ClusterRoleBinding/CRDs from their own GitOps repo is one of the most common privilege-escalation paths in a multi-tenant Argo CD install. Default it to empty and expand deliberately. - Assuming Helm chart hooks "just work" under Argo CD. Since Argo CD renders via
helm templaterather than runninghelm upgrade, chart-native hooks need translation toargocd.argoproj.io/hookannotations for ordering guarantees to hold - don't assume a chart'spre-installJob runs before the rest of the release. - No
prePromotionAnalysisor automated canary analysis in Argo Rollouts. A canary/blue-green strategy with only manualpausesteps and no metric-based gate still requires a human to notice a regression - the automation only pays off once analysis can abort and roll back on its own. - Skipping AppProject
destinationsrestrictions on a shared cluster. Without a namespace glob or explicit destination list, any Application in the project can target any namespace on any registered cluster, which defeats the purpose of having separate AppProjects per team. - Bootstrapping App of Apps manually every time. Only the root Application should ever be applied by hand; if child Applications are also being
kubectl apply'd directly, you've reintroduced the exact manual-drift problem GitOps is meant to eliminate.
Source Links¶
- Argo CD documentation
- argoproj/argo-cd on GitHub and its releases
- Argo CD upgrade notes
- Application CRD reference
- AppProject and multi-tenancy
- GPG commit signature verification (
signatureKeys) - Sync options, waves, and hooks
- Argo CD Helm support (
helm templaterendering) - ApplicationSet controller
- Argo Rollouts documentation and its analysis / AnalysisTemplate reference
- Argo CD Image Updater
- CNCF project page: Argo
Related Concepts¶
- Flux CD - the other major GitOps engine, composable controllers vs. Argo CD's monolithic app model
- Helm
- Operators and CRDs
- KEDA
- Kyverno