One Application per service per environment means dozens of ArgoCD Application YAMLs. The App-of-Apps pattern and ApplicationSet generator solve this — one definition, infinite applications. This is how platform teams scale GitOps.

The Scaling Problem

Manual: 5 services × 3 envs = 15 Application YAMLs api-staging.yaml, api-production.yaml, api-preview.yaml web-staging.yaml, web-production.yaml, web-preview.yaml ... (and growing) 😫 Adding a service = create 3 more files + kubectl apply App-of-Apps: 1 root app manages everything Root watches apps/ directory Add YAML to apps/ → ArgoCD auto-creates it Or: ApplicationSet generates from template ✨ Adding a service = add directory + push

Pattern 1: App-of-Apps

Root Application watches: apps/ directory api-staging api-production web-staging web-production + Add YAML to apps/ → auto-created!
The Root app watches the apps/ folder. Each YAML in that folder becomes a managed ArgoCD Application.

Root Application

# apps/root.yaml (apply this manually ONCE)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/YOUR_USER/cicd-mastery-gitops.git
    targetRevision: main
    path: apps
    directory:
      recurse: false
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated: { prune: true, selfHeal: true }

Child Application (just add files to apps/)

# apps/api-staging.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-staging
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/YOUR_USER/cicd-mastery-gitops.git
    path: environments/staging
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: staging
  syncPolicy:
    automated: { prune: true, selfHeal: true }
    syncOptions: [CreateNamespace=true]

Adding a new service: Drop a new YAML in apps/ → push → root app syncs → child app created → service deployed. Zero manual ArgoCD interaction.

Pattern 2: ApplicationSet (Even Better)

Instead of writing one YAML per app, generate them from a template:

Generator Scans: services/*/staging/ Finds: api, web, worker → Generates 3 Applications Auto-Generated Applications api-staging web-staging worker-staging Add services/payment/staging/ → 4th app auto-created!
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-services
  namespace: argocd
spec:
  generators:
    # Auto-discover directories matching pattern
    - git:
        repoURL: https://github.com/YOUR_USER/cicd-mastery-gitops.git
        revision: main
        directories:
          - path: 'services/*/staging'
          - path: 'services/*/production'

  template:
    metadata:
      # {{path[1]}} = service name, {{path[2]}} = environment
      name: '{{path[1]}}-{{path[2]}}'
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/YOUR_USER/cicd-mastery-gitops.git
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path[2]}}'
      syncPolicy:
        automated: { prune: true, selfHeal: true }
        syncOptions: [CreateNamespace=true]
ApplicationSet is the platform engineering tool. Create a new directory (services/payment/staging/) with manifests → push → ApplicationSet generator auto-creates the ArgoCD Application → service deployed. Zero config, zero tickets.

When to Use Which

PatternBest ForTradeoff
Individual Apps1-3 services, simple setupDoesn't scale past ~10 apps
App-of-Apps5-20 services, per-app customizationStill one YAML per app, but managed automatically
ApplicationSet20+ services, convention-drivenLess per-app control, more consistency

🧠 Recall Check

  1. What does the "root" application in App-of-Apps watch?
  2. How do you add a new service with App-of-Apps?
  3. What does an ApplicationSet generator do?
  4. As an architect with 30 microservices, which pattern would you choose?
Reveal answers
  1. The apps/ directory in the GitOps repo. Every YAML file in there becomes a managed ArgoCD Application.
  2. Add a new Application YAML file to apps/ and push. The root app auto-syncs and creates it.
  3. It auto-discovers (e.g., directories matching a pattern) and generates Application resources from a template. No per-service YAML needed.
  4. ApplicationSet. At 30 services, you want convention-over-configuration. Define the directory pattern once, and every new service that follows the convention is automatically deployed.

Next lesson: Argo Rollouts — progressive delivery (canary + blue-green) with automated analysis.