Skip to content

ArgoCD — GitOps Deployment & Application Management Guide

DodaTech Updated 2026-06-24 5 min read

In this tutorial, you'll learn about ArgoCD. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

ArgoCD is a declarative, GitOps-driven continuous delivery tool for Kubernetes that automatically synchronizes cluster state with the desired state defined in a Git Repository.

What You'll Learn

Why It Matters

Manual kubectl commands and CI-push deployments create configuration drift — what's running in the cluster differs from what's in Git. ArgoCD enforces that the cluster state always matches the Git Repository, providing automated sync, drift detection, instant rollback, and a visual UI for application state. DodaTech manages 80+ Kubernetes applications across 5 clusters using ArgoCD, eliminating configuration drift and reducing deployment Incident Response time by 70%.

Real-World Use

DodaZIP's Kubernetes stack — 30 Microservices, 15 databases, 10 middleware components — is defined entirely in a dodatech-infra Git Repository. ArgoCD watches this Repository and automatically applies any change to the production cluster. A misconfigured deployment is rolled back by reverting a Git commit.

flowchart TD
    A[Git Repository] --> B[ArgoCD Server]
    B --> C[Application Controller]
    C --> D[Kubernetes Cluster]
    D --> E[Deployments]
    D --> F[Services]
    D --> G[ConfigMaps]
    D --> H[Ingresses]
    E --> I{Drift Detected?}
    I -->|Yes| J[Auto-Sync / Prune]
    I -->|No| K[Healthy Status]
    J --> D
    style A fill:#2F73DA,color:#fff
    style B fill:#EF7B4D,color:#fff
â„šī¸ Info

Prerequisites: A running Kubernetes cluster. kubectl and argocd CLI installed. Basic understanding of GitOps principles.

Installing ArgoCD

# Create namespace
kubectl create namespace argocd

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

# Expected output:
# namespace/argocd created
# customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
# customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
# serviceaccount/argocd-redis created
# ... (25+ resources created)

# Verify pods
kubectl get pods -n argocd

# NAME                                                READY   STATUS
# argocd-application-controller-0                     1/1     Running
# argocd-applicationset-controller-6c9b5f8d5f-4hj2k   1/1     Running
# argocd-dex-server-7f5c6b74b6-8zkw9                  1/1     Running
# argocd-notifications-controller-5b8c9d5b99-v9xfc    1/1     Running
# argocd-redis-7f8f458bc6-vshmb                       1/1     Running
# argocd-repo-server-6f7b5c8d4c-j48h2                 1/1     Running
# argocd-server-b64fcfccc-c9pz6                       1/1     Running
# Access the UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

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

# Login via CLI
argocd login localhost:8080 --username admin --password <initial-password> --insecure

# Change password
argocd account update-password

Defining an Application

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: user-service
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/dodatech/user-service.git
    targetRevision: main
    path: k8s/overlays/production
    helm:
      valueFiles:
        - values-prod.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  revisionHistoryLimit: 10
# Create the application
kubectl apply -f application.yaml

# Or use the CLI
argocd app create user-service \
  --repo https://github.com/dodatech/user-service.git \
  --path k8s/overlays/production \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace production \
  --sync-policy automated \
  --auto-prune \
  --self-heal

Sync Management

# Manual sync
argocd app sync user-service

# Expected output:
# TIMESTAMP                  GROUP        KIND            NAMESPACE    NAME
# 2026-06-24T10:00:00Z       apps         Deployment      production   user-service    Synced
# 2026-06-24T10:00:01Z       v1           Service         production   user-service    Synced
# 2026-06-24T10:00:02Z       networking   Ingress         production   user-service    Synced

# Get sync status
argocd app get user-service

# Expected output:
# Name:               user-service
# Project:            default
# Server:             https://kubernetes.default.svc
# Namespace:          production
# URL:                https://argocd.dodatech.com/applications/user-service
# Repo:               https://github.com/dodatech/user-service.git
# Target:             main
# Path:               k8s/overlays/production
# Sync Status:        Synced
# Health Status:      Healthy

# Rollback to previous revision
argocd app rollback user-service --prune

AppProject Configuration

# project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: doda-backend
  namespace: argocd
spec:
  description: DodaTech backend services
  sourceRepos:
    - 'https://github.com/dodatech/*'
  destinations:
    - namespace: production
      server: https://kubernetes.default.svc
    - namespace: staging
      server: https://kubernetes.default.svc
    - namespace: 'team-*'
      server: 'https://*.k8s.dodatech.com'
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
    - group: 'rbac.authorization.k8s.io'
      kind: ClusterRole
  namespaceResourceBlacklist:
    - group: ''
      kind: Secret
  roles:
    - name: read-only
      description: Read-only access to applications
      policies:
        - p, proj:doda-backend:read-only, applications, get, doda-backend/*, allow
      groups:
        - dodatech-developers
    - name: ci-deployer
      description: Automated deployment from CI
      policies:
        - p, proj:doda-backend:ci-deployer, applications, sync, doda-backend/*, allow
      jwtTokens:
        - iat: 1719200000

Multi-Cluster Management

# Add a remote cluster
argocd cluster add production-context \
  --name prod-us-east \
  --label environment=production \
  --label region=us-east-1

# Expected output:
# Cluster 'prod-us-east' added

# List clusters
argocd cluster list

# SERVER                          NAME            STATUS
# https://kubernetes.default.svc  in-cluster      Successful
# https://api.prod-us-east.k8s.io prod-us-east    Successful
# https://api.staging-eu.k8s.io   staging-eu      Successful

# Deploy to a specific cluster
argocd app create multi-cluster-app \
  --repo https://github.com/dodatech/infra.git \
  --path k8s/app \
  --dest-server https://api.prod-us-east.k8s.io \
  --dest-namespace production

ApplicationSet for Multi-Environment

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: user-service-envs
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - environment: dev
            cluster: https://api.dev.k8s.dodatech.com
            namespace: user-service-dev
          - environment: staging
            cluster: https://api.staging.k8s.dodatech.com
            namespace: user-service-staging
          - environment: production
            cluster: https://api.prod.k8s.dodatech.com
            namespace: user-service-production
  template:
    metadata:
      name: 'user-service-{{environment}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/dodatech/user-service.git
        targetRevision: main
        path: 'k8s/overlays/{{environment}}'
      destination:
        server: '{{cluster}}'
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Common Configuration Mistakes

  1. Not enabling auto-prune: Without prune: true, ArgoCD never removes resources that were deleted from Git. Stale resources accumulate and drift goes undetected.

  2. Hardcoding cluster URLs in Application manifests: When clusters rotate, every Application must be updated. Use ApplicationSet generators or cluster labels for dynamic targeting.

  3. Missing syncOptions for namespace creation: Without CreateNamespace=true, the sync fails if the target namespace doesn't exist. Always include this for new environments.

  4. Ignoring sync waves for dependency ordering: Resources without sync waves deploy in arbitrary order. Use argocd.argoproj.io/sync-wave: "-5" annotations to order dependent resources (e.g., ConfigMap before Deployment).

  5. Using default project without restricting sources: The default project allows any source Repository. Create AppProjects with locked-down sourceRepos and destinations.

Practice Questions

  1. What is the difference between sync and health status in ArgoCD? Answer: Sync status indicates whether the cluster matches Git (Synced/OutOfSync). Health status indicates whether the application is running correctly (Healthy/Degraded/Progressing).

  2. How does self-heal differ from auto-sync? Answer: Auto-sync refreshes from Git on schedule or Webhook. Self-heal automatically corrects any manual changes made to the cluster, reverting them to the Git-defined state.

  3. What is an ApplicationSet generator? Answer: ApplicationSet generators dynamically create Applications based on parameters — from lists, Git directories, cluster labels, pull requests, or SCM providers.

  4. How do you handle secrets in a GitOps workflow? Answer: Use SealedSecrets (encrypted secrets stored in Git), External Secrets Operator (syncing from AWS Secrets Manager/Vault), or SOPS-encrypted files with ArgoCD's built-in decryption.

Challenge

Deploy a complete application stack with ArgoCD: create an AppProject for a team with restricted source repos and destination namespaces, define Applications for a frontend and backend service across dev, staging, and production using an ApplicationSet, configure automated sync with self-heal and prune, set up sync waves so ConfigMaps deploy before Deployments, and add a multi-cluster cluster destination.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro