Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXVII · Argo CDCRD

The Application CRD — source, destination, sync, and the resource model

Advanced⏱ ~25 mingitargocd

What you'll learn

  • Read an Application CRD and identify the source, destination, and sync sections
  • Explain why the Application is a Kubernetes resource rather than an Argo CD-only object
  • Configure source, destination, and sync policy fields for a production Application
  • Distinguish the Application from the AppProject; recognise which fields each owns

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The Application is the unit Argo CD reconciles - not the deployment, not the Helm release, not the Kustomize overlay. An Application is a Kubernetes custom resource whose spec names a source (where to read desired state from), a destination (which cluster and namespace to apply it to), and a syncPolicy that decides whether the controller applies the diff itself or waits for an operator. Understanding the CRD is the prerequisite for every subsequent topic in this part.

Why the Application is a CRD

Argo CD does not store deployment state in a proprietary database. It stores Applications as Kubernetes custom resources in the Argo CD namespace. The API server, the application controller, and the UI all read and write the same Kubernetes objects the user reads with kubectl get applications. Three operational consequences follow:

  • Applications can be created and managed declaratively, like any other Kubernetes resource. A team that already manages Deployments and Ingresses via GitOps can manage Applications the same way.
  • The Application’s lifecycle is the cluster’s lifecycle. Deleting the CRD unregisters it; deployed resources remain unless pruned.
  • The Application’s RBAC is the Kubernetes RBAC on the Argo CD namespace plus the Argo CD project policy. Both layers matter.
flowchart LR
    CRD["Application CRD"] --> Spec["spec section"]
    Spec --> Src["source"]
    Spec --> Dst["destination"]
    Spec --> Sync["syncPolicy"]
    Spec --> Ign["ignoreDifferences"]
    Src --> RS["Repo server renders"]
    Dst --> AC["Controller applies"]
    Sync --> AC
    Ign --> AC["diff logic"]

The spec section, field by field

An Application’s spec has three core fields and a handful of optional ones. The CLI command to create one captures the essentials:

argocd app create payment-api \
  --repo https://github.com/example/payment-api \
  --path helm \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace payments

The same object as a Kubernetes manifest:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payment-api
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/example/payment-api
    targetRevision: HEAD
    path: helm
    helm:
      valueFiles:
        - values-prod.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: payments
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

The fields and what they own:

  • spec.project — the AppProject the Application belongs to. The project is the multi-tenant boundary: it defines which sources the Application can read from, which destinations it can apply to, and which cluster credentials are reachable. LXXVII-05 covers projects in detail.
  • spec.source.repoURL — the Git (or Helm, or OCI) repository the controller’s repo server reads from. The controller does not fetch this directly; it tells the repo server to fetch it, and the repo server renders the manifests.
  • spec.source.targetRevision — the revision pointer. For Git this is a branch, a tag, or a commit SHA. For Helm it can be a chart version; for OCI it is a tag or digest. Pinning to a commit SHA is the production rule.
  • spec.source.path — the path inside the repo that contains the manifests. The repo server reads this path and renders it according to the source type (Helm, Kustomize, plain directory, plugin).
  • spec.destination.server — the target cluster API server URL. The Application controller uses the Argo CD-stored credentials for this server.
  • spec.destination.namespace — the namespace the controller applies the manifests into. Resources can be applied into other namespaces if the manifests themselves include metadata.namespace; the destination namespace is the default.
  • spec.syncPolicy.automated — when present, the controller applies the diff itself. When absent, the controller surfaces the diff and waits. LXXVII-04 covers this in detail.
flowchart TB
    subgraph Spec["Application spec"]
        P["project: default"]
        S["source: repoURL, targetRevision, path, helm"]
        D["destination: server, namespace"]
        SP["syncPolicy: automated | manual"]
    end
    P --> Boundary["Multi-tenant boundary (LXXVII-05)"]
    S --> Render["Renders via repo server (LXXVII-03)"]
    D --> Apply["Applies via controller (LXXVII-04)"]
    SP --> Apply

Status, history, and operation

The status section is populated by the controller and is read-only from the user’s perspective. The fields that matter:

  • status.sync.statusSynced or OutOfSync. The controller’s verdict on whether desired matches live.
  • status.health.statusHealthy, Degraded, Progressing, Suspended, Missing. Derived from live resources; a healthy Deployment with a failing Pod is Degraded.
  • status.history — the deployment history. Every sync is recorded with revision, timestamp, and initiator. The audit trail for the Application.

Two operations matter for an engineer:

argocd app sync payment-api
argocd app get payment-api --output yaml

sync triggers an out-of-cycle reconcile and (under automated policy) an apply. get --output yaml shows the full CRD including status.history, the per-Application audit trail.

Under the hood

The Application CRD is the API surface that makes Argo CD declaratively manageable. The Helm chart for Argo CD ships with an Application CRD; every CLI operation is a CRUD operation on it. A team can manage Applications the same way they manage Deployments: a Git repository of manifests, a pull request, a CI pipeline that validates before merge.

Production discipline

The rules for production Applications:

  1. targetRevision is pinned to a SHA or a signed tag. Branch heads are a moving target. The deployed SHA is the audit answer; it must be the SHA that was reviewed.
  2. syncPolicy.automated.prune is set deliberately. Pruning deletes resources in the target namespace that are no longer in the source. The default is false; turning it on is a decision the team owns.
  3. ignoreDifferences is documented in the Application. The field suppresses diffs for paths the controller should not reconcile against (HPA-managed replicas, in-cluster certificates). Each entry has a comment in the PR that adds it.

Cross-course references

  • Kubernetes for Production Sysadmins - Part XII (Custom Resources) covers the operator pattern the Application CRD participates in; Part XIV (CRD Schemas) covers how the OpenAPI schema makes the spec fields valid.
  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXVI-04 (Source Hydrator) is the source-model predecessor; the Application’s source field is what the hydrator feeds.

Quiz

Knowledge check · 4 questions

  1. Q1. Which field in an Application spec controls whether the application controller applies the diff itself or waits for a human?

  2. Q2. Setting spec.targetRevision to HEAD means the controller will deploy the latest commit on the default branch at the time of each reconcile.

  3. Q3. Name the three core fields of an Application spec and identify which one names the source the repo server should render.

  4. Q4. Diagnose why the audit trail cannot answer which commit was deployed and recommend the fix.

    An Application is registered with targetRevision: HEAD against a chart that is updated multiple times a day. Six months later, an incident requires identifying the exact commit that was running at 14:23 UTC. The status.history shows revision 'HEAD' for every entry, with no commit SHA recorded.

Passing score: 75%. Answers are checked in this browser.