ArgoCD: GitOps for Kubernetes - Complete Guide 2026

ArgoCD has become the de facto standard for implementing GitOps on Kubernetes. With over 15,000 stars on GitHub and adoption by companies like Intuit, Red Hat, and Tesla, ArgoCD represents the most mature solution for declarative continuous deployment. In this guide, we'll see how to install, configure, and use ArgoCD to automate deployments of your Kubernetes applications.

What is GitOps

Before diving into ArgoCD, let's understand the GitOps paradigm.

GitOps Principles

  1. Declarative: The entire system is described declaratively
  2. Versioned: The desired state is versioned in Git
  3. Automatic: Approved changes are applied automatically
  4. Continuous Reconciliation: The system automatically corrects drift

GitOps vs Traditional CI/CD

Traditional CI/CD (Push-based):

Developer -> Git -> CI Pipeline -> kubectl apply -> Cluster

Problems:

  • CI has access to the cluster (security concern)
  • No complete audit trail
  • Difficult rollback

GitOps (Pull-based):

Developer -> Git <- ArgoCD (in cluster) -> Reconciliation

Advantages:

  • The cluster "pulls" configurations
  • Git is the single source of truth
  • Complete audit trail
  • Rollback = git revert

What is ArgoCD

ArgoCD is a Kubernetes controller that:

  • Monitors Git repositories
  • Compares desired state (Git) vs actual state (cluster)
  • Automatically synchronizes differences
  • Provides UI and CLI for management

Architecture

ArgoCD Architecture - Kubernetes Cluster with ArgoCD Namespace, Application Controller and Git Repository
ArgoCD Architecture: the Kubernetes cluster "pulls" configurations from the Git repository

Main Components

ComponentFunction
API ServerExposes REST and gRPC API, serves the UI
Repository ServerClones and caches Git repositories
Application ControllerMonitors and synchronizes applications
RedisCache for application state
DexIdentity provider for SSO (optional)

Installation

Prerequisites

  • Kubernetes cluster (1.25+)
  • kubectl configured
  • Helm 3 (optional but recommended)

Method 1: YAML Manifest (Simple)

# Create namespace
kubectl create namespace argocd

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

# Verify pods
kubectl get pods -n argocd

Method 2: Helm (Recommended for production)

# Add Helm repo
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

# Install with custom values
helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --values values.yaml

values.yaml example:

server:
  replicas: 2
  ingress:
    enabled: true
    ingressClassName: nginx
    hosts:
      - argocd.example.com
    tls:
      - secretName: argocd-tls
        hosts:
          - argocd.example.com

controller:
  replicas: 1
  resources:
    requests:
      cpu: 250m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 512Mi

repoServer:
  replicas: 2

redis:
  enabled: true

configs:
  params:
    server.insecure: false
  cm:
    url: https://argocd.example.com

Initial Access

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

# Port forward for local access
kubectl port-forward svc/argocd-server -n argocd 8080:443

# Access https://localhost:8080
# Username: admin
# Password: (the one obtained above)

Install CLI

# Linux
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd
sudo mv argocd /usr/local/bin/

# macOS
brew install argocd

# CLI Login
argocd login localhost:8080

Fundamental Concepts

Application

An Application in ArgoCD defines:

  • Source: where manifests are located (Git repo)
  • Destination: where to deploy (cluster + namespace)
  • Sync policy: how to synchronize
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-app.git
    targetRevision: main
    path: kubernetes/
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Project

An AppProject groups applications with common policies:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  description: Production applications
  sourceRepos:
    - https://github.com/myorg/*
  destinations:
    - namespace: '*'
      server: https://kubernetes.default.svc
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  roles:
    - name: developer
      policies:
        - p, proj:production:developer, applications, get, production/*, allow
        - p, proj:production:developer, applications, sync, production/*, allow

Repository

Register private Git repositories:

# Via CLI
argocd repo add https://github.com/myorg/private-repo.git \
  --username git \
  --password $GITHUB_TOKEN

Sync Policies and Strategies

Auto Sync

syncPolicy:
  automated:
    prune: true       # Delete resources not in Git
    selfHeal: true    # Correct manual changes
    allowEmpty: false # Don't sync if repo is empty

Sync Options

syncOptions:
  - Validate=true              # Validate manifests before sync
  - CreateNamespace=true       # Create namespace if it doesn't exist
  - PrunePropagationPolicy=foreground  # Wait for deletion
  - PruneLast=true             # Prune after apply
  - ApplyOutOfSyncOnly=true    # Apply only OutOfSync resources
  - ServerSideApply=true       # Use server-side apply

Sync Waves and Hooks

Control deployment order:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: database
  annotations:
    argocd.argoproj.io/sync-wave: "-1"  # First
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  annotations:
    argocd.argoproj.io/sync-wave: "0"   # After database
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  annotations:
    argocd.argoproj.io/sync-wave: "1"   # After backend

Resource Hooks

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: myorg/migrations:latest
          command: ["./migrate.sh"]
      restartPolicy: Never

Hook types:

  • PreSync: Before synchronization
  • Sync: During synchronization
  • PostSync: After synchronization
  • SyncFail: If sync fails

Helm Integration

ArgoCD natively supports Helm charts:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx-ingress
spec:
  source:
    repoURL: https://kubernetes.github.io/ingress-nginx
    chart: ingress-nginx
    targetRevision: 4.8.3
    helm:
      releaseName: nginx-ingress
      values: |
        controller:
          replicaCount: 2
          service:
            type: LoadBalancer
          metrics:
            enabled: true
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx

App of Apps Pattern

Manage all applications with a single Application:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/argocd-apps.git
    path: apps
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Notifications

Configure notifications for events:

# ConfigMap for notifications
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
data:
  service.slack: |
    token: $slack-token
  template.app-sync-succeeded: |
    message: |
      Application {{.app.metadata.name}} sync succeeded.
      Revision: {{.app.status.sync.revision}}
  trigger.on-sync-succeeded: |
    - when: app.status.sync.status == 'Synced'
      send: [app-sync-succeeded]

Production Best Practices

Security

# Granular RBAC
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.csv: |
    p, role:developer, applications, get, */*, allow
    p, role:developer, applications, sync, */*, allow
    p, role:admin, applications, *, */*, allow
    g, developers, role:developer
    g, admins, role:admin
  policy.default: role:readonly

High Availability

# values.yaml for HA
controller:
  replicas: 2
  env:
    - name: ARGOCD_CONTROLLER_REPLICAS
      value: "2"

server:
  replicas: 3
  autoscaling:
    enabled: true
    minReplicas: 3
    maxReplicas: 5

repoServer:
  replicas: 3
  autoscaling:
    enabled: true

redis-ha:
  enabled: true

Troubleshooting

Useful Commands

# Application status
argocd app get my-app

# Sync logs
argocd app logs my-app

# Force sync
argocd app sync my-app --force

# Diff
argocd app diff my-app

# History
argocd app history my-app

# Rollback
argocd app rollback my-app <revision>

Common Problems

App stuck in "Progressing":

# Check resources
kubectl get all -n my-app-namespace

# Events
kubectl get events -n my-app-namespace --sort-by='.lastTimestamp'

Sync failed:

# Error details
argocd app get my-app --show-operation

# Remove orphan resources
argocd app sync my-app --prune

Conclusions

ArgoCD is the essential tool for implementing GitOps on Kubernetes. Key advantages:

  • Git as source of truth: complete audit trail
  • Automatic reconciliation: the cluster self-corrects
  • Intuitive UI: immediate visibility on state
  • Mature ecosystem: integrations, plugins, active community

Implementation checklist:

  • Install ArgoCD with Helm
  • Configure SSO/RBAC
  • Structure GitOps repository
  • Implement App of Apps pattern
  • Configure notifications
  • Test rollback
  • Document team workflow

Resources