Mastering the building blocks of ArgoCD deployments
Use arrow keys or click sides to navigate
The Challenge
The Multi-Team Problem
Your company has three teams: frontend, backend, and platform. Each team has their own Git repo and deploys to different namespaces. The frontend team should never accidentally deploy to the backend namespace. How do you enforce these boundaries while giving each team autonomy? This is where ArgoCD Applications and AppProjects shine.
Application Anatomy
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: frontend-app # Unique name in ArgoCD
namespace: argocd # Must be in argocd namespace
labels:
team: frontend
env: production
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: frontend-team # AppProject reference
source: ... # Where to find manifests
destination: ... # Where to deploy
syncPolicy: ... # How and when to sync
The metadata Section
name: Unique application identifier -- use descriptive names like frontend-prod
namespace: Always argocd unless using apps-in-any-namespace
labels: Organize and filter apps -- team, env, tier
The finalizer trick: Adding resources-finalizer.argocd.argoproj.io means deleting the Application also deletes all its deployed resources. Without it, resources are orphaned in the cluster.
The source Section
spec:
source:
repoURL: https://github.com/your-org/manifests.git
targetRevision: main # Branch, tag, or commit SHA
path: apps/frontend # Path within the repo
targetRevision
Can be a branch (main), tag (v1.2.3), full commit SHA, or HEAD.
path
Directory within the repo. ArgoCD auto-detects the tool (Helm, Kustomize, plain YAML).
Source Type: Directory (Plain YAML)
The simplest source type -- just plain Kubernetes manifests in a directory.
spec:
source:
repoURL: https://github.com/your-org/manifests.git
targetRevision: main
path: apps/frontend
directory:
recurse: true # Include subdirectories
exclude: '{*.test.yaml}' # Glob exclude patterns
include: '{*.yaml,*.yml}' # Glob include patterns
When to use: Small projects, learning ArgoCD, or when you prefer fully rendered YAML without templating overhead.
ArgoCD runs helm template internally -- it does not use helm install. This means Helm hooks are handled differently (via ArgoCD resource hooks).
Helm: Values Files from Git
# Option B: Helm chart in a Git repo with value files
spec:
source:
repoURL: https://github.com/your-org/charts.git
targetRevision: main
path: charts/my-app
helm:
releaseName: my-app
valueFiles:
- values.yaml
- values-production.yaml # Overrides values.yaml
parameters:
- name: image.tag
value: "v2.1.0" # Highest priority override
Priority order:parameters override valueFiles, which override the chart's default values.yaml. This is the same as Helm's native behavior.
Quiz Time
Source Types Check
1. When ArgoCD deploys a Helm chart, which Helm command does it use internally?
Correct! ArgoCD uses helm template to render manifests, then applies them with its own sync engine. It does not use Tiller or helm install.
2. What does the resources-finalizer.argocd.argoproj.io finalizer do?
Correct! The finalizer triggers a cascading delete -- when you delete the Application CRD, ArgoCD also deletes all the Kubernetes resources it manages.
3. In ArgoCD Helm source, which has the highest override priority?
Correct! Parameters have the highest priority, then valueFiles (in order), then the chart defaults. This matches Helm's native --set flag behavior.
Source Type: Kustomize
spec:
source:
repoURL: https://github.com/your-org/manifests.git
targetRevision: main
path: overlays/production # Directory with kustomization.yaml
kustomize:
namePrefix: prod-
nameSuffix: -v2
images:
- name: my-app
newTag: v1.2.3
commonLabels:
env: production
commonAnnotations:
managed-by: argocd
ArgoCD auto-detects Kustomize when it finds a kustomization.yaml file. It runs kustomize build internally.
Kustomize: Base + Overlays Pattern
base/
→↓→
overlays/dev
overlays/staging
overlays/prod
Best practice: Create one ArgoCD Application per overlay. Point each to its overlay directory. The base contains shared manifests; overlays contain environment-specific patches.
The destination Section
spec:
destination:
# Option A: Deploy to the same cluster ArgoCD runs in
server: https://kubernetes.default.svc
namespace: frontend-prod
# Option B: Deploy to a registered external cluster
# server: https://aks-cluster-2.eastus.azmk8s.io
# namespace: frontend-prod
# Option C: Use cluster name instead of URL
# name: production-cluster
# namespace: frontend-prod
server: Kubernetes API server URL (https://kubernetes.default.svc for the local cluster)
name: Alternative to server -- use the cluster name registered in ArgoCD
namespace: Target namespace for resources (unless overridden in the manifest)
Registering External Clusters
# Add a cluster using the CLI (uses current kubeconfig context)
argocd cluster add aks-production-context
# List registered clusters
argocd cluster list
# Under the hood, ArgoCD creates a ServiceAccount in the
# target cluster with appropriate RBAC permissions and stores
# the credentials as a Kubernetes Secret in the argocd namespace.
AKS tip: For Azure Kubernetes Service, ensure the ArgoCD ServiceAccount has appropriate Azure RBAC or Kubernetes RBAC on the target cluster. Consider using Workload Identity for keyless auth.
The syncPolicy Section
spec:
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual cluster changes
allowEmpty: false # Don't sync if no resources found
syncOptions:
- CreateNamespace=true # Create namespace if missing
- PrunePropagationPolicy=foreground
- PruneLast=true # Prune after all syncs complete
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
We will dive deep into sync strategies in Module 03. For now, know that automated means ArgoCD syncs without human intervention.
Deep Dive
What is an AppProject?
An AppProject is a logical grouping and security boundary for ArgoCD Applications.
Think of AppProjects as tenants in a multi-team ArgoCD installation. Each project defines what sources an app can use, where it can deploy, and what resources it can manage.
Restrict which Git repositories applications can use
Restrict which clusters and namespaces applications can deploy to
Restrict which Kubernetes resource kinds can be deployed
Define RBAC roles for team-specific access
Quiz Time
Destinations & Sync
1. What is the Kubernetes API server URL for the cluster ArgoCD is running in?
Correct! https://kubernetes.default.svc is the in-cluster Kubernetes API address, automatically available to any pod running in the cluster.
2. How does ArgoCD store credentials for external clusters?
Correct! Cluster credentials are stored as labeled Kubernetes Secrets in the argocd namespace. ArgoCD creates a ServiceAccount on the target cluster and stores the token.
3. What is the primary purpose of an AppProject?
Correct! AppProjects define security boundaries -- they restrict which repos, clusters, namespaces, and resource types an Application can use.
Warning: The default project has no restrictions. In production, always create custom AppProjects with least-privilege access. Never let application teams use the default project.
spec:
# Only these repos can be used as Application sources
sourceRepos:
- 'https://github.com/your-org/frontend-manifests.git'
- 'https://github.com/your-org/shared-charts.git'
- 'https://charts.bitnami.com/bitnami'
# Wildcards are supported
# - 'https://github.com/your-org/*' # Any repo in org
# - '*' # Any repo (not recommended)
Security benefit: Even if a developer has ArgoCD access, they cannot point an Application to a malicious external repo -- the AppProject blocks it.
AppProject: Destination Restrictions
spec:
destinations:
# Allow specific namespace on local cluster
- namespace: frontend-prod
server: https://kubernetes.default.svc
# Wildcard namespace pattern
- namespace: 'frontend-*'
server: https://kubernetes.default.svc
# Deny specific namespace (use deny list)
# Note: Use namespaceResourceBlacklist for fine-grained control
# Which cluster-scoped resources are allowed
clusterResourceWhitelist:
- group: ''
kind: Namespace # Only allow Namespace creation
# Or block specific cluster resources
clusterResourceBlacklist:
- group: ''
kind: Node # Never allow Node modifications
AppProject: RBAC Roles
spec:
roles:
- name: frontend-developers
description: "Frontend team access"
policies:
# Allow sync and get for apps in this project
- p, proj:frontend-team:frontend-developers, applications, get, frontend-team/*, allow
- p, proj:frontend-team:frontend-developers, applications, sync, frontend-team/*, allow
# Deny delete
- p, proj:frontend-team:frontend-developers, applications, delete, frontend-team/*, deny
groups:
# Map to SSO groups (Azure AD groups)
- "azure-ad-frontend-team-group"
Multi-tenancy pattern: Each team gets their own AppProject with RBAC roles mapped to their Azure AD groups. Teams can sync their own apps but cannot touch other teams' resources.
AppProject: Sync Windows
spec:
syncWindows:
# Allow syncs only during business hours on weekdays
- kind: allow
schedule: '0 9 * * 1-5' # Cron: Mon-Fri at 9 AM
duration: 8h # For 8 hours (9 AM - 5 PM)
applications:
- '*'
namespaces:
- 'frontend-prod'
# Block syncs during a maintenance window
- kind: deny
schedule: '0 2 * * 6' # Sat at 2 AM
duration: 4h
applications:
- '*'
manualSync: true # Also blocks manual syncs
Sync windows let you control when deployments can happen. Perfect for production environments with change windows.
Quiz Time
AppProject Knowledge
1. Why is the "default" AppProject considered insecure for production?
Correct! The default project uses wildcards (*) for everything -- any repo, any cluster, any namespace, any resource. In production, create restricted projects per team.
2. What does clusterResourceWhitelist: [] (empty list) mean in an AppProject?
Correct! An empty whitelist means no cluster-scoped resources can be created by Applications in this project. Only namespace-scoped resources are permitted.
3. How can AppProject RBAC roles be integrated with Azure AD?
Correct! AppProject roles can map their groups field to Azure AD group names. When Dex or direct OIDC is configured for Azure AD, group claims are matched to project roles.
Credentials
Repository Credentials
ArgoCD needs credentials to clone private Git repositories and access private Helm chart repositories.
Credential templates use repo-creds label type (not repository). They match any repo URL that starts with the given prefix. Add one template and all repos under that org are covered automatically.
Azure Container Registry supports OCI Helm charts natively. Use a Service Principal or Managed Identity token.
Application Health Assessment
ArgoCD has built-in health checks for standard Kubernetes resource types.
Built-in Health Checks
Deployment -- checks rollout status
StatefulSet -- checks ready replicas
DaemonSet -- checks desired vs ready
Service -- checks endpoints
Ingress -- checks assigned address
PVC -- checks bound status
Pod -- checks phase and conditions
Health Statuses
Healthy -- working correctly
Progressing -- updating
Degraded -- failing
Suspended -- paused
Missing -- not found
Unknown -- no check
Sync Status In-Depth
Synced
Live = Desired. All is well.
OutOfSync
Live differs from Git. Needs sync.
Unknown
Cannot determine state.
Important distinction: Sync status and health status are independent. An app can be Synced but Degraded (the manifests match Git, but the app is crashing) or OutOfSync but Healthy (someone manually scaled up replicas).
Viewing Apps with CLI
# List all applications with their sync/health status
argocd app list
# Detailed view of a specific application
argocd app get frontend-prod
# Output includes:
# - Sync status and health status
# - Source repo, revision, path
# - Destination cluster and namespace
# - List of managed resources with individual status
# - Last sync result and any errors
# Filter by project
argocd app list --project frontend-team
# Get application manifests (what ArgoCD would apply)
argocd app manifests frontend-prod
Application Diffing
# View the diff between desired (Git) and live (cluster) state
argocd app diff frontend-prod
# The diff shows exactly what would change on sync
# - Added lines (resources in Git but not in cluster)
# - Removed lines (resources in cluster but not in Git)
# - Modified lines (resources that differ)
# Ignore specific fields in diff (e.g., auto-generated)
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # Ignore HPA-managed replicas
- group: '*'
kind: '*'
managedFieldsManagers:
- kube-controller-manager # Ignore controller changes
Quiz Time
Final Review
1. What is the label type for ArgoCD credential templates (matching multiple repos by URL prefix)?
Correct! The repo-creds type creates a credential template that matches any repository URL starting with the given prefix. The repository type is for individual repo credentials.
2. Can an application be "Synced" and "Degraded" at the same time?
Correct! Sync status and health status are independent. "Synced" means the live manifests match Git. "Degraded" means the resources are not working. Both can be true simultaneously.
3. What does ignoreDifferences with /spec/replicas help solve?
Correct! When HPA manages replicas, the live count may differ from Git. Using ignoreDifferences on /spec/replicas prevents ArgoCD from showing the app as OutOfSync due to HPA scaling.
Summary
Module 02 Recap
Applications define source (Git/Helm/Kustomize) and destination (cluster/namespace)