Published: 2026-06-01 β€’ Updated: 2026-07-05

Mastering the ArgoCD Application of Applications Pattern: Enterprise-Scale GitOps Architecture

Deploy, manage, and bootstrap entire Kubernetes clusters dynamically using a single declarative root application. Learn the production-grade patterns, security guardrails, sync waves, and troubleshooting techniques used by elite platform engineering teams.


What is the ArgoCD Application of Applications Pattern?

The ArgoCD Application of Applications pattern is a declarative design pattern where a single ArgoCD Application resource (known as the "Root Application") is configured to track, deploy, and manage a collection of other child ArgoCD Application resources. Instead of manually applying dozens of individual Application manifests to a Kubernetes cluster, operators apply one Root Application that recursively bootstraps the entire cluster state.

Featured Snippet / Quick Definition:
The Application of Applications pattern leverages the fact that an ArgoCD Application is simply a Kubernetes Custom Resource Definition (CRD). Because ArgoCD can manage any Kubernetes resource, it can manage its own Application CRDs. By pointing a "Root" Application to a Git repository directory containing child Application manifests, ArgoCD automatically generates, syncs, and maintains those child applications, which in turn deploy actual workloads (microservices, databases, ingress controllers) to the cluster.

Table of Contents

What You Will Learn

In this comprehensive guide, we will cover the following operational and architectural concepts:

  • How to design and structure a production-grade Root Application.
  • How to write dry, reusable child templates using both Helm and Kustomize.
  • How to enforce ordering and dependency management using ArgoCD Sync Waves and Sync Phases.
  • How to prevent cluster-wide outages using deletion finalizers and cascading deletion controls.
  • How to secure multi-tenant clusters by configuring strict AppProjects and namespace constraints.
  • Real-world troubleshooting playbooks for breaking out of out-of-sync loops and finalizer deadlocks.

Prerequisites

To get the most out of this masterclass lesson, you should meet the following requirements:

  • A solid understanding of Kubernetes core concepts (Namespaces, Deployments, Services, CRDs, RBAC).
  • Familiarity with GitOps principles as taught in our Introduction to GitOps course.
  • ArgoCD installed and running on a local (e.g., Kind, Minikube) or cloud-based Kubernetes cluster. See our ArgoCD Architecture Deep Dive for setup instructions.
  • Basic proficiency with Helm templating or Kustomize configuration management tools.

The Problem: Managing Multi-App Scale

When organizations first adopt GitOps with ArgoCD, they start by deploying individual applications. An engineer runs a command like:

kubectl apply -f my-single-app.yaml

This works perfectly fine for five or ten applications. However, as the platform scales to accommodate dozens of engineering teams, microservices, and cluster utilities, this approach breaks down. Organizations face severe operational bottlenecks:

  • Manual Bootstrapping: When provisioning a new Kubernetes cluster (e.g., for disaster recovery or staging), operators must manually run kubectl apply for every single application manifest. This is error-prone and defeats the purpose of continuous delivery.
  • Configuration Drift: If an operator deletes an application manifest from the local terminal, ArgoCD cannot self-heal or reconcile its absence unless the application resource itself is tracked in Git.
  • Lack of Centralized Governance: There is no single source of truth defining *what* applications should run on a specific cluster. Security, networking, and application teams operate in silos without a unified cluster-state declaration.

The Application of Applications pattern solves these issues by treating the cluster's entire operational state as a single, self-healing, hierarchical tree of resources.

Architecture and Conceptual Design

The core concept of the Application of Applications pattern is hierarchical reconciliation. ArgoCD's controller continuously reconciles the state of the cluster with the state declared in Git. By placing Application manifests inside Git, we instruct ArgoCD to reconcile those application definitions themselves.

Let's break down the components of this hierarchy:

  • The Root Application: This is the entry point. It is manually applied to the cluster once (or via a bootstrap script). It points to a Git repository folder containing child application definitions.
  • The Child Applications: These are individual ArgoCD Application manifests generated by the Root Application. They point to the actual raw manifests, Helm charts, or Kustomize directories of the workloads (e.g., Prometheus, NGINX Ingress, API Services).
  • The Target Workloads: These are the final Kubernetes resources (Deployments, Services, ConfigMaps, CRDs) deployed by the child applications.

This hierarchy creates a powerful tree structure within the ArgoCD UI, giving operators a single dashboard to monitor the health of the entire cluster infrastructure.

Architecture Diagrams

Hierarchical Resource Tree

+------------------------------------------------------------+
|                       Git Repository                       |
|                                                            |
|  /bootstrap/root-app.yaml                                  |
|  /apps/                                                    |
|    β”œβ”€β”€ cert-manager.yaml  (Child Application CRD)          |
|    β”œβ”€β”€ ingress-nginx.yaml (Child Application CRD)          |
|    └── payment-api.yaml   (Child Application CRD)          |
+------------------------------------------------------------+
                             β”‚
                             β–Ό
+------------------------------------------------------------+
|                     Kubernetes Cluster                     |
|                                                            |
|   +────────────────────────────────────────────────────+   |
|   β”‚               ArgoCD Controller                    β”‚   |
|   +────────────────────────────────────────────────────+   |
|                             β”‚                              |
|                             β–Ό                              |
|   +────────────────────────────────────────────────────+   |
|   β”‚            Root Application (argocd/root)          β”‚   |
|   +────────────────────────────────────────────────────+   |
|         β”‚                   β”‚                   β”‚          |
|         β–Ό                   β–Ό                   β–Ό          |
|   +───────────+       +───────────+       +───────────+    |
|   β”‚Child App: β”‚       β”‚Child App: β”‚       β”‚Child App: β”‚    |
|   β”‚cert-mgr   β”‚       β”‚ingress-ng β”‚       β”‚payment-apiβ”‚    |
|   +───────────+       +───────────+       +───────────+    |
|         β”‚                   β”‚                   β”‚          |
|         β–Ό                   β–Ό                   β–Ό          |
|   +───────────+       +───────────+       +───────────+    |
|   β”‚Actual Helmβ”‚       β”‚Actual K8s β”‚       β”‚Actual K8s β”‚    |
|   β”‚Resources  β”‚       β”‚Resources  β”‚       β”‚Resources  β”‚    |
|   +───────────+       +───────────+       +───────────+    |
+------------------------------------------------------------+
    

Internal Reconciliation Loop

The diagram below illustrates how the ArgoCD Application Controller processes updates within an App of Apps hierarchy:

[ Git Commit Pushed ] 
        β”‚
        β–Ό
[ Root App detects change in /apps/ directory ]
        β”‚
        β–Ό
[ ArgoCD Syncs Root App ] ──> Deploy/Update Child Application CRDs in K8s
        β”‚
        β–Ό
[ Child Apps detect change in their respective target paths ]
        β”‚
        β–Ό
[ ArgoCD Syncs Child Apps ] ──> Deploy/Update target workloads (Pods, Services)
    

Step-by-Step Implementation Guide

We will now build a production-ready Application of Applications pattern from scratch. We will explore three implementation strategies: Plain YAML, Helm-templated, and Kustomize-based.

Option A: Plain YAML Implementation

This is the simplest form of the pattern. It is highly readable and excellent for smaller clusters.

1. Directory Structure

git-repo/
β”œβ”€β”€ bootstrap/
β”‚   └── root-app.yaml
└── apps/
    β”œβ”€β”€ cert-manager.yaml
    β”œβ”€β”€ ingress-nginx.yaml
    └── web-app.yaml

2. The Root Application Manifest (bootstrap/root-app.yaml)

This manifest declares the Root Application. Notice that it deploys into the argocd namespace, which is where the ArgoCD controller is running and listening for Application CRDs.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-application
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: 'https://github.com/your-org/gitops-infra.git'
    targetRevision: HEAD
    path: apps
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

3. Child Application Manifest: Cert-Manager (apps/cert-manager.yaml)

This child application points to an external Helm repository to deploy Cert-Manager. It is managed by the Root Application.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cert-manager
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    chart: cert-manager
    repoURL: https://charts.jetstack.io
    targetRevision: v1.12.0
    helm:
      parameters:
        - name: installCRDs
          value: "true"
  destination:
    server: https://kubernetes.default.svc
    namespace: cert-manager
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

4. Child Application Manifest: Web Application (apps/web-app.yaml)

This child application points to raw manifests stored inside the same Git repository (a monorepo pattern).

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: billing-api
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: 'https://github.com/your-org/gitops-infra.git'
    targetRevision: HEAD
    path: k8s-manifests/billing-api
  destination:
    server: https://kubernetes.default.svc
    namespace: billing
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Option B: Helm-Templated App of Apps (Enterprise Standard)

Using plain YAML files leads to massive duplication. If you have 50 applications, you have 50 highly similar YAML files. By wrapping the child applications inside a local Helm chart, you can dynamically generate application definitions using a values.yaml file.

1. Directory Structure

git-repo/
β”œβ”€β”€ bootstrap/
β”‚   └── root-app-helm.yaml
└── cluster-bootstrap-chart/
    β”œβ”€β”€ Chart.yaml
    β”œβ”€β”€ values.yaml
    └── templates/
        └── applications.yaml

2. The Chart Definition (cluster-bootstrap-chart/Chart.yaml)

apiVersion: v2
name: cluster-bootstrap-chart
description: A Helm chart to generate ArgoCD Child Applications
type: application
version: 1.0.0
appVersion: "1.0.0"

3. The Dynamic Values File (cluster-bootstrap-chart/values.yaml)

This file acts as the single pane of glass specifying every application running on the cluster and its unique configurations.

applications:
  cert-manager:
    enabled: true
    namespace: cert-manager
    repoURL: https://charts.jetstack.io
    chart: cert-manager
    targetRevision: v1.12.0
    helmParameters:
      - name: installCRDs
        value: "true"
  
  ingress-nginx:
    enabled: true
    namespace: ingress-nginx
    repoURL: https://kubernetes.github.io/ingress-nginx
    chart: ingress-nginx
    targetRevision: 4.7.1
    helmParameters:
      - name: controller.service.type
        value: LoadBalancer

  payment-gateway:
    enabled: true
    namespace: processing
    repoURL: https://github.com/your-org/gitops-infra.git
    path: apps/payment-gateway
    targetRevision: main

4. The Template Generator (cluster-bootstrap-chart/templates/applications.yaml)

This template loops through the defined values and dynamically generates ArgoCD Application manifests.

{{- range $appName, $appConfig := .Values.applications }}
{{- if $appConfig.enabled }}
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: {{ $appName }}
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: {{ $appConfig.repoURL | quote }}
    targetRevision: {{ $appConfig.targetRevision | quote }}
    {{- if $appConfig.chart }}
    chart: {{ $appConfig.chart | quote }}
    {{- else }}
    path: {{ $appConfig.path | quote }}
    {{- end }}
    {{- if $appConfig.helmParameters }}
    helm:
      parameters:
        {{- toYaml $appConfig.helmParameters | nindent 8 }}
    {{- end }}
  destination:
    server: https://kubernetes.default.svc
    namespace: {{ $appConfig.namespace | quote }}
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
---
{{- end }}
{{- end }}

5. The Root Application pointed to our Helm Chart (bootstrap/root-app-helm.yaml)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-helm-bootstrap
  namespace: argocd
spec:
  project: default
  source:
    repoURL: 'https://github.com/your-org/gitops-infra.git'
    targetRevision: HEAD
    path: cluster-bootstrap-chart
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Advanced Sequencing: Sync Waves and Phases

In a real-world cluster, applications have strict dependencies. You cannot deploy your microservice APIs before the Service Mesh or Ingress Controllers are fully operational. If you attempt to sync everything simultaneously, pods will crash, health checks will fail, and cluster startup will be highly unstable.

ArgoCD provides Sync Waves to orchestrate the precise order in which applications and resources are synchronized.

Understanding Sync Waves

  • Every resource in ArgoCD can have a sync wave assigned via an annotation: argocd.argoproj.io/sync-wave.
  • Waves can be negative or positive integers (e.g., -5, 0, 5, 10).
  • ArgoCD reconciles resources in ascending order. It starts with the lowest wave value and waits for all resources in that wave to reach a Healthy state before moving to the next wave.

Production Sequencing Strategy

To implement proper sequencing within your App of Apps pattern, apply sync wave annotations directly to your child Application manifests:

Wave -10: Core CRDs (e.g., Cert-Manager CRDs, Prometheus Operator CRDs)
    β”‚
    β–Ό
Wave -5 : Cluster Ingress & Routing (e.g., NGINX Ingress, Istio Control Plane)
    β”‚
    β–Ό
Wave 0  : Shared Services & Databases (e.g., Redis, PostgreSQL, Vault)
    β”‚
    β–Ό
Wave 5  : Core Business Applications (e.g., Payment API, Frontend, Auth Service)
    β”‚
    β–Ό
Wave 10 : Post-Deployment Verification & Smoke Tests
    

Example: Annotated Child Application with Sync Wave

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ingress-nginx
  namespace: argocd
  annotations:
    # This application will sync before normal workloads (wave 0)
    argocd.argoproj.io/sync-wave: "-5"
spec:
  project: default
  source:
    chart: ingress-nginx
    repoURL: https://kubernetes.github.io/ingress-nginx
    targetRevision: 4.7.1
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

For more details on sync sequencing, read our deep dive on ArgoCD Sync Phases and Waves.

Cascading Deletions and Finalizers

One of the most dangerous operational risks when using the App of Apps pattern is the unintended deletion of resources. If the Root Application is deleted, does it delete all the child applications? And do those child applications delete all the actual pods, services, and databases in the cluster?

This behavior is controlled by Finalizers.

The Deletion Finalizer

ArgoCD uses the following finalizer to control cascading deletion:

finalizers:
  - resources-finalizer.argocd.argoproj.io

Cascading vs. Non-Cascading Deletion

Finalizer Present? Deletion Type Behavior upon Application Deletion
Yes Cascading Deletion ArgoCD will delete the Application CRD *and* actively clean up all Kubernetes resources managed by it (e.g., Deployments, Services, PVCs).
No Non-Cascading Deletion ArgoCD will delete the Application CRD from its dashboard, but all actual Kubernetes resources running in the cluster will be left orphaned (untouched).

Production Best Practice

For critical core infrastructure (such as databases or stateful sets), you should omit the finalizer from the child application templates. This acts as a safety buffer: even if someone accidentally deletes the Root Application or the child Application CRD, the actual database pods and persistent volumes will remain intact in Kubernetes.

For stateless, ephemeral, or development-stage workloads, always include the finalizer to ensure clean resource teardown and avoid resource leakage.

Enterprise Multi-Tenancy and Security Guardrails

In an enterprise with hundreds of developers, allowing any child application to deploy resources anywhere is a significant security risk. A malicious or misconfigured application could deploy resources into the kube-system namespace or overwrite cluster RBAC settings.

To secure the App of Apps pattern, you must combine it with ArgoCD AppProjects.

1. The AppProject Boundary

An AppProject resource defines a logical boundary for applications. It restricts:

  • Which Git repositories applications can pull manifests from.
  • Which target clusters and namespaces applications can deploy to.
  • Which Kubernetes API groups (kinds of resources) can be created (e.g., blocking ClusterRoles).

2. Production Multi-Tenant Architecture

To implement secure multi-tenancy, configure your Root Application to run in the default project, but force child applications to run in restricted projects.

+─────────────────────────────────────────────────────────+
|               Root App (Project: default)               |
+─────────────────────────────────────────────────────────+
                             β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β–Ό                                 β–Ό
+───────────────────────+         +───────────────────────+
| Child App: Payments   |         | Child App: Marketing  |
| Project: payment-proj |         | Project: market-proj  |
+───────────────────────+         +───────────────────────+
            β”‚                                 β”‚
            β–Ό                                 β–Ό
Deploy only to 'processing'       Deploy only to 'marketing'
No Cluster-wide RBAC allowed      No Cluster-wide RBAC allowed
    

3. Implementation Example: The Secure AppProject

The following AppProject ensures that any application assigned to it can only deploy standard resources to the processing namespace, and is blocked from creating cluster-wide resources like ClusterRoles:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: payment-project
  namespace: argocd
spec:
  description: "Strict isolation boundary for the Payment Team"
  # Allow child applications to only pull from the official organization Git repo
  sourceRepos:
    - 'https://github.com/your-org/payment-services.git'
  # Restrict deployments to the specific processing namespace on the local cluster
  destinations:
    - namespace: processing
      server: https://kubernetes.default.svc
  # Block cluster-scoped resources from being created by this project's applications
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  namespaceResourceWhitelist:
    - group: '*'
      kind: '*'

Ensure that your child application manifest references this project:

spec:
  project: payment-project # Enforces the safety guardrails defined above

Repository Layouts: Monorepo vs. Polyrepo

Choosing the right repository structure is critical for maintainability, security, and velocity. Organizations typically choose between a Monorepo approach (one repository for everything) and a Polyrepo approach (separate repositories for platform config and application code).

1. The Monorepo Layout

In a monorepo, everything is stored together. This is ideal for small to medium-sized teams where operations and development are tightly integrated.

git-repo/
β”œβ”€β”€ bootstrap/               # Root Application manifests
β”œβ”€β”€ cluster-config/          # Shared platform services (Ingress, Cert-Manager)
└── apps/                    # Business applications
    β”œβ”€β”€ auth-service/
    β”‚   β”œβ”€β”€ deployment.yaml
    β”‚   └── service.yaml
    └── payment-service/
        β”œβ”€β”€ deployment.yaml
        └── service.yaml

Pros: Easy to trace dependencies; atomic changes across applications and infrastructure are simple; single source of truth.
Cons: Access control (RBAC) is difficult to enforce at the Git level; large repositories can slow down CI/CD pipelines and Git operations.

2. The Polyrepo Layout (Enterprise Grade)

In an enterprise, platform engineering teams manage the cluster configuration, while individual application teams manage their own services in separate code repositories.

Platform Repository (gitops-infra):
β”œβ”€β”€ bootstrap/
β”‚   └── root-app.yaml        # Deployed by Platform Team
└── cluster-bootstrap-chart/
    └── values.yaml          # Points to external application repos

Application Repository (payment-service-repo):
β”œβ”€β”€ src/                     # Application source code
└── deploy/                  # Kubernetes manifests / Helm charts

Pros: Perfect separation of concerns; developers have full control over their own application repositories; platform team controls cluster-wide configurations.
Cons: Harder to track changes across multiple repositories; require robust automated testing to ensure changes in application repos do not break cluster configurations.

App of Apps vs. ApplicationSets: The Definitive Comparison

ArgoCD introduced ApplicationSets as another way to generate applications dynamically. How do they compare, and when should you use one over the other?

Feature Application of Applications Pattern ApplicationSets
Core Technology Standard hierarchical Application CRDs. ApplicationSet Controller using Generators.
Dynamic Discovery Manual or Helm-templated list of apps. Automatic discovery of clusters, Git directories, or pull requests.
Multi-Cluster Deployments Requires manual duplication or complex Helm templating. Native multi-cluster target generation using Cluster Generators.
Learning Curve Low. Uses standard YAML/Helm concepts. Medium. Requires understanding generators and templating syntax.
Best For Bootstrapping single clusters; strict ordering with Sync Waves. Multi-tenant SaaS platforms; deploying applications across hundreds of clusters.

Can they be combined?

Yes! In many enterprise architectures, the Root Application deploys an ApplicationSet as one of its children. The ApplicationSet then dynamically generates child applications across multiple target clusters. This hybrid pattern provides the ultimate flexibility and scalability.

To learn more about dynamic generation, check out our guide on the ArgoCD ApplicationSet Generator.

Troubleshooting and Common Failure Modes

Operating an App of Apps pattern at scale will inevitably lead to unique failure scenarios. Below are the most common issues and step-by-step playbooks to resolve them.

1. Infinite Sync Loops

Symptom: The Root Application or a child application is constantly syncing and never reaches a Healthy or Synced state.

Cause: This usually happens when a controller (like a mutating webhook or an operator) modifies a resource in the cluster after ArgoCD has applied it. ArgoCD detects this change as drift and attempts to overwrite it, triggering a continuous fight between ArgoCD and the controller.

Resolution: Use the ignoreDifferences configuration in the application spec to instruct ArgoCD to ignore the fields managed by external controllers.

spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas # Ignore replicas if managed by an HPA (Horizontal Pod Autoscaler)

2. Deletion Deadlocks (The Stuck Finalizer)

Symptom: You attempt to delete an application, but it remains stuck in the Terminating state indefinitely.

Cause: The application has the resources-finalizer.argocd.argoproj.io finalizer, but some resources inside the cluster cannot be deleted (e.g., a namespace stuck in terminating due to an active APIService, or a PersistentVolumeClaim waiting for a pod to detach).

Resolution: If you are in an emergency and need to force deletion, you can patch the application to remove the finalizer. Warning: This will orphan any remaining resources in the cluster.

kubectl patch app billing-api -n argocd -p '{"metadata":{"finalizers":null}}' --type=merge

3. Missing Namespace Creation

Symptom: Child applications fail to sync with an error like namespaces "xyz" not found.

Cause: The child application is trying to deploy resources into a namespace that does not exist yet, and the CreateNamespace=true sync option was omitted.

Resolution: Ensure your child application spec explicitly includes the namespace creation sync option:

spec:
  syncPolicy:
    syncOptions:
      - CreateNamespace=true

Monitoring and Observability

When managing entire clusters via a single Root Application, monitoring the health of your App of Apps tree is paramount. If the Root Application goes out of sync, the entire cluster configuration could be drifting unnoticed.

Prometheus Metrics to Track

ArgoCD exports rich Prometheus metrics. You should set up alerts for the following metrics:

  • argocd_app_sync_status{name="root-application", sync_status="OutOfSync"}: Alerts when the Root Application drifts from Git. This indicates that someone has pushed a change to Git that is not applying, or a manual change was made to a child app.
  • argocd_app_health_status{health_status="Degraded"}: Alerts when any child application enters a Degraded state, indicating failing pods, crash loops, or broken ingress configurations.

Custom Health Checks

You can write custom Lua health checks inside the ArgoCD ConfigMap to define what "Healthy" means for complex custom resources, ensuring that child applications only report healthy when their underlying components are truly operational.

Technical Interview Q&A

Q1: Explain the difference between cascading and non-cascading deletion in the context of the App of Apps pattern.

Answer: Cascading deletion occurs when the child application is configured with the resources-finalizer.argocd.argoproj.io finalizer. When the application CRD is deleted, ArgoCD actively deletes all resources (Deployments, Services, etc.) generated by that application. Non-cascading deletion occurs when the finalizer is omitted; deleting the application CRD removes it from ArgoCD's management, but leaves the actual resources running in the Kubernetes cluster untouched.

Q2: How do you prevent a child application from deploying resources into unauthorized namespaces in a multi-tenant cluster?

Answer: This is enforced using ArgoCD AppProjects. We create a dedicated AppProject for each tenant and specify the allowed target namespaces in the destinations field. We then assign the child application to this project. If the application attempts to deploy resources outside of these namespaces, the ArgoCD controller will refuse to sync the resource.

Q3: What happens if there is a circular dependency in Sync Waves? For example, App A (Wave 1) depends on App B (Wave 2), but App B also depends on App A.

Answer: ArgoCD executes sync waves in ascending order. If App A is in Wave 1, ArgoCD will attempt to sync it first. Since it depends on App B (which is in Wave 2 and hasn't synced yet), App A will fail its health check or remain in a progressing state. ArgoCD will block and never proceed to Wave 2, resulting in a deadlock. Sync waves must be strictly linear and acyclic.

Q4: How do you handle secrets inside the Git repository when using the App of Apps pattern?

Answer: You should never store raw secrets in Git. Instead, integrate secret management tools with your GitOps workflow. Common strategies include using Sealed Secrets (where secrets are encrypted and can only be decrypted by a controller inside the cluster), or using external secret operators (like External Secrets Operator or HashiCorp Vault) where the Git repository only contains references to secrets stored in a secure external vault.

Frequently Asked Questions (FAQs)

Can a child application also be a parent application to other applications?

Yes. ArgoCD supports deep nesting of applications. A Root Application can deploy a child application, which in turn deploys its own child applications. However, for operational simplicity and maintainability, it is highly recommended to limit nesting to a maximum of two levels (Root -> Child).

How does ArgoCD handle pruning of child applications?

If prune: true is enabled on the Root Application, deleting a child application manifest from Git will cause ArgoCD to automatically delete the corresponding Application CRD from the cluster. If cascading deletion is enabled on that child application, all of its underlying workloads will be pruned as well.

Can I use different Git repositories for my child applications?

Absolutely. While the Root Application must point to a single repository to read the child manifests, those child manifests themselves can point to completely different Git repositories, Helm registries, or Kustomize paths across your organization.

How do I manual sync a single child application if the Root App is set to auto-sync?

You can still interact with individual child applications via the ArgoCD UI, CLI, or API. You can trigger manual syncs, rollback, or pause auto-sync on a specific child application without affecting the Root Application or other sibling applications.

What is the performance impact of having hundreds of applications in a single App of Apps tree?

As the number of applications grows, the ArgoCD application controller has to perform more reconciliation loops, which can increase CPU and memory utilization. To mitigate this, you can tune the controller's status and resource parallel processors, increase the sync reconciliation timeout, and use Webhooks instead of polling to trigger instant syncs upon Git commits.

Should the Root Application deploy the ArgoCD controller itself?

No, this is generally considered an anti-pattern (often called "ArgoCD managing ArgoCD"). The ArgoCD controller should be installed and managed as part of the initial cluster bootstrapping process (e.g., via Terraform or a bootstrap shell script). Once ArgoCD is running, it can then take over the management of all other cluster resources, including its own configuration maps and projects, but bootstrap installation should remain external.

Summary & Next Steps

The ArgoCD Application of Applications pattern is a cornerstone of modern platform engineering. By treating your entire cluster state as a declarative tree, you achieve true disaster recovery readiness, eliminate configuration drift, and establish clean multi-tenant security boundaries.

About the Author

Naresh Kumar

Naresh Kumar

Senior Java Backend Engineer experienced in Banking, Payments, ISO 20022, Spring Boot, Microservices, Kafka, Docker, Kubernetes, AWS and Cloud Native Systems.

Built enterprise payment solutions, transaction processing systems, API platforms and scalable microservices used in production.

LinkedIn Profile