GitOps with ArgoCD

Implement Continuous Deployment with ArgoCD

Learn how to deploy and manage applications using GitOps principles with ArgoCD.

What You’ll Learn

  • Install ArgoCD on AWS EKS cluster
  • Access and configure ArgoCD Web UI
  • Deploy applications using GitOps from Git repository
  • Create ArgoCD Applications for nginx and Todo App
  • Monitor application sync status and health
  • Understand GitOps workflow and benefits

Prerequisites

  • AWS EKS cluster running (see Exercise 1)
  • kubectl configured and connected to your cluster
  • Git repository with Kubernetes manifests
  • Basic understanding of Kubernetes resources

Verify prerequisites:

kubectl get nodes
kubectl version --client

Understanding GitOps and ArgoCD

What is GitOps?

GitOps is a way of managing Kubernetes deployments where:

  • Git repository = Single source of truth
  • Desired state = Manifests in Git
  • Actual state = What’s running in cluster
  • ArgoCD = Ensures desired state matches actual state

Traditional Deployment vs GitOps

Traditional (Push Model):

Developer β†’ kubectl apply β†’ Cluster

Problems:

  • No audit trail
  • Manual process
  • Hard to rollback
  • Credentials needed

GitOps (Pull Model):

Developer β†’ Git Push β†’ ArgoCD detects change β†’ Cluster syncs automatically

Benefits:

  • Full audit trail (Git history)
  • Automated sync
  • Easy rollback (Git revert)
  • No cluster credentials needed for developers

How ArgoCD Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Git Repo     β”‚ ← Developer pushes changes
β”‚ (manifests)  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       ↓ (ArgoCD polls every 3 minutes)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ArgoCD       β”‚ ← Compares desired vs actual state
β”‚ Application  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       ↓ (Auto-sync or manual sync)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Kubernetes   β”‚ ← Resources created/updated
β”‚ Cluster      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step 1: Install ArgoCD

1.1 Create ArgoCD Namespace

kubectl create namespace argocd

1.2 Install ArgoCD

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

This installs:

  • ArgoCD API Server
  • Repository Server (connects to Git)
  • Application Controller (syncs resources)
  • Redis (caching)
  • Dex (SSO, optional)
  • Web UI

Expected output:

customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/applicationsets.argoproj.io created
...
deployment.apps/argocd-server created

1.3 Verify Installation

# Check all pods are running
kubectl get pods -n argocd

Expected:

NAME                                  READY   STATUS    RESTARTS   AGE
argocd-application-controller-0       1/1     Running   0          2m
argocd-applicationset-controller-...  1/1     Running   0          2m
argocd-dex-server-...                 1/1     Running   0          2m
argocd-notifications-controller-...   1/1     Running   0          2m
argocd-redis-...                      1/1     Running   0          2m
argocd-repo-server-...                1/1     Running   0          2m
argocd-server-...                     1/1     Running   0          2m

Wait for all pods to be ready (may take 2-3 minutes):

kubectl wait --for=condition=ready pod --all -n argocd --timeout=300s

Step 2: Access ArgoCD Web UI

2.1 Expose ArgoCD Server

Option 1: Port Forward (Recommended for Testing)

kubectl port-forward svc/argocd-server -n argocd 8080:443

Keep this terminal open. ArgoCD UI will be available at https://localhost:8080

Option 2: LoadBalancer (For Production)

kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

Wait for external IP:

kubectl get svc argocd-server -n argocd -w

Get the URL:

kubectl get svc argocd-server -n argocd -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

2.2 Get Initial Admin Password

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo

Copy this password! You’ll need it to login.

Example output:

TkX9vR2zQ8yHn4Wp

2.3 Login to ArgoCD Web UI

  1. Open browser to https://localhost:8080 (or LoadBalancer URL)
  2. Accept the self-signed certificate warning
  3. Login:
    • Username: admin
    • Password: (from step 2.2)

You should see the ArgoCD dashboard!

In the ArgoCD UI:

  1. Click User Info (top right)
  2. Click Update Password
  3. Enter current password and new password
  4. Click Save

Step 3: Understand the Git Repository Structure

3.1 Repository Overview

We’ll use: https://github.com/larsappel/ArgoCD.git

Repository structure:

ArgoCD/
β”œβ”€β”€ nginx/
β”‚   β”œβ”€β”€ nginx-deployment.yaml
β”‚   └── nginx-service.yaml
β”‚
└── ToDoApp-K8S-Manifests/
    β”œβ”€β”€ storageclass.yaml
    β”œβ”€β”€ mongodb-statefulset.yaml
    β”œβ”€β”€ mongodb-service.yaml
    β”œβ”€β”€ mongo-init-job.yaml
    β”œβ”€β”€ mongo-express-deployment.yaml
    β”œβ”€β”€ mongo-express-service.yaml
    β”œβ”€β”€ todo-configmap.yaml
    β”œβ”€β”€ todo-deployment.yaml
    └── todo-service.yaml

3.2 What Each Folder Contains

nginx/ - Simple nginx deployment for testing

  • nginx-deployment.yaml: Nginx pods
  • nginx-service.yaml: Nginx service

ToDoApp-K8S-Manifests/ - Complete Todo application

  • Storage: StorageClass and StatefulSet for MongoDB
  • Database: MongoDB with persistent storage
  • Admin UI: Mongo Express for database inspection
  • Application: Todo app with ConfigMap

Step 4: Deploy Nginx with ArgoCD (Web UI)

4.1 Create New Application

In the ArgoCD Web UI:

  1. Click + NEW APP (top left)
  2. Fill in the form:

GENERAL:

  • Application Name: nginx
  • Project Name: default
  • Sync Policy: Manual

SOURCE:

  • Repository URL: https://github.com/larsappel/ArgoCD.git
  • Revision: HEAD
  • Path: nginx

DESTINATION:

  • Cluster URL: https://kubernetes.default.svc (should be auto-filled)
  • Namespace: default
  1. Click CREATE (top of page)

4.2 View Application Status

You should see the nginx app in the dashboard:

Status: OutOfSync - This means Git has resources that aren’t in the cluster yet

Click on the nginx application to see the details.

4.3 Sync the Application

Method 1: Manual Sync (Full Control)

  1. Click SYNC button (top right)
  2. Review the resources that will be created:
    • Deployment: nginx
    • Service: nginx-service
  3. Click SYNCHRONIZE

Expected: Status changes to:

  • Syncing β†’ Synced
  • Health: Progressing β†’ Healthy

4.4 Verify Deployment in Cluster

# Check pods
kubectl get pods -l app=nginx

# Check service
kubectl get svc nginx-service

Expected:

NAME                     READY   STATUS    RESTARTS   AGE
nginx-85b98978db-xxxxx   1/1     Running   0          1m

NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   10.100.x.x      <none>        80/TCP    1m

4.5 Explore Application in UI

In the ArgoCD UI:

Application View shows:

  • Resource tree (Deployment β†’ ReplicaSet β†’ Pods)
  • Sync status
  • Health status
  • Last sync time

Click on resources to see:

  • Live manifest
  • Desired manifest (from Git)
  • Differences
  • Events
  • Logs (for pods)

Step 5: Deploy Todo App with ArgoCD (Web UI)

5.1 Create Todo App Application

In the ArgoCD Web UI:

  1. Click + NEW APP
  2. Fill in the form:

GENERAL:

  • Application Name: todo-app
  • Project Name: default
  • Sync Policy: Manual

SOURCE:

  • Repository URL: https://github.com/larsappel/ArgoCD.git
  • Revision: HEAD
  • Path: ToDoApp-K8S-Manifests

DESTINATION:

  • Cluster URL: https://kubernetes.default.svc
  • Namespace: default
  1. Click CREATE

5.2 Review Resources Before Sync

  1. Click on the todo-app application
  2. Status shows OutOfSync
  3. You can see all resources that will be created:
    • StorageClass: ebs-sc
    • StatefulSet: mongodb
    • Services: mongodb-service, todo-service, mongo-express-service
    • Deployments: todo-deployment, mongo-express
    • ConfigMap: todo-configmap
    • Job: mongo-init-job

5.3 Sync the Todo App

  1. Click SYNC button
  2. Review all resources
  3. Click SYNCHRONIZE

ArgoCD will:

  1. Create StorageClass first
  2. Create MongoDB StatefulSet with PVC
  3. Wait for MongoDB to be ready
  4. Run mongo-init-job to seed database
  5. Deploy Todo app and Mongo Express
  6. Create services

5.4 Monitor Deployment Progress

Watch the application tree in real-time:

Initial state:

StorageClass: Synced, Healthy
StatefulSet: Syncing, Progressing
PVC: Syncing, Pending

After ~2 minutes:

StorageClass: Synced, Healthy
StatefulSet: Synced, Healthy
PVC: Synced, Bound
Services: Synced, Healthy
Deployments: Synced, Healthy

5.5 Verify in Cluster

# Check all resources
kubectl get all -l app=todo-webapp

# Check MongoDB
kubectl get statefulset mongodb
kubectl get pvc

# Check services
kubectl get svc | grep -E 'todo-service|mongo-express-service'

5.6 Access the Todo Application

Get service endpoints:

# Todo App
kubectl get svc todo-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

# Mongo Express
kubectl get svc mongo-express-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

If using LoadBalancer type, wait 2-3 minutes for AWS to provision the load balancers.

Access in browser:

http://<todo-service-url>
http://<mongo-express-service-url>

Step 6: Enable Auto-Sync

6.1 Enable Auto-Sync for Nginx

  1. In ArgoCD UI, click on nginx application
  2. Click APP DETAILS (top left)
  3. Click ENABLE AUTO-SYNC
  4. Options:
    • βœ… Prune Resources (delete resources not in Git)
    • βœ… Self Heal (revert manual changes)
  5. Click OK

Now ArgoCD will automatically sync changes from Git every 3 minutes!

6.2 Test Auto-Sync

Make a change in Git (using GitHub web interface or locally):

  1. Go to: https://github.com/larsappel/ArgoCD
  2. Navigate to nginx/nginx-deployment.yaml
  3. Click Edit (pencil icon)
  4. Change replicas from 1 to 3:
    spec:
      replicas: 3  # Changed from 1
  5. Commit the change

Watch ArgoCD UI:

  • Wait ~3 minutes (polling interval)
  • Status changes to OutOfSync
  • ArgoCD automatically syncs
  • Status changes to Synced
  • You now have 3 nginx pods!

Verify:

kubectl get pods -l app=nginx

Expected: 3 nginx pods running

6.3 Test Self-Heal

Make a manual change to the cluster:

kubectl scale deployment nginx --replicas=5

Watch ArgoCD UI:

  • ArgoCD detects the drift
  • Self-heal kicks in (~30 seconds)
  • Reverts back to 3 replicas (desired state from Git)

This demonstrates GitOps: Git is the source of truth!


Step 7: Understanding Application Health

7.1 Health Status Types

ArgoCD shows different health statuses:

Healthy βœ…

  • Resource is running as expected
  • Example: Deployment has desired replicas running

Progressing πŸ”„

  • Resource is being created or updated
  • Example: Deployment rolling out new pods

Degraded ⚠️

  • Resource exists but not fully functional
  • Example: Some pods are crashing

Suspended ⏸️

  • Resource is intentionally paused
  • Example: CronJob is suspended

Missing ❌

  • Resource should exist but doesn’t
  • Example: Resource deleted manually

Unknown ❓

  • ArgoCD can’t determine health
  • Example: Custom resource without health check

7.2 Sync Status Types

Synced βœ…

  • Git state matches cluster state
  • Everything is up to date

OutOfSync ⚠️

  • Git state differs from cluster state
  • Changes need to be applied

Unknown ❓

  • ArgoCD can’t determine sync status

Step 8: Rollback Using GitOps

8.1 View Application History

  1. In ArgoCD UI, click on nginx application
  2. Click HISTORY AND ROLLBACK tab
  3. You see all sync operations with:
    • Revision (Git commit SHA)
    • Timestamp
    • User/System who triggered sync
    • Status

8.2 Rollback to Previous Version

Method 1: ArgoCD UI Rollback

  1. In HISTORY AND ROLLBACK tab
  2. Find the previous successful sync
  3. Click (three dots)
  4. Click ROLLBACK
  5. Confirm

ArgoCD syncs to the previous Git commit!

Method 2: Git Revert (Preferred)

This is the true GitOps way:

# Clone the repo locally
git clone https://github.com/larsappel/ArgoCD.git
cd ArgoCD

# View history
git log --oneline nginx/

# Revert the last commit
git revert HEAD

# Push
git push origin main

ArgoCD will detect the revert and sync automatically (if auto-sync enabled).


Step 9: Application Best Practices

9.1 Sync Policy Recommendations

Manual Sync:

  • Use for: Production environments
  • Why: Human approval before changes
  • When: Critical applications

Auto-Sync:

  • Use for: Development/staging environments
  • Why: Fast feedback loop
  • When: Non-critical applications

Auto-Sync with Prune:

  • Use for: Fully GitOps-managed apps
  • Why: Ensures Git is single source of truth
  • Risk: Can delete resources not in Git

9.2 Repository Organization

Good structure:

repo/
β”œβ”€β”€ base/              # Shared resources
β”œβ”€β”€ overlays/
β”‚   β”œβ”€β”€ dev/          # Dev environment
β”‚   β”œβ”€β”€ staging/      # Staging environment
β”‚   └── prod/         # Production environment

Per-app structure:

repo/
β”œβ”€β”€ app1/
β”‚   └── manifests/
β”œβ”€β”€ app2/
β”‚   └── manifests/

9.3 Namespace Strategy

Option 1: App per namespace

DESTINATION:
  Namespace: nginx-app
  Namespace: todo-app

Option 2: Environment per namespace

DESTINATION:
  Namespace: dev
  Namespace: prod

Step 10: Monitoring and Troubleshooting

10.1 View Application Logs

In ArgoCD UI:

  1. Click on application
  2. Click on a Pod in the resource tree
  3. Click LOGS tab
  4. View real-time logs

10.2 View Events

In ArgoCD UI:

  1. Click on any resource
  2. Click EVENTS tab
  3. See Kubernetes events for that resource

10.3 Compare Live vs Desired State

In ArgoCD UI:

  1. Click on any resource
  2. Click DIFF tab
  3. See differences between:
    • Desired: What’s in Git
    • Live: What’s in cluster

Green: Git has it, cluster doesn’t Red: Cluster has it, Git doesn’t

10.4 Refresh vs Hard Refresh

Refresh:

  • Compares Git and cluster state
  • No changes to cluster
  • Fast (uses cached data)

Hard Refresh:

  • Re-fetches everything from cluster
  • Clears cache
  • Slow but accurate

Use Hard Refresh when:

  • State seems incorrect
  • Resources modified externally
  • Debugging sync issues

Troubleshooting

Issue 1: Application Stuck in “Progressing”

Symptom: Application shows “Progressing” health for >5 minutes

Check:

In ArgoCD UI:

  1. Click on the resource showing “Progressing”
  2. Check EVENTS tab
  3. Check LOGS tab (if pod)

Common causes:

  • Image pull errors
  • Resource limits too low
  • Missing dependencies (ConfigMap, Secret)

Fix:

# Describe the resource in kubectl
kubectl describe pod <pod-name>

# Check events
kubectl get events --sort-by='.lastTimestamp'

Issue 2: “OutOfSync” But Already Synced

Symptom: Application shows OutOfSync immediately after sync

Cause: Resource has fields managed by Kubernetes controllers

Common culprits:

  • Service with clusterIP auto-assigned
  • StatefulSet with volumeClaimTemplates
  • Deployment with resource defaults

Fix:

Add ignore differences in Application:

  1. Click APP DETAILS
  2. Scroll to SYNC POLICY
  3. Add IGNORE DIFFERENCE:
    Group: ""
    Kind: Service
    JSONPointers: ["/spec/clusterIP"]

Issue 3: “Permission Denied” Errors

Symptom: ArgoCD can’t create resources

Check:

# Check ArgoCD controller logs
kubectl logs -n argocd deployment/argocd-application-controller

Common causes:

  • ArgoCD doesn’t have RBAC permissions for resource type
  • Namespace doesn’t exist
  • Resource requires cluster-admin

Fix:

Grant additional permissions to ArgoCD:

# Create ClusterRole and ClusterRoleBinding if needed

Issue 4: “Repository Not Found”

Symptom: Application can’t connect to Git repository

Check:

  1. In ArgoCD UI: Settings β†’ Repositories
  2. Check connection status

Common causes:

  • Private repo without credentials
  • Wrong URL
  • Network issues

Fix:

For private repos, add credentials:

  1. Settings β†’ Repositories
  2. Click CONNECT REPO
  3. Add SSH key or HTTPS credentials

Issue 5: Sync Takes Too Long

Symptom: Manual sync is very slow

Cause:

  • Large number of resources
  • Slow image pulls
  • Resource dependencies

Check:

# Check sync operation status
kubectl get application nginx -n argocd -o yaml

Optimization:

  • Use sync waves (annotations) for ordering
  • Split into multiple apps
  • Pre-pull images

Cleanup

Remove Applications (Keeps Cluster Resources)

In ArgoCD UI:

  1. Click on application
  2. Click DELETE
  3. Uncheck “Cascade”
  4. Click OK

This removes app from ArgoCD but keeps resources in cluster.

Delete Applications (Removes Cluster Resources)

In ArgoCD UI:

  1. Click on application
  2. Click DELETE
  3. Check “Cascade” βœ…
  4. Click OK

This removes app from ArgoCD AND deletes all resources from cluster.

Or via kubectl:

# Delete with cascade (removes cluster resources)
kubectl delete application nginx -n argocd

# Delete without cascade (keeps cluster resources)
kubectl delete application nginx -n argocd --cascade=false

Uninstall ArgoCD

# Delete ArgoCD
kubectl delete -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Delete namespace
kubectl delete namespace argocd

Understanding GitOps Benefits

Before GitOps (Manual kubectl)

Challenges:

  • ❌ No history of what was deployed
  • ❌ Hard to reproduce deployments
  • ❌ Manual process, error-prone
  • ❌ Need cluster credentials
  • ❌ No audit trail
  • ❌ Difficult rollbacks

After GitOps (ArgoCD)

Benefits:

  • βœ… Git history = deployment history
  • βœ… Reproducible deployments
  • βœ… Automated process
  • βœ… No cluster credentials for developers
  • βœ… Full audit trail (who changed what, when)
  • βœ… Easy rollbacks (git revert)
  • βœ… Drift detection and self-healing
  • βœ… Multi-cluster support

Real-World Workflow

Development:

1. Developer commits code
2. CI builds container image
3. CI updates manifest in Git (new image tag)
4. ArgoCD detects change
5. ArgoCD deploys to dev cluster

Promotion to Production:

1. Merge dev branch to main
2. ArgoCD detects change in main
3. ArgoCD deploys to prod cluster

Rollback:

1. Issue detected in production
2. Git revert the problematic commit
3. Push to main
4. ArgoCD syncs previous version
5. Application restored

Summary

What you accomplished:

  • βœ… Installed ArgoCD on EKS cluster
  • βœ… Accessed ArgoCD Web UI
  • βœ… Created Applications from Git repository
  • βœ… Deployed nginx and Todo App using GitOps
  • βœ… Enabled auto-sync and self-healing
  • βœ… Tested GitOps workflow (commit β†’ auto-deploy)
  • βœ… Monitored application health and sync status
  • βœ… Performed GitOps rollback

Key concepts learned:

  1. GitOps principles - Git as single source of truth
  2. Declarative deployments - Desired state in Git
  3. Continuous sync - ArgoCD keeps cluster in sync with Git
  4. Self-healing - Automatic revert of manual changes
  5. Audit trail - Git history tracks all changes
  6. Easy rollbacks - Git revert = instant rollback

Architecture you built:

Developer β†’ Git Push β†’ GitHub Repository
                            ↓
                    (ArgoCD polls)
                            ↓
                    ArgoCD Application
                            ↓
                    (Auto/Manual Sync)
                            ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ AWS EKS Cluster                         β”‚
β”‚  β”œβ”€β”€ Nginx (nginx/)                     β”‚
β”‚  └── Todo App (ToDoApp-K8S-Manifests/)  β”‚
β”‚      β”œβ”€β”€ MongoDB StatefulSet            β”‚
β”‚      β”œβ”€β”€ Todo App Deployment            β”‚
β”‚      └── Mongo Express                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Next Steps

Ready for more?

  1. Multiple environments - Create dev, staging, prod apps
  2. Application Sets - Manage multiple apps from one definition
  3. Helm charts - Use ArgoCD with Helm
  4. Sync waves - Control resource creation order
  5. SSO integration - Use corporate SSO (Okta, Google, etc.)
  6. Notifications - Slack/email alerts for sync events
  7. Progressive delivery - Canary and blue-green deployments

Congratulations! πŸŽ‰

You now know how to:

  • Implement GitOps with ArgoCD
  • Manage Kubernetes applications declaratively
  • Automate deployments from Git
  • Monitor and troubleshoot applications
  • Rollback using Git history
  • Build production-ready CD pipelines

GitOps is the modern way to manage Kubernetes deployments!


Happy GitOps! πŸš€