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: argocd

The argocd/apps/ directory contains individual Application manifests:

argocd/apps/
  frontend.yaml
  backend-api.yaml
  postgres.yaml
  redis.yaml
  monitoring.yaml

Each 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: monitoring

This 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-team

This 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.yaml

Each environment gets its own ArgoCD Application pointing to its overlay:

spec:
  source:
    path: apps/my-app/overlays/production

Helm 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.yaml
spec:
  source:
    path: apps/my-app
    helm:
      valueFiles:
        - values.yaml
        - values-production.yaml

Helm 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#

  1. Using App of Apps when ApplicationSets would eliminate all the boilerplate. App of Apps is simpler to understand but generates maintenance overhead at scale.
  2. Not using projects to restrict applications. The default project lets any Application deploy anything anywhere. This is a security and operational risk in multi-team environments.
  3. Putting environment-specific values in the base. Base should be environment-agnostic. All environment specifics belong in overlays or environment-specific values files.
  4. 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.