Kubernetes ArgoCD Tasks¶
- Hands-on Kubernetes exercises covering ArgoCD installation, CLI usage, application deployment, GitOps workflows, and the App of Apps pattern.
- Each task includes a description, scenario, and a detailed solution with step-by-step instructions.
- Practice these tasks to master ArgoCD from initial installation to advanced multi-app orchestration.
Table of Contents¶
- 01. Install ArgoCD via Helm
- 02. Expose ArgoCD via Ingress
- 03. Login with the ArgoCD CLI
- 04. Deploy Your First Application via CLI
- 05. Inspect Application Status and Health
- 06. Manually Trigger a Sync
- 07. Diff Live State Against Git
- 08. Enable Auto-Sync with Self-Heal and Auto-Prune
- 09. Test Self-Healing
- 10. View Deployment History
- 11. Rollback an Application
- 12. Deploy a Helm Chart via ArgoCD
- 13. Deploy from Kustomize via ArgoCD
- 14. Connect a Private Repository
- 15. The App of Apps Pattern
- 16. Use Sync Waves for Ordered Deployment
- 17. Manage Projects
- 18. Use Resource Hooks (PreSync / PostSync)
- 19. Troubleshoot a Failed Sync
- 20. Cleanup and Uninstall ArgoCD
- 21. Chain CLI Commands for Release Workflows
01. Install ArgoCD via Helm¶
Install ArgoCD on a Kubernetes cluster using the official Argo Helm chart.
Scenario:¶
◦ Your team has adopted GitOps and needs a central delivery platform for all Kubernetes workloads. ◦ You’ve chosen ArgoCD and need to install it on the cluster from scratch using Helm.
Hint: helm repo add, helm upgrade --install, kubectl get pods -n argocd
Solution
# 1. Add the Argo Helm repository
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update argo
# 2. Install ArgoCD in the argocd namespace (insecure mode: TLS terminated at Ingress)
helm upgrade --install argocd argo/argo-cd \
--namespace argocd \
--create-namespace \
--set server.insecure=true \
--wait
# 3. Verify all pods are Running
kubectl get pods -n argocd
# Expected output (all pods Running):
# NAME READY STATUS
# argocd-application-controller-0 1/1 Running
# argocd-dex-server-xxxx 1/1 Running
# argocd-redis-xxxx 1/1 Running
# argocd-repo-server-xxxx 1/1 Running
# argocd-server-xxxx 1/1 Running
# 4. Retrieve the initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d; echo
# Save this password - you'll need it for the CLI and Web UI
# 5. Verify the ArgoCD CRDs were installed
kubectl get crd | grep argoproj
# Expected: applications.argoproj.io, appprojects.argoproj.io, ...
02. Expose ArgoCD via Ingress¶
Expose the ArgoCD API server using an Nginx Ingress so it is accessible via a hostname instead of port-forwarding.
Scenario:¶
◦ Port-forwarding is fine for development but your team needs a stable URL to access the ArgoCD UI and CLI. ◦ You will create an Ingress pointing argocd.local at the ArgoCD server.
Prerequisites: Nginx Ingress Controller installed on the cluster.
Hint: argocd-ingress.yaml, /etc/hosts, kubectl apply
Solution
# 1. Create the Ingress manifest
cat > argocd-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
rules:
- host: argocd.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
# 2. Apply the Ingress
kubectl apply -f argocd-ingress.yaml
# 3. Verify the Ingress was created
kubectl get ingress -n argocd
# 4. Get the Ingress IP (use node IP for Kind/Minikube)
INGRESS_IP=$(kubectl get nodes \
-o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
echo "Ingress IP: ${INGRESS_IP}"
# 5. Add the hostname to /etc/hosts
echo "${INGRESS_IP} argocd.local" | sudo tee -a /etc/hosts
# 6. Verify connectivity
curl -s -o /dev/null -w "%{http_code}" http://argocd.local
# Expected: 200
# Open in browser
open http://argocd.local
# Fallback: port-forward if Ingress is not available
kubectl port-forward svc/argocd-server -n argocd 8080:80 &
open http://localhost:8080
03. Login with the ArgoCD CLI¶
Install the ArgoCD CLI and authenticate to the server.
Scenario:¶
◦ You will use the ArgoCD CLI to manage applications, repositories, and sync policies from the terminal. ◦ Before any CLI operations, you must authenticate to the ArgoCD server.
Hint: brew install argocd, argocd login, argocd account update-password
Solution
# ── Step 1: Install the ArgoCD CLI ──
# macOS
brew install argocd
# Linux
curl -sSL -o argocd-linux-amd64 \
https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64
# Verify installation
argocd version --client
# ── Step 2: Retrieve the admin password ──
ARGOCD_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d)
echo "Admin password: ${ARGOCD_PASSWORD}"
# ── Step 3: Login via Ingress ──
argocd login argocd.local \
--username admin \
--password "${ARGOCD_PASSWORD}" \
--insecure
# Login via port-forward (fallback)
argocd login localhost:8080 \
--username admin \
--password "${ARGOCD_PASSWORD}" \
--insecure
# ── Step 4: Verify login ──
argocd account get-user-info
# Output shows the currently logged-in user
argocd cluster list
# Should show: https://kubernetes.default.svc in-cluster ...
# ── Step 5: Change the admin password (recommended) ──
argocd account update-password \
--current-password "${ARGOCD_PASSWORD}" \
--new-password "MySecurePassword123!"
04. Deploy Your First Application via CLI¶
Create and deploy the classic ArgoCD guestbook example application using the CLI.
Scenario:¶
◦ You want to deploy a demo application to validate that ArgoCD can fetch from Git and deploy to the cluster. ◦ You will use the public argocd-example-apps repository and the guestbook path.
Hint: argocd app create, argocd app sync, kubectl port-forward
Solution
# 1. Create the application
argocd app create guestbook \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace guestbook \
--sync-option CreateNamespace=true
# 2. Verify the application was created
argocd app list
# Output shows:
# NAME CLUSTER NAMESPACE STATUS HEALTH SYNCPOLICY ...
# guestbook in-cluster guestbook OutOfSync Missing <none>
# 3. Manually trigger the first sync (deploy to cluster)
argocd app sync guestbook
# 4. Wait for the application to become Healthy + Synced
argocd app wait guestbook --health --sync --timeout 120
# 5. Verify Kubernetes resources were created
kubectl get all -n guestbook
# Expected:
# pod/guestbook-ui-xxxx Running
# service/guestbook-ui
# deployment/guestbook-ui
# 6. Access the application
kubectl port-forward svc/guestbook-ui -n guestbook 8081:80 &
open http://localhost:8081
# Cleanup the port-forward
kill %1
05. Inspect Application Status and Health¶
Use the CLI to inspect the full status, health, and resource tree of a deployed application.
Scenario:¶
◦ A team member deployed an application and you need to understand its current state without touching the cluster directly. ◦ You want to see what Kubernetes resources ArgoCD is managing.
Hint: argocd app get, --refresh, --output tree
Solution
# 1. Get a summary of the application
argocd app get guestbook
# Output includes:
# Name: guestbook
# Project: default
# Server: https://kubernetes.default.svc
# Namespace: guestbook
# URL: http://argocd.local/applications/guestbook
# Repo: https://github.com/argoproj/argocd-example-apps.git
# Target: HEAD
# Path: guestbook
# SyncWindow: Sync Allowed
# Sync Policy: <none>
# Sync Status: Synced to HEAD
# Health Status: Healthy
#
# GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
# Service guestbook guestbook-ui Synced Healthy
# apps Deployment guestbook guestbook-ui Synced Healthy ...
# 2. Force-refresh from Git before displaying
argocd app get guestbook --refresh
# 3. Display as a resource tree
argocd app get guestbook --output tree
# 4. Output as JSON for scripting
argocd app get guestbook -o json
# 5. Get JSON and parse with jq
argocd app get guestbook -o json | \
jq '{name: .metadata.name, sync: .status.sync.status, health: .status.health.status}'
# 6. Watch live updates
watch argocd app get guestbook
# 7. Get all applications and their health at once
argocd app list -o wide
06. Manually Trigger a Sync¶
Practice manually triggering a sync and understand all the sync options available.
Scenario:¶
◦ You pushed a change to Git and want to immediately deploy it without waiting for the 3-minute poll interval. ◦ You also want to understand force sync, dry-run, and selective resource sync options.
Hint: argocd app sync, --dry-run, --force, --resource
Solution
# 1. Basic sync - apply the Git state to the cluster
argocd app sync guestbook
# 2. Sync and wait for completion with a timeout
argocd app sync guestbook --timeout 120
# 3. Dry-run - preview what would change without applying
argocd app sync guestbook --dry-run
# 4. Force sync - replace resources even if spec is unchanged
argocd app sync guestbook --force
# 5. Sync with pruning - delete resources removed from Git
argocd app sync guestbook --prune
# 6. Sync a specific resource only (avoids re-applying unchanged resources)
argocd app sync guestbook \
--resource apps:Deployment:guestbook-ui
# 7. Sync only resources matching a label
argocd app sync guestbook \
--label app=guestbook-ui
# 8. Sync with apply-out-of-sync-only (skip already-synced resources)
argocd app sync guestbook --apply-out-of-sync-only
# 9. Sync multiple applications at once
argocd app sync guestbook app-of-apps efk-stack
# 10. Check the sync status after sync
argocd app get guestbook | grep -E "Sync|Health"
07. Diff Live State Against Git¶
Use argocd app diff to see exactly what has drifted between the live cluster state and Git.
Scenario:¶
◦ A developer manually patched a running Deployment with kubectl edit and your monitoring shows the application is OutOfSync. ◦ Before syncing to fix the drift, you want to see exactly what changed.
Hint: argocd app diff, kubectl scale, drift detection
Solution
# 1. Deliberately introduce drift - manually scale the deployment
kubectl scale deployment guestbook-ui --replicas=5 -n guestbook
# 2. Wait for ArgoCD to detect the drift
sleep 10
argocd app get guestbook | grep -E "Sync|Health"
# Sync Status: OutOfSync
# 3. Show the diff - what changed in live vs Git
argocd app diff guestbook
# Output highlights the replica count change:
# ===== apps/Deployment guestbook/guestbook-ui ======
# 10 - replicas: 5 (live)
# 10 + replicas: 1 (desired from Git)
# 4. Diff against a specific Git revision
argocd app diff guestbook --revision HEAD~1
# 5. Diff only a specific resource
argocd app diff guestbook \
--resource apps:Deployment:guestbook-ui
# 6. Use in CI - exit non-zero if drift is detected
if ! argocd app diff guestbook --exit-code; then
echo "DRIFT DETECTED - syncing..."
argocd app sync guestbook
fi
# 7. Restore the desired state from Git
argocd app sync guestbook
kubectl get deployment guestbook-ui -n guestbook
# READY should be back to 1/1
08. Enable Auto-Sync with Self-Heal and Auto-Prune¶
Configure automated sync so ArgoCD continuously reconciles the cluster state with Git.
Scenario:¶
◦ Your team pushes application changes directly to Git and expects them to be deployed automatically. ◦ You also want ArgoCD to clean up resources removed from Git and heal any manual drift.
Hint: argocd app set --sync-policy automated, --self-heal, --auto-prune
Solution
# 1. Enable automated sync (ArgoCD polls Git every ~3 minutes)
argocd app set guestbook --sync-policy automated
# 2. Verify the sync policy was applied
argocd app get guestbook | grep "Sync Policy"
# Sync Policy: Automated
# 3. Add self-heal: restores Git state if cluster is manually modified
argocd app set guestbook --self-heal
# 4. Add auto-prune: deletes resources removed from Git
argocd app set guestbook --auto-prune
# 5. Verify all options are active
argocd app get guestbook | grep -E "Sync Policy|Prune|Self Heal"
# 6. Test auto-sync: manually break the state
kubectl scale deployment guestbook-ui --replicas=5 -n guestbook
echo "Waiting for ArgoCD self-heal..."
sleep 30
kubectl get deployment guestbook-ui -n guestbook
# READY should be restored to 1/1 by ArgoCD
# 7. Configure using app manifest equivalents (declarative approach)
# The equivalent spec in an Application YAML:
# spec:
# syncPolicy:
# automated:
# prune: true
# selfHeal: true
# syncOptions:
# - CreateNamespace=true
# 8. Disable auto-sync (switch back to manual)
argocd app set guestbook --sync-policy none
argocd app get guestbook | grep "Sync Policy"
# Sync Policy: <none>
09. Test Self-Healing¶
Validate that ArgoCD self-healing works by deliberately introducing drift and observing automatic recovery.
Scenario:¶
◦ A runbook says to test ArgoCD self-healing quarterly. ◦ You need to break the cluster state and confirm ArgoCD repairs it within the reconciliation window.
Hint: kubectl scale, kubectl delete, watch argocd app get
Solution
# 0. Ensure auto-sync + self-heal are enabled
argocd app set guestbook --sync-policy automated --self-heal --auto-prune
# ── Test 1: Scale drift ──
# Break it
kubectl scale deployment guestbook-ui --replicas=10 -n guestbook
echo "Breaking: scaled to 10 replicas"
# Watch ArgoCD detect and fix it (up to ~30s)
watch -n 5 "kubectl get deployment guestbook-ui -n guestbook && argocd app get guestbook | grep -E 'Status|Health'"
# After ~30 seconds, replicas will return to the value in Git
kubectl get deployment guestbook-ui -n guestbook
# DESIRED should match Git (e.g., 1)
# ── Test 2: Delete a managed resource ──
# Delete the service
kubectl delete service guestbook-ui -n guestbook
echo "Deleted the guestbook-ui service"
# ArgoCD detects the missing resource and recreates it
sleep 30
kubectl get service guestbook-ui -n guestbook
# Service should be recreated
# ── Test 3: Manual label change ──
# Add a label not in Git
kubectl label deployment guestbook-ui -n guestbook manual-change=true
# ArgoCD will detect and revert this within the next sync cycle
sleep 60
kubectl get deployment guestbook-ui -n guestbook --show-labels | grep manual-change
# Label should be gone
# ── Summary ──
argocd app get guestbook
# Health Status: Healthy
# Sync Status: Synced
10. View Deployment History¶
Use argocd app history to inspect the deployment history of an application.
Scenario:¶
◦ You need to audit which Git commits were deployed over the past month. ◦ You want to identify the revision ID to use for a rollback.
Hint: argocd app history, -o json, jq
Solution
# 1. Create some history by triggering multiple syncs
argocd app sync guestbook
argocd app sync guestbook
argocd app sync guestbook
# 2. View the deployment history
argocd app history guestbook
# Output shows each deployment:
# ID DATE REVISION
# 0 2026-02-22 10:00:00 +0000 UTC HEAD (abc1234)
# 1 2026-02-22 10:05:00 +0000 UTC HEAD (abc1234)
# 2 2026-02-22 10:10:00 +0000 UTC HEAD (abc1234)
# 3. Output as JSON for scripting
argocd app history guestbook -o json
# 4. Extract key fields with jq
argocd app history guestbook -o json | \
jq '.[] | {id: .id, date: .deployedAt, revision: .revision}'
# 5. Find the most recent deployment
argocd app history guestbook -o json | jq '.[-1]'
# 6. Find deployments by Git commit SHA
argocd app history guestbook -o json | \
jq '.[] | select(.revision | contains("abc1234"))'
# 7. Save history to file for an audit log
argocd app history guestbook -o json > guestbook-deploy-history.json
cat guestbook-deploy-history.json
11. Rollback an Application¶
Rollback an application to a previously deployed revision using the ArgoCD CLI.
Scenario:¶
◦ A recent deployment introduced a regression. ◦ You need to immediately revert to the last known-good revision to restore service.
Hint: argocd app history, argocd app rollback, argocd app set --sync-policy
Solution
# 1. Inspect the deployment history to choose a target revision
argocd app history guestbook
# Note the ID of the revision you want to roll back to.
# In this example, we'll rollback to revision ID 0.
# 2. Perform the rollback
argocd app rollback guestbook 0
# ArgoCD rolls back the cluster state to the snapshot from revision 0.
# NOTE: Rollback disables automated sync on the app to prevent
# ArgoCD from immediately re-syncing forward again.
# 3. Wait for the rollback to complete
argocd app wait guestbook --health --timeout 120
# 4. Verify the status
argocd app get guestbook
# 5. Verify the Kubernetes resources reflect the rolled-back state
kubectl get all -n guestbook
# 6. Check history - rollback is recorded as a new entry
argocd app history guestbook
# 7. Re-enable auto-sync after the incident is resolved
argocd app set guestbook \
--sync-policy automated \
--self-heal \
--auto-prune
# 8. Confirm the app is back to Synced + Healthy
argocd app get guestbook | grep -E "Sync|Health"
12. Deploy a Helm Chart via ArgoCD¶
Use ArgoCD to deploy a Helm chart from a chart repository, with custom values managed in Git.
Scenario:¶
◦ You want ArgoCD to own the lifecycle of a Helm release, including upgrades and drift detection. ◦ Custom values.yaml overrides are stored in Git so changes go through GitOps.
Hint: argocd app create --helm-chart, --helm-set, --revision
Solution
# ── Option A: Deploy a Helm chart from an OCI / chart registry ──
argocd app create nginx-helm \
--repo https://charts.bitnami.com/bitnami \
--helm-chart nginx \
--revision 15.1.0 \
--dest-server https://kubernetes.default.svc \
--dest-namespace nginx-helm \
--sync-option CreateNamespace=true \
--helm-set service.type=ClusterIP \
--helm-set replicaCount=2
# Sync and wait
argocd app sync nginx-helm
argocd app wait nginx-helm --health --timeout 120
# ── Option B: Deploy a Helm chart stored in a Git repository ──
# Store values overrides in Git, e.g.:
# my-repo/nginx/values.yaml
# apiVersion: argoproj.io/v1alpha1 ← not needed, ArgoCD auto-detects Helm
argocd app create nginx-git-helm \
--repo https://github.com/my-org/my-charts.git \
--path nginx \
--dest-server https://kubernetes.default.svc \
--dest-namespace nginx-git-helm \
--sync-option CreateNamespace=true
# ── Update Helm values through CLI (without changing Git) ──
argocd app set nginx-helm \
--helm-set replicaCount=3 \
--helm-set image.tag=1.25.0
argocd app sync nginx-helm
# ── Verify ──
argocd app get nginx-helm | grep -E "Sync|Health|Revision"
kubectl get deployment -n nginx-helm
# ── Cleanup ──
argocd app delete nginx-helm --yes
kubectl delete namespace nginx-helm
13. Deploy from Kustomize via ArgoCD¶
Use ArgoCD to deploy a Kustomize-based application, showing how ArgoCD auto-detects the tool.
Scenario:¶
◦ Your team uses Kustomize overlays to manage configuration across environments (base + overlays). ◦ You want ArgoCD to render and deploy the Kustomize manifests for a specific overlay.
Hint: ArgoCD auto-detects Kustomize from kustomization.yaml. Point --path to the overlay directory.
Solution
# 1. Create a minimal Kustomize app structure in your Git repo
# Structure:
# kustomize-demo/
# ├── base/
# │ ├── deployment.yaml
# │ ├── service.yaml
# │ └── kustomization.yaml
# └── overlays/
# └── dev/
# ├── replica-patch.yaml
# └── kustomization.yaml
# 2. Create the application in ArgoCD pointing at a Kustomize overlay
argocd app create kustomize-demo \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path kustomize-guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace kustomize-demo \
--sync-option CreateNamespace=true
# ArgoCD detects kustomization.yaml and uses `kustomize build` to render manifests
# 3. Sync the application
argocd app sync kustomize-demo
argocd app wait kustomize-demo --health --timeout 120
# 4. View rendered manifests (what kustomize build produced)
argocd app manifests kustomize-demo
# 5. Verify resources
kubectl get all -n kustomize-demo
# 6. Apply a Kustomize image override via CLI
argocd app set kustomize-demo \
--kustomize-image gcr.io/argoproj/argocd-example-apps/guestbook-ui:v0.2
argocd app sync kustomize-demo
# 7. Cleanup
argocd app delete kustomize-demo --yes
kubectl delete namespace kustomize-demo
14. Connect a Private Repository¶
Add a private Git repository to ArgoCD using an HTTPS token or SSH key.
Scenario:¶
◦ Your application manifests live in a private GitHub repository. ◦ ArgoCD needs credentials to clone the repository in order to deploy from it.
Hint: argocd repo add, --username, --password, --ssh-private-key-path
Solution
# ── Option A: Connect via HTTPS Personal Access Token (PAT) ──
# Create a GitHub PAT with 'repo' scope at https://github.com/settings/tokens
argocd repo add https://github.com/my-org/private-repo.git \
--username git \
--password <YOUR_PAT_HERE>
# ── Option B: Connect via SSH Key ──
# Generate a deploy key (no passphrase)
ssh-keygen -t ed25519 -C "argocd-deploy-key" -f ~/.ssh/argocd-deploy-key -N ""
# Add the public key to GitHub repo:
# GitHub Repo → Settings → Deploy Keys → Add Deploy Key
# Paste the contents of ~/.ssh/argocd-deploy-key.pub
# Add the private key to ArgoCD
argocd repo add git@github.com:my-org/private-repo.git \
--ssh-private-key-path ~/.ssh/argocd-deploy-key
# ── Option C: Add a private Helm chart repository ──
argocd repo add https://my-private-charts.example.com \
--type helm \
--name private-charts \
--username admin \
--password <PASSWORD>
# ── Verify the connection ──
argocd repo list
# Expected output shows STATUS: Successful
# SERVER TYPE STATUS MESSAGE
# https://github.com/my-org/private-repo.git git Successful
# ── Use the private repo in an application ──
argocd app create my-private-app \
--repo https://github.com/my-org/private-repo.git \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace my-app
# ── Remove a repository ──
argocd repo rm https://github.com/my-org/private-repo.git
15. The App of Apps Pattern¶
Use a single root Application to manage a directory of child Application manifests declaratively.
Scenario:¶
◦ You have many microservices and want a single GitOps entry point. ◦ Adding or removing an app is as simple as committing or deleting a YAML file in Git. ◦ The App of Apps pattern makes fleet management fully declarative.
Hint: argocd app create pointing at a directory of Application YAMLs, argocd app list
Solution
# ── Step 1: Create child Application manifests and commit them to Git ──
# apps/guestbook.yaml
cat > /tmp/guestbook.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
# apps/nginx.yaml
cat > /tmp/nginx.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: nginx-ingress
destination:
server: https://kubernetes.default.svc
namespace: nginx-demo
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
# Commit both files to the apps/ directory of your Git repository
# ── Step 2: Create the root App of Apps ──
argocd app create app-of-apps \
--repo https://github.com/my-org/my-gitops-repo.git \
--path apps \
--dest-server https://kubernetes.default.svc \
--dest-namespace argocd \
--sync-policy automated \
--auto-prune \
--self-heal
# ── Step 3: Sync the root app ──
argocd app sync app-of-apps
# ArgoCD discovers all YAML files in apps/ and creates child Applications
# ── Step 4: Verify all child apps were created ──
argocd app list
# Expected:
# NAME CLUSTER NAMESPACE STATUS HEALTH SYNCPOLICY
# app-of-apps in-cluster argocd Synced Healthy Auto-Prune
# guestbook in-cluster guestbook Synced Healthy Auto-Prune
# nginx-demo in-cluster nginx-demo Synced Healthy Auto-Prune
# ── Step 5: Add a new application (GitOps way) ──
# Commit a new YAML file to the apps/ directory in Git.
# ArgoCD detects the change and automatically creates the child Application.
# No kubectl or argocd commands needed!
# ── Step 6: Remove an application (GitOps way) ──
# Delete the YAML file from apps/ in Git and commit.
# With auto-prune enabled, ArgoCD deletes the Application and its resources.
16. Use Sync Waves for Ordered Deployment¶
Control the order in which resources are synced during a deployment using sync wave annotations.
Scenario:¶
◦ You have a database, a backend API, and a frontend that must start in order. ◦ Sync waves let you define phases so that each component waits for the previous one to become healthy.
Hint: argocd.argoproj.io/sync-wave annotation, wave numbers
Solution
# Sync waves are set as annotations on Kubernetes resources in Git.
# Resources in lower waves deploy and become healthy before higher waves start.
# ── Example: 3-tier app with ordered deployment ──
# wave 0: Namespace and ConfigMaps (no dependencies)
cat > /tmp/namespace.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: my-app
annotations:
argocd.argoproj.io/sync-wave: "0"
EOF
# wave 1: Database (must be healthy before the API starts)
cat > /tmp/database-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: my-app
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_PASSWORD
value: "mysecretpassword"
EOF
# wave 2: Backend API (waits for database to be healthy)
cat > /tmp/api-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-api
namespace: my-app
annotations:
argocd.argoproj.io/sync-wave: "2"
spec:
replicas: 2
selector:
matchLabels:
app: backend-api
template:
metadata:
labels:
app: backend-api
spec:
containers:
- name: api
image: my-api:latest
EOF
# wave 3: Frontend (waits for the API to be healthy)
cat > /tmp/frontend-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: my-app
annotations:
argocd.argoproj.io/sync-wave: "3"
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: my-frontend:latest
EOF
# Commit all files to a Git path, then create the app:
argocd app create my-app \
--repo https://github.com/my-org/my-repo.git \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace my-app \
--sync-option CreateNamespace=true
argocd app sync my-app
# Watch the wave-by-wave deployment progress
watch argocd app get my-app
# Wave execution order:
# Wave 0: Namespace created
# Wave 1: postgres Deployment reaches Healthy
# Wave 2: backend-api Deployment reaches Healthy
# Wave 3: frontend Deployment reaches Healthy
17. Manage Projects¶
Create an ArgoCD Project to restrict what repositories, clusters, and namespaces an application can use.
Scenario:¶
◦ Your cluster hosts applications for multiple teams (frontend, backend, ops). ◦ You want to prevent the frontend team from accidentally deploying to the kube-system namespace. ◦ ArgoCD Projects provide RBAC-level isolation between teams.
Hint: argocd proj create, --src-repos, --dest, argocd proj list
Solution
# 1. Create a project for the frontend team
argocd proj create frontend \
--description "Frontend team applications" \
--src-repos "https://github.com/my-org/frontend-repo.git" \
--dest "https://kubernetes.default.svc,frontend-*" \
--dest "https://kubernetes.default.svc,staging"
# --src-repos: only this repo is allowed as a source
# --dest: only these patterns are allowed as destinations (cluster,namespace)
# 2. Verify the project was created
argocd proj list
# 3. View project details
argocd proj get frontend
# 4. Add additional allowed source repositories
argocd proj add-source frontend \
"https://github.com/my-org/shared-charts.git"
# 5. Add allowed destinations
argocd proj add-destination frontend \
https://kubernetes.default.svc production-frontend
# 6. Set cluster-scope resource DENY list (prevent modification of cluster-level resources)
argocd proj deny-cluster-resource frontend "*" "*"
argocd proj allow-cluster-resource frontend "" "Namespace"
# 7. Assign an application to the project
argocd app create frontend-app \
--project frontend \
--repo https://github.com/my-org/frontend-repo.git \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace frontend-prod
# 8. Attempting to use a disallowed repo will fail with a permission error
argocd app create bad-app \
--project frontend \
--repo https://github.com/other-org/other-repo.git \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace kube-system
# Error: application destination {... kube-system} is not permitted in project 'frontend'
# 9. Cleanup
argocd app delete frontend-app --yes 2>/dev/null || true
argocd proj delete frontend
18. Use Resource Hooks (PreSync / PostSync)¶
Use ArgoCD resource hooks to run Jobs before or after a sync operation - e.g., database migrations or smoke tests.
Scenario:¶
◦ Your application requires a database migration to run before the new version starts. ◦ After deployment you want a smoke test to verify the application is responding correctly.
Hint: argocd.argoproj.io/hook annotation, PreSync, PostSync, argocd.argoproj.io/hook-delete-policy
Solution
# Hooks are standard Kubernetes Jobs with special ArgoCD annotations.
# They are stored in your Git repository alongside the application manifests.
# ── PreSync Hook: Run database migration before sync ──
cat > /tmp/pre-sync-migration.yaml << 'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
namespace: my-app
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: my-app:latest
command: ["./migrate.sh"]
env:
- name: DB_HOST
value: postgres.my-app.svc
EOF
# ── PostSync Hook: Run smoke test after sync ──
cat > /tmp/post-sync-smoke-test.yaml << 'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: smoke-test
namespace: my-app
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: smoke-test
image: curlimages/curl:latest
command:
- sh
- -c
- |
echo "Running smoke test..."
until curl -sf http://my-app.my-app.svc/health; do
echo "Service not ready, retrying in 5s..."
sleep 5
done
echo "Smoke test passed!"
EOF
# ── Sync Wave + Hook combination ──
# Use sync waves to order hooks relative to other resources:
# annotations:
# argocd.argoproj.io/hook: PreSync
# argocd.argoproj.io/sync-wave: "-5" # Run early within PreSync phase
# ── Hook Delete Policies ──
# HookSucceeded: Delete job when it succeeds (default)
# HookFailed: Delete job when it fails
# BeforeHookCreation: Delete previous run before creating a new one
# ── Commit the hook files to Git and sync ──
argocd app sync my-app
# Watch hooks execute
kubectl get jobs -n my-app -w
# Check hook logs
kubectl logs job/db-migration -n my-app
kubectl logs job/smoke-test -n my-app
19. Troubleshoot a Failed Sync¶
Diagnose and fix a sync failure using CLI commands and kubectl.
Scenario:¶
◦ An application is stuck in OutOfSync or Degraded state. ◦ You need to identify the root cause and resolve it.
Hint: argocd app get, argocd app diff, kubectl describe, kubectl logs
Solution
# ── Step 1: Get the high-level status ──
argocd app get <app-name>
# Look for degraded resources or error messages in the resource list
# ── Step 2: Show the diff to understand what ArgoCD is trying to apply ──
argocd app diff <app-name>
# ── Step 3: Get the rendered manifests ──
argocd app manifests <app-name>
# Validate the manifest looks correct
# ── Step 4: Check ArgoCD conditions and events ──
kubectl describe application <app-name> -n argocd
# Look at Conditions and Events sections
# ── Step 5: Check the ArgoCD application controller logs ──
kubectl logs -n argocd \
-l app.kubernetes.io/name=argocd-application-controller \
--tail=100 | grep -i "error\|failed\|<app-name>"
# ── Step 6: Check the repo-server logs (manifest rendering issues) ──
kubectl logs -n argocd \
-l app.kubernetes.io/name=argocd-repo-server \
--tail=50 | grep -i "error\|failed"
# ── Step 7: Force-refresh and retry sync ──
argocd app get <app-name> --refresh
argocd app sync <app-name> --force
# ── Step 8: Common issues and fixes ──
# Issue: Repository error (auth failure)
argocd repo list # Check STATUS column
argocd repo get <repo-url> # Check detailed status
# Issue: Out of sync but diff shows no changes (stuck sync)
argocd app sync <app-name> --force --replace
# Issue: Hook is stuck running
kubectl get jobs -n <namespace>
kubectl delete job <stuck-job-name> -n <namespace>
argocd app sync <app-name>
# Issue: Resource exists with different owner (e.g., fields managed by another controller)
argocd app sync <app-name> --server-side-apply
# Issue: Namespace doesn't exist
argocd app set <app-name> --sync-option CreateNamespace=true
argocd app sync <app-name>
# ── Step 9: App of Apps - child apps not created ──
argocd app get app-of-apps # Check root is Synced
argocd repo list # Confirm repo is accessible
argocd app manifests app-of-apps # Confirm apps/ dir renders correctly
kubectl get applications -n argocd # Check all Application CRs
20. Cleanup and Uninstall ArgoCD¶
Safely remove all ArgoCD applications and uninstall ArgoCD from the cluster.
Scenario:¶
◦ You’ve finished a demo or training environment and need to tear everything down cleanly. ◦ Resources must be deleted in the correct order to avoid orphaned namespaces or finalizer deadlocks.
Hint: argocd app delete --cascade, helm uninstall, finalizer removal
Solution
# ── Step 1: Delete all managed applications (cascade removes K8s resources too) ──
# List all applications first
argocd app list
# Delete individual apps with cascade
argocd app delete guestbook --yes
argocd app delete app-of-apps --yes
# Or delete ALL applications in one command
argocd app list -o name | xargs -I {} argocd app delete {} --yes
# ── Step 2: Verify managed namespaces were cleaned up ──
kubectl get namespace | grep -E "guestbook|efk|nginx"
# ── Step 3: Remove connected repositories ──
argocd repo list | awk 'NR>1 {print $1}' | xargs -I {} argocd repo rm {}
# ── Step 4: Remove custom Projects (if any were created) ──
argocd proj list | awk 'NR>1 && $1 != "default" {print $1}' | \
xargs -I {} argocd proj delete {}
# ── Step 5: If apps are stuck due to finalizers, remove them manually ──
# List all Application CRs
kubectl get applications -n argocd
# Remove a stuck application's finalizer
kubectl patch application <app-name> -n argocd \
-p '{"metadata":{"finalizers":[]}}' \
--type merge
# ── Step 6: Uninstall ArgoCD via Helm ──
helm uninstall argocd --namespace argocd
# ── Step 7: Delete the ArgoCD namespace and CRDs ──
kubectl delete namespace argocd
# Delete ArgoCD CRDs
kubectl get crd | grep argoproj.io | awk '{print $1}' | \
xargs kubectl delete crd
# ── Step 8: Verify everything is gone ──
kubectl get all -n argocd # Should return "No resources found"
kubectl get crd | grep argoproj # Should return nothing
helm list --all-namespaces # argocd should not appear
21. Chain CLI Commands for Release Workflows¶
Practice common multi-step ArgoCD CLI workflows for day-to-day GitOps operations.
Scenario:¶
◦ You want repeatable, scriptable workflows for deploying, validating, and rolling back GitOps applications. ◦ These one-liners and scripts model real-world CI/CD integration patterns.
Hint: Chain argocd and kubectl commands with &&, ||, and loops.
Solution
# ── Workflow 1: Install ArgoCD, login, and deploy guestbook in one sequence ──
helm upgrade --install argocd argo/argo-cd \
--namespace argocd --create-namespace \
--set server.insecure=true --wait && \
PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d) && \
argocd login argocd.local \
--username admin --password "$PASS" --insecure && \
argocd app create guestbook \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace guestbook \
--sync-option CreateNamespace=true && \
argocd app sync guestbook && \
argocd app wait guestbook --health --timeout 120 && \
echo "Guestbook deployed successfully!"
# ── Workflow 2: Deploy and auto-heal setup ──
argocd app create guestbook \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace guestbook \
--sync-policy automated \
--auto-prune \
--self-heal \
--sync-option CreateNamespace=true && \
argocd app wait guestbook --health && \
argocd app get guestbook
# ── Workflow 3: Health-check and rollback on failure ──
argocd app sync guestbook --timeout 120 && \
argocd app wait guestbook --health --timeout 60 || \
( echo "Deployment failed - rolling back." && argocd app rollback guestbook 0 )
# ── Workflow 4: Check all apps and alert on degraded ──
DEGRADED=$(argocd app list -o json | jq -r \
'.[] | select(.status.health.status != "Healthy") | .metadata.name')
if [ -n "$DEGRADED" ]; then
echo "ALERT: Degraded applications detected:"
echo "$DEGRADED"
else
echo "All applications are Healthy."
fi
# ── Workflow 5: Force-refresh and sync all out-of-sync apps ──
argocd app list -o json | \
jq -r '.[] | select(.status.sync.status == "OutOfSync") | .metadata.name' | \
xargs -I {} bash -c 'argocd app get {} --refresh && argocd app sync {}'
# ── Workflow 6: Deploy App of Apps and wait for all children ──
argocd app create app-of-apps \
--repo https://github.com/my-org/my-gitops-repo.git \
--path apps \
--dest-server https://kubernetes.default.svc \
--dest-namespace argocd \
--sync-policy automated && \
argocd app sync app-of-apps && \
sleep 10 && \
argocd app list
# ── Workflow 7: Export all application definitions for backup ──
mkdir -p argocd-backup
argocd app list -o name | while read APP; do
argocd app get "$APP" -o json > "argocd-backup/${APP}.json"
echo "Backed up: ${APP}"
done
ls -la argocd-backup/
# ── Workflow 8: Multi-environment deployment with different values ──
for ENV in dev staging prod; do
argocd app create "guestbook-${ENV}" \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace "guestbook-${ENV}" \
--sync-policy automated \
--auto-prune \
--self-heal \
--sync-option CreateNamespace=true
echo "Created: guestbook-${ENV}"
done
argocd app list
# ── Workflow 9: Full teardown ──
argocd app list -o name | xargs -I {} argocd app delete {} --yes && \
helm uninstall argocd -n argocd && \
kubectl delete namespace argocd && \
echo "ArgoCD fully removed."
# Cleanup multi-env apps
for ENV in dev staging prod; do
kubectl delete namespace "guestbook-${ENV}" 2>/dev/null || true
done
rm -rf argocd-backup/
Diagram: ArgoCD GitOps Workflow¶
┌─────────────────────────────────────────────────────────────────────┐
│ ArgoCD GitOps Flow │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Developer ──► git push ──► Git Repository (Source of Truth) │
│ │ │
│ ArgoCD polls every ~3 min │
│ │ │
│ ┌────────────────────────▼──────────────────────┐ │
│ │ ArgoCD Control Plane │ │
│ │ │ │
│ │ API Server ◄── argocd CLI / Web UI │ │
│ │ │ │ │
│ │ App Controller ──► compare desired vs live │ │
│ │ │ │ │
│ │ Repo Server ──► renders Helm/Kustomize/YAML │ │
│ └────────────────────────┬──────────────────────┘ │
│ │ │
│ sync / heal │
│ │ │
│ ┌────────────────────────▼──────────────────────┐ │
│ │ Kubernetes Cluster │ │
│ │ │ │
│ │ Namespace: guestbook ──► Deployment, Svc │ │
│ │ Namespace: efk ──► Elasticsearch,… │ │
│ │ Namespace: argocd ──► App of Apps │ │
│ └───────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Quick Reference: Essential ArgoCD CLI Commands¶
| Command | Description |
|---|---|
argocd login <server> | Authenticate to ArgoCD server |
argocd account update-password | Change the admin password |
argocd cluster list | List connected clusters |
argocd repo add <url> | Connect a Git or Helm repository |
argocd repo list | List all connected repositories |
argocd app create <name> | Create a new application |
argocd app list | List all applications and their status |
argocd app get <name> | Get detailed status and resource tree |
argocd app get <name> --refresh | Force-refresh from Git before displaying |
argocd app sync <name> | Manually trigger a sync |
argocd app sync <name> --dry-run | Preview a sync without applying |
argocd app diff <name> | Show diff between Git and live state |
argocd app set <name> --sync-policy automated | Enable automated sync |
argocd app set <name> --self-heal --auto-prune | Enable self-heal and auto-prune |
argocd app wait <name> --health | Wait for application to become Healthy |
argocd app history <name> | Show deployment history |
argocd app rollback <name> <revision-id> | Rollback to a previous revision |
argocd app manifests <name> | Show rendered Kubernetes manifests |
argocd app delete <name> --yes | Delete application (cascades to K8s resources) |
argocd proj create <name> | Create a new project |
argocd proj list | List all projects |
argocd context | List all saved server contexts |
argocd context <name> | Switch to a different ArgoCD server context |