ArgoCD Patterns#
Once ArgoCD is running and you have a few applications deployed, you hit a scaling problem: managing dozens or hundreds of Application resources by hand is unsustainable. These patterns solve that.
App of Apps#
The App of Apps pattern uses one ArgoCD Application to manage other Application resources. You create a “root” application that points to a directory containing Application YAML files. When ArgoCD syncs the root app, it creates all the child applications.
Root application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-config.git
targetRevision: main
path: argocd/apps
destination:
server: https://kubernetes.default.svc
namespace: argocdThe argocd/apps/ directory contains individual Application manifests:
argocd/apps/
frontend.yaml
backend-api.yaml
postgres.yaml
redis.yaml
monitoring.yamlEach file is a standard Application CRD. When you add a new file to this directory and push to Git, ArgoCD automatically creates the new application. When you remove a file, ArgoCD deletes the application (if prune is enabled on the root app).
This pattern is simple and transparent. The downside is repetition – every Application YAML has boilerplate that differs only in the app name, path, or namespace.
ApplicationSets#
ApplicationSets eliminate that boilerplate. An ApplicationSet is a CRD that generates Application resources from templates and generators.
Git Generator#
Creates one Application per directory in a repo:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/myorg/gitops-config.git
revision: main
directories:
- path: apps/*
template:
metadata:
name: '{{path.basename}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-config.git
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'If the repo has apps/frontend/, apps/backend/, and apps/redis/, this generates three Application resources automatically. Add a new directory, get a new application. Delete a directory, the application is removed.
Cluster Generator#
Creates one Application per registered cluster. Useful for deploying the same stack across multiple clusters:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: monitoring-stack
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: 'monitoring-{{name}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-config.git
targetRevision: main
path: monitoring
destination:
server: '{{server}}'
namespace: monitoringThis deploys the monitoring stack to every cluster labeled env: production. When you register a new production cluster with ArgoCD, the monitoring stack is deployed automatically.
Matrix Generator#
Combines two generators to produce the cross-product. Deploy every app to every cluster:
spec:
generators:
- matrix:
generators:
- git:
repoURL: https://github.com/myorg/gitops-config.git
revision: main
directories:
- path: apps/*
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: '{{path.basename}}-{{name}}'
spec:
source:
path: '{{path}}'
destination:
server: '{{server}}'
namespace: '{{path.basename}}'If you have 5 apps and 3 clusters, this generates 15 Application resources.
Project-Scoped Access Control#
ArgoCD projects restrict what applications can do. The default project allows everything. Production environments should use dedicated projects:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-backend
namespace: argocd
spec:
description: Backend team applications
sourceRepos:
- https://github.com/myorg/backend-manifests.git
destinations:
- namespace: 'backend-*'
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ''
kind: Namespace
namespaceResourceBlacklist:
- group: ''
kind: ResourceQuota
roles:
- name: developer
description: Backend developers
policies:
- p, proj:team-backend:developer, applications, sync, team-backend/*, allow
- p, proj:team-backend:developer, applications, get, team-backend/*, allow
groups:
- backend-teamThis project allows the backend team to deploy only from their repo, only to namespaces starting with backend-, and only sync and view applications – not create or delete them. The clusterResourceWhitelist controls which cluster-scoped resources (like Namespaces or ClusterRoles) applications in this project can create.
Multi-Environment Directory Structures#
Kustomize Overlays#
The most common pattern for managing dev/staging/prod:
apps/my-app/
base/
deployment.yaml
service.yaml
kustomization.yaml
overlays/
dev/
kustomization.yaml # patches for dev
replica-patch.yaml
staging/
kustomization.yaml
production/
kustomization.yaml
replica-patch.yaml
hpa.yamlEach environment gets its own ArgoCD Application pointing to its overlay:
spec:
source:
path: apps/my-app/overlays/productionHelm Values Per Environment#
Use the same chart with different values files:
apps/my-app/
Chart.yaml
values.yaml # defaults
values-dev.yaml
values-staging.yaml
values-production.yamlspec:
source:
path: apps/my-app
helm:
valueFiles:
- values.yaml
- values-production.yamlHelm vs Kustomize as ArgoCD Source#
ArgoCD natively supports both. The choice matters for how teams interact with their deployments.
Helm works well when you consume third-party charts (Prometheus, PostgreSQL, nginx-ingress) and need to override values per environment. ArgoCD renders the Helm template server-side and applies the result. You never run helm install yourself.
Kustomize works well for in-house applications where the base manifests are plain YAML you control. Overlays let you patch specific fields per environment without templating syntax. ArgoCD runs kustomize build and applies the output.
Plain manifests work when you have simple applications and do not need parameterization. ArgoCD applies the YAML files directly from the specified path. This is the simplest option but does not scale when you need per-environment variation.
You can mix approaches in the same ArgoCD instance. Use Helm for third-party charts and Kustomize for your own applications. Each Application resource declares its source type independently.
Common Mistakes#
- Using App of Apps when ApplicationSets would eliminate all the boilerplate. App of Apps is simpler to understand but generates maintenance overhead at scale.
- Not using projects to restrict applications. The
defaultproject lets any Application deploy anything anywhere. This is a security and operational risk in multi-team environments. - Putting environment-specific values in the base. Base should be environment-agnostic. All environment specifics belong in overlays or environment-specific values files.
- Matrix generators without careful naming. The generated Application names must be unique across the entire ArgoCD instance. Use
{{path.basename}}-{{name}}or similar patterns to guarantee uniqueness.