Git, CI/CD & GitOpsLXXVII · Argo CDCRD
The Application CRD — source, destination, sync, and the resource model
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
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 includemetadata.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.status—SyncedorOutOfSync. The controller’s verdict on whether desired matches live.status.health.status—Healthy,Degraded,Progressing,Suspended,Missing. Derived from live resources; a healthy Deployment with a failing Pod isDegraded.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:
targetRevisionis 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.syncPolicy.automated.pruneis 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.ignoreDifferencesis 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
sourcefield is what the hydrator feeds.
Quiz
Knowledge check · 4 questions
Q1. Which field in an Application spec controls whether the application controller applies the diff itself or waits for a human?
Q2. Setting spec.targetRevision to HEAD means the controller will deploy the latest commit on the default branch at the time of each reconcile.
Q3. Name the three core fields of an Application spec and identify which one names the source the repo server should render.
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.