# Argo CD Custom Resources (CRDs) Explained: GitOps Configuration for Kubernetes

> Understand Argo CD Custom Resources (CRDs) and how they extend Kubernetes for declarative GitOps. Learn about Application, AppProject, and ApplicationSet resources for managing deployments.

- Repository: [Argo Project/argo-cd](https://github.com/argoproj/argo-cd)
- Tags: deep-dive
- Published: 2026-07-14

---

**Argo CD uses Custom Resource Definitions (CRDs) to extend the Kubernetes API with declarative GitOps objects, enabling cluster-state management through Application, AppProject, and ApplicationSet resources that reconcile Git repositories with live deployments.**

Argo CD implements its core functionality through Kubernetes Custom Resource Definitions (CRDs), allowing you to define GitOps workflows using native declarative syntax. These CRDs transform Argo CD into a Kubernetes controller that watches custom resources to synchronize cluster state with version-controlled manifests. Understanding the structure, lifecycle, and implementation details of these CRDs is critical for troubleshooting sync failures, managing multi-tenant environments, and extending the platform with custom operators.

## Core Argo CD CRD Types

Argo CD ships with several CRDs that define its primary API objects. These definitions reside in `manifests/crds/` and establish the schema for GitOps resources.

### Application CRD

The `applications.argoproj.io` CRD represents a single deployable unit in Argo CD. Defined in [`manifests/crds/application-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/application-crd.yaml), this resource specifies:

- **Source**: Git repository URL, path, and target revision
- **Destination**: Target cluster and namespace
- **Sync Policy**: Automated synchronization, pruning, and self-healing settings
- **Health Checks**: Resource-specific health assessment configurations

When you create an Application resource, the Argo CD controller monitors its specification and reconciles the live cluster state with the desired state defined in Git.

### AppProject CRD

The `appprojects.argoproj.io` CRD, defined in [`manifests/crds/appproject-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/appproject-crd.yaml), provides multi-tenancy and governance controls:

- **RBAC**: Role-based access control for teams and applications
- **Source Repositories**: Allowed Git repository patterns
- **Destination Clusters**: Permitted target clusters and namespaces
- **Sync Windows**: Time-based deployment restrictions

Projects group related Applications and enforce organizational policies before sync operations execute.

### ApplicationSet CRD

The `applicationset.argoproj.io` CRD, located in [`manifests/crds/applicationset-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/applicationset-crd.yaml), enables template-based Application generation. This resource uses **generators** (Git directory, List, Cluster, or SCM provider) to dynamically create multiple Application objects from a single template, streamlining management of microservices or multi-cluster deployments.

## How Argo CD Watches and Reconciles CRDs

The Argo CD architecture uses the Kubernetes controller pattern to monitor CRD state changes and execute synchronization logic.

### Dynamic Client and Informer Setup

The application controller establishes watches on custom resources through a dynamic client. In [`gitops-engine/pkg/sync/sync_context.go`](https://github.com/argoproj/argo-cd/blob/main/gitops-engine/pkg/sync/sync_context.go) (around line 1290), the controller fetches CRD definitions to understand resource schemas:

```go
crd, err := sc.extensionsclientset.ApiextensionsV1().
        CustomResourceDefinitions().
        Get(ctx, name, metav1.GetOptions{})

```

This dynamic client enables Argo CD to handle arbitrary CRDs without requiring compiled-in type definitions, supporting extensible GitOps workflows that include custom operators.

### Sync Task Ordering for CRD Dependencies

When an Application includes CRD manifests, Argo CD must apply these definitions before instantiating resources of that custom type. The ordering logic resides in [`gitops-engine/pkg/sync/sync_tasks.go`](https://github.com/argoproj/argo-cd/blob/main/gitops-engine/pkg/sync/sync_tasks.go), where the controller constructs a dependency graph ensuring CRDs deploy before their dependent custom resources.

This sequencing prevents sync failures where custom resource instances would fail validation because their defining CRD hasn't reached the `Established` state.

### Health Assessment and Status Conditions

Argo CD monitors CRD health through the `status.conditions` field defined in `resource_customizations/apiextensions.k8s.io/CustomResourceDefinition/`. The controller watches for:

- **Established**: The CRD is recognized by the API server and ready for resource creation
- **NamesAccepted**: The CRD's plural name is accepted and not in conflict

The UI component in [`ui/src/app/applications/components/resources.ts`](https://github.com/argoproj/argo-cd/blob/main/ui/src/app/applications/components/resources.ts) (around line 44) maps these conditions to visual indicators, displaying "Progressing" while CRDs establish and "Healthy" once accepted.

## Practical CRD Usage Patterns

### Deploying Applications Containing CRDs

When managing operators or applications that define their own CRDs, Argo CD handles the installation order automatically. Consider this Application manifest that deploys an operator with custom resources:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx-operator
spec:
  project: default
  source:
    repoURL: https://github.com/example/nginx-operator
    path: helm
    helm:
      valueFiles:
      - values.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: nginx-operator
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

```

The Helm chart contains a [`templates/crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/templates/crd.yaml) file defining the operator's CRD. According to the sync logic in [`controller/sync.go`](https://github.com/argoproj/argo-cd/blob/main/controller/sync.go), Argo CD detects this CRD resource, applies it first, waits for the `Established` condition, then proceeds to create the Deployment and custom resource instances.

### Generating CRD-backed Applications with ApplicationSet

For multi-tenant scenarios where each tenant requires a distinct CRD-based application, use an ApplicationSet:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: multi-tenant
spec:
  generators:
    - git:
        repoURL: https://github.com/example/multi-tenant-repo
        directories:
          - path: tenant-*
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/example/multi-tenant-repo
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'

```

Each generated Application may contain tenant-specific CRDs. Argo CD ensures proper ordering across all generated resources, waiting for each CRD to become established before creating dependent custom resources.

## Key Source Files and Implementation Details

Understanding the codebase helps troubleshoot CRD-related issues:

- **[`manifests/crds/application-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/application-crd.yaml)**: Core Application resource schema defining GitOps specifications
- **[`manifests/crds/appproject-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/appproject-crd.yaml)**: Project boundaries and RBAC constraints
- **[`manifests/crds/applicationset-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/applicationset-crd.yaml)**: Template-based Application generation schema
- **[`gitops-engine/pkg/sync/sync_context.go`](https://github.com/argoproj/argo-cd/blob/main/gitops-engine/pkg/sync/sync_context.go)**: Dynamic CRD client initialization and API extensions interaction
- **[`gitops-engine/pkg/sync/sync_tasks.go`](https://github.com/argoproj/argo-cd/blob/main/gitops-engine/pkg/sync/sync_tasks.go)**: Task ordering logic ensuring CRDs precede custom resource instances
- **`resource_customizations/apiextensions.k8s.io/CustomResourceDefinition/`**: Health check Lua scripts interpreting CRD status conditions
- **[`hack/gen-crd-spec/main.go`](https://github.com/argoproj/argo-cd/blob/main/hack/gen-crd-spec/main.go)**: Code generation utility extracting CRD specifications from Go structs

## Summary

- **Argo CD CRDs** (`applications`, `appprojects`, `applicationsets`) extend Kubernetes to support declarative GitOps workflows through custom API objects.
- **Dynamic client architecture** in [`sync_context.go`](https://github.com/argoproj/argo-cd/blob/main/sync_context.go) allows Argo CD to watch and validate arbitrary CRDs without recompilation.
- **Sync ordering** logic ensures CRDs reach `Established` status before dependent resources are created, preventing validation errors.
- **Health monitoring** tracks CRD conditions through resource customizations, with UI visualization in [`resources.ts`](https://github.com/argoproj/argo-cd/blob/main/resources.ts).
- **Multi-tenancy patterns** leverage AppProjects for RBAC and ApplicationSets for generating CRD-backed Applications across environments.

## Frequently Asked Questions

### How does Argo CD handle CRDs that are part of an Application's source repository?

Argo CD detects CRD resources within the manifest collection and applies them before other resources. In [`gitops-engine/pkg/sync/sync_tasks.go`](https://github.com/argoproj/argo-cd/blob/main/gitops-engine/pkg/sync/sync_tasks.go), the controller constructs a task list that orders CRD creation first, then waits for the `Established` condition in `status.conditions` before proceeding with custom resource instances that depend on that definition.

### Can Argo CD manage its own CRDs as part of an Application?

Yes, though this requires careful handling. When an Application includes CRD manifests that define resource types used by other manifests in the same Application, Argo CD's sync wave logic ensures the CRD applies first. However, updating existing CRD schemas requires using the`replace` sync option or manual intervention, as Kubernetes restricts certain CRD modifications to prevent data loss.

### Where does Argo CD store the health check logic for CRDs?

Health assessment for CRDs resides in `resource_customizations/apiextensions.k8s.io/CustomResourceDefinition/`, which contains Lua scripts that evaluate the CRD's `status.conditions` array. These scripts check for the `Established` and `NamesAccepted` conditions to determine if the CRD is ready for use, returning "Healthy", "Progressing", or "Degraded" states that appear in the UI component defined in [`ui/src/app/applications/components/resources.ts`](https://github.com/argoproj/argo-cd/blob/main/ui/src/app/applications/components/resources.ts).

### What happens when I delete an Application that owns a CRD?

Deletion behavior depends on `metadata.ownerReferences` and propagation policies. If the Application created the CRD and owns it, Argo CD respects the Kubernetes garbage collection propagation policy. By default, deleting the Application does **not** cascade to CRDs unless explicitly configured with `ownerReferences` and cascade flags, preventing accidental deletion of shared resource definitions that might be used by other Applications in the cluster.