ArgoCD — GitOps Continuous Delivery for Kubernetes

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Introduction

Why This Matters

ArgoCD is the de-facto GitOps tool for Kubernetes. Rather than pushing deployments from CI pipelines, ArgoCD continuously reconciles your cluster state against Git — making Git the single source of truth for what runs in production. Any drift is detected and corrected automatically, audit logs are implicit (it is just Git history), and rollback is a git revert away.

Installation

# Create namespace and install
kubectl create namespace argocd
kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
 
# Wait for all pods to be ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=120s
 
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d && echo
 
# Port-forward the UI
kubectl port-forward -n argocd svc/argocd-server 8080:443
 
# Install CLI (macOS)
brew install argocd
 
# Login via CLI
argocd login localhost:8080 --username admin --insecure

Deploying an Application

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/my-org/my-app
    path: k8s/overlays/production
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true        # delete removed resources
      selfHeal: true     # fix manual cluster changes
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
# Apply the application manifest
kubectl apply -f application.yaml
 
# Or create via CLI
argocd app create my-app \
  --repo https://github.com/my-org/my-app \
  --path k8s/overlays/production \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace production \
  --sync-policy automated \
  --auto-prune \
  --self-heal

Sync Strategies and Policies

syncPolicy:
  # Automated sync — ArgoCD reconciles on every Git push
  automated:
    prune: true       # remove K8s resources not in Git
    selfHeal: true    # revert manual kubectl changes
    allowEmpty: false # do not sync if source produces zero resources
 
  # Retry failed syncs with backoff
  retry:
    limit: 5
    backoff:
      duration: 5s
      factor: 2
      maxDuration: 3m
 
  syncOptions:
    - CreateNamespace=true
    - ServerSideApply=true
    - RespectIgnoreDifferences=true

Projects and RBAC

# AppProject scopes which repos and clusters an app can use
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-backend
  namespace: argocd
spec:
  description: Backend team project
  sourceRepos:
    - 'https://github.com/my-org/*'
  destinations:
    - namespace: 'backend-*'
      server: https://kubernetes.default.svc
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota
  roles:
    - name: developer
      description: Read-only access
      policies:
        - p, proj:team-backend:developer, applications, get, team-backend/*, allow
        - p, proj:team-backend:developer, applications, sync, team-backend/*, allow

Multi-Cluster Management

# Register external cluster
argocd cluster add my-eks-cluster --name production-eks
 
# List registered clusters
argocd cluster list
 
# Deploy to external cluster
argocd app create remote-app \
  --dest-server https://eks-cluster-api-server.amazonaws.com \
  --dest-namespace production

Rollback

# View app history
argocd app history my-app
 
# Rollback to previous revision
argocd app rollback my-app --revision 42
 
# Or revert in Git (preferred GitOps approach)
git revert HEAD
git push origin main
# ArgoCD picks up the change automatically

Common Mistakes

  • Enabling prune: true without testing — it will delete resources not tracked in Git
  • Not setting finalizers on Application resources, causing orphaned cluster resources on deletion
  • Using automated sync for stateful applications without carefully managing data migrations
  • Storing sensitive values (passwords, API keys) directly in Git manifests — use Sealed Secrets or External Secrets Operator
  • Not configuring AppProjects — leaving all teams deploying to all namespaces

Best Practices

  • Separate your application code repo from your GitOps config repo (app-of-apps pattern)
  • Enable selfHeal in production to ensure cluster state never drifts from Git
  • Use Kustomize overlays or Helm chart values per environment within the same repo
  • Configure webhook notifications so ArgoCD syncs immediately on Git push rather than polling
  • Use ArgoCD Image Updater to automate image tag updates without manual Git commits
  • Protect the argocd namespace with NetworkPolicies and strict RBAC

Key Takeaways

  • ArgoCD implements GitOps by continuously syncing Kubernetes cluster state to match a Git repository
  • prune: true removes cluster resources that have been deleted from Git — powerful but requires care
  • selfHeal: true automatically reverts any manual kubectl changes that differ from Git state
  • AppProjects provide RBAC boundaries controlling which teams can deploy to which namespaces and clusters
  • Rollbacks in GitOps are Git reverts — ArgoCD detects the change and reconciles the cluster
  • The App-of-Apps pattern uses one ArgoCD Application to manage many child Applications declaratively
  • ArgoCD supports Helm, Kustomize, Jsonnet, and plain YAML manifests as source formats
  • Multi-cluster management is native — register external clusters and deploy across environments from one control plane

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading