Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

ArgoCD

GitOps continuous delivery tool for Kubernetes that declaratively manages applications from Git repositories.

ArgoCD

GitOps continuous delivery tool for Kubernetes that declaratively manages applications from Git repositories.

Overview

ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It automates the deployment of applications by continuously monitoring Git repositories and synchronising the desired application state (defined in Git) with the live state in the cluster. ArgoCD provides a visual dashboard, CLI, and API for managing application deployments, rollbacks, and health monitoring across multiple clusters.

GitOps WorkflowOut of SyncIn SyncDeveloperGit RepositoryArgoCD ServerCompareSync to ClusterMonitorKubernetes ClusterGitOps WorkflowOut of SyncIn SyncDeveloperGit RepositoryArgoCD ServerCompareSync to ClusterMonitorKubernetes Cluster

Installation and Setup

ArgoCD can be installed in various configurations depending on your requirements for high availability and cluster size.

Key Concepts

  • Namespace: ArgoCD typically runs in its own namespace (argocd)
  • Installation Modes: Non-HA (single replica) or HA (multiple replicas with Redis)
  • Core Components: API Server, Repository Server, Application Controller, Dex (SSO), Redis (caching)
ArgoCD ArchitectureAPI ServerRepository ServerApplicationControllerGit ReposKubernetes APIRedisWeb UICLIArgoCD ArchitectureAPI ServerRepository ServerApplicationControllerGit ReposKubernetes APIRedisWeb UICLI

Common Commands/Patterns

# Install ArgoCD (non-HA)
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Install ArgoCD (HA)
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/ha/install.yaml

# Install using Helm
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace

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

# Access ArgoCD UI (port-forward)
kubectl port-forward svc/argocd-server -n argocd 8080:443

# Install ArgoCD 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

# Login to ArgoCD
argocd login localhost:8080

# Change admin password
argocd account update-password

Examples

Expose ArgoCD with Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  rules:
    - host: argocd.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: argocd-server
                port:
                  name: https
  tls:
    - hosts:
        - argocd.example.com
      secretName: argocd-server-tls

Configure ArgoCD with ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  # Repository credentials
  repositories: |
    - url: https://github.com/myorg/myrepo
      passwordSecret:
        name: repo-creds
        key: password
      usernameSecret:
        name: repo-creds
        key: username

  # OIDC configuration
  oidc.config: |
    name: Okta
    issuer: https://myorg.okta.com
    clientID: xxxxxxxxx
    clientSecret: $oidc.okta.clientSecret
    requestedScopes: ["openid", "profile", "email", "groups"]

  # Resource tracking method
  application.resourceTrackingMethod: annotation

Concepts: Applications, Projects, and Repositories

Understanding ArgoCD's core concepts is essential for effective GitOps implementation.

Key Concepts

  • Application: A group of Kubernetes resources defined by a manifest in a Git repository
  • Project: A logical grouping of applications that defines source repos, destination clusters, and permissions
  • Repository: A Git repository containing application manifests (YAML, Helm, Kustomize)
  • Destination: The target cluster and namespace where resources are deployed
ArgoCD Resource HierarchyAppProjectApplication 1Application 2Application 3Git RepoDestination ClusterAnother ClusterHelm RepoArgoCD Resource HierarchyAppProjectApplication 1Application 2Application 3Git RepoDestination ClusterAnother ClusterHelm Repo

AppProject Configuration

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  # Project description
  description: Production applications

  # Allowed source repositories
  sourceRepos:
    - 'https://github.com/myorg/*'
    - 'https://charts.helm.sh/stable'

  # Allowed destination clusters and namespaces
  destinations:
    - namespace: 'prod-*'
      server: https://kubernetes.default.svc
    - namespace: '*'
      server: https://prod-cluster.example.com

  # Allowed cluster resources
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
    - group: 'rbac.authorization.k8s.io'
      kind: ClusterRole
    - group: 'rbac.authorization.k8s.io'
      kind: ClusterRoleBinding

  # Denied namespaced resources
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota
    - group: ''
      kind: LimitRange

  # Roles for RBAC
  roles:
    - name: developer
      description: Developer access
      policies:
        - p, proj:production:developer, applications, get, production/*, allow
        - p, proj:production:developer, applications, sync, production/*, allow
      groups:
        - developers

    - name: admin
      description: Admin access
      policies:
        - p, proj:production:admin, applications, *, production/*, allow
      groups:
        - platform-team

  # Sync windows (maintenance windows)
  syncWindows:
    - kind: allow
      schedule: '0 22 * * *'
      duration: 1h
      applications:
        - '*'
    - kind: deny
      schedule: '0 9 * * 1-5'
      duration: 8h
      applications:
        - 'prod-*'

Repository Management

# Add a Git repository
argocd repo add https://github.com/myorg/myrepo \
  --username myuser \
  --password mytoken

# Add a private repository with SSH
argocd repo add git@github.com:myorg/private-repo \
  --ssh-private-key-path ~/.ssh/id_rsa

# Add a Helm repository
argocd repo add https://charts.helm.sh/stable \
  --type helm \
  --name stable

# Add OCI Helm repository
argocd repo add registry.example.com/charts \
  --type helm \
  --name myregistry \
  --enable-oci

# List repositories
argocd repo list

# Remove a repository
argocd repo rm https://github.com/myorg/myrepo

Examples

Repository credentials secret:

apiVersion: v1
kind: Secret
metadata:
  name: private-repo-creds
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  type: git
  url: https://github.com/myorg/private-repo
  username: git
  password: ghp_xxxxxxxxxxxx

SSH key repository secret:

apiVersion: v1
kind: Secret
metadata:
  name: private-repo-ssh
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  type: git
  url: git@github.com:myorg/private-repo.git
  sshPrivateKey: |
    -----BEGIN OPENSSH PRIVATE KEY-----
    ...
    -----END OPENSSH PRIVATE KEY-----

Application CRD and Manifests

The Application CRD is the primary resource for defining how applications are deployed.

Key Concepts

  • Source: Where to get the application manifests (Git repo, Helm chart, or path)
  • Destination: Where to deploy the application (cluster and namespace)
  • Sync Policy: How and when to synchronise the application
  • Health Status: Whether the application resources are healthy

Application Manifest Structure

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
  # Finalizer ensures resources are cleaned up
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  # Project the application belongs to
  project: default

  # Source of the application manifests
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: HEAD
    path: kubernetes/overlays/production

  # Destination cluster and namespace
  destination:
    server: https://kubernetes.default.svc
    namespace: production

  # Sync policy configuration
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

  # Ignore differences in specific fields
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas
    - group: ''
      kind: ConfigMap
      name: my-config
      jsonPointers:
        - /data/key

Source Types

Directory of YAML files:

source:
  repoURL: https://github.com/myorg/myapp
  targetRevision: main
  path: manifests/
  directory:
    recurse: true
    exclude: '{config.json,*.txt}'
    include: '*.yaml'

Helm chart from Git:

source:
  repoURL: https://github.com/myorg/helm-charts
  targetRevision: main
  path: charts/myapp
  helm:
    releaseName: myapp
    valueFiles:
      - values.yaml
      - values-production.yaml
    values: |
      replicaCount: 3
      image:
        tag: v1.2.3
    parameters:
      - name: service.type
        value: LoadBalancer

Helm chart from repository:

source:
  repoURL: https://charts.bitnami.com/bitnami
  chart: nginx
  targetRevision: 13.2.10
  helm:
    releaseName: my-nginx
    values: |
      replicaCount: 2

Kustomize:

source:
  repoURL: https://github.com/myorg/myapp
  targetRevision: main
  path: overlays/production
  kustomize:
    namePrefix: prod-
    nameSuffix: -v1
    images:
      - myapp=myregistry/myapp:v1.2.3
    commonLabels:
      environment: production
    commonAnnotations:
      team: platform

Examples

Multi-source application (Helm + values from Git):

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-multisource
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: https://charts.bitnami.com/bitnami
      chart: nginx
      targetRevision: 13.2.10
      helm:
        valueFiles:
          - $values/nginx/production-values.yaml
    - repoURL: https://github.com/myorg/config
      targetRevision: main
      ref: values
  destination:
    server: https://kubernetes.default.svc
    namespace: production

Application with resource hooks:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-with-hooks
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: manifests/
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
---
# Hook resource (in your Git repo)
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: myapp:latest
          command: ["./migrate.sh"]
      restartPolicy: Never

Git Repository Setup and Sync

Configuring Git repositories and understanding the synchronisation process.

Key Concepts

KubernetesApp ControllerRepo ServerGit RepositoryKubernetesApp ControllerRepo ServerGit RepositoryRequest manifestsClone/FetchReturn filesRender manifestsReturn manifestsGet live stateReturn resourcesCompare desired vs liveApply changesKubernetesApp ControllerRepo ServerGit RepositoryKubernetesApp ControllerRepo ServerGit RepositoryRequest manifestsClone/FetchReturn filesRender manifestsReturn manifestsGet live stateReturn resourcesCompare desired vs liveApply changes
  • Target Revision: The Git branch, tag, or commit to track
  • Path: The directory within the repository containing manifests
  • Refresh: Fetching the latest state from Git
  • Hard Refresh: Invalidating the cache and re-fetching

Common Commands/Patterns

# Create an application from CLI
argocd app create myapp \
  --repo https://github.com/myorg/myapp \
  --path kubernetes/production \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace production \
  --revision main

# Create from file
argocd app create -f application.yaml

# List applications
argocd app list

# Get application details
argocd app get myapp

# Sync an application
argocd app sync myapp

# Sync with specific revision
argocd app sync myapp --revision v1.2.3

# Sync with prune
argocd app sync myapp --prune

# Sync specific resources only
argocd app sync myapp --resource :Service:myapp-service
argocd app sync myapp --resource apps:Deployment:myapp

# Preview sync (dry run)
argocd app sync myapp --dry-run

# Force sync (replace resources)
argocd app sync myapp --force

# Refresh application
argocd app get myapp --refresh

# Hard refresh (invalidate cache)
argocd app get myapp --hard-refresh

# Delete application
argocd app delete myapp

# Delete application and resources
argocd app delete myapp --cascade

Webhook Configuration

# GitHub webhook secret
apiVersion: v1
kind: Secret
metadata:
  name: github-webhook-secret
  namespace: argocd
stringData:
  webhook.github.secret: mysecrettoken
---
# ArgoCD ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  webhook.github.secret: mysecrettoken

Examples

Application tracking a specific tag:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-release
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: v1.2.3  # Git tag
    path: manifests/
  destination:
    server: https://kubernetes.default.svc
    namespace: production

Application tracking a specific commit:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-pinned
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: a1b2c3d4e5f6  # Commit SHA
    path: manifests/
  destination:
    server: https://kubernetes.default.svc
    namespace: staging

Sync Policies

Sync policies control how and when ArgoCD synchronises applications with the cluster.

Key Concepts

  • Manual Sync: User-initiated synchronisation
  • Automated Sync: ArgoCD automatically syncs when changes are detected
  • Self-Heal: Automatically fix drift from the desired state
  • Prune: Remove resources that are no longer in Git
  • Sync Windows: Time-based restrictions on when syncs can occur
New commit detectedAuto/Manual syncSuccessErrorRetryGit changeManual change (drift)Self-healOutOfSyncSyncingSyncedSyncFailedNew commit detectedAuto/Manual syncSuccessErrorRetryGit changeManual change (drift)Self-healOutOfSyncSyncingSyncedSyncFailed

Sync Policy Options

syncPolicy:
  # Automated sync configuration
  automated:
    # Prune resources that no longer exist in Git
    prune: true
    # Automatically sync when drift is detected
    selfHeal: true
    # Only sync if the app is OutOfSync
    allowEmpty: false

  # Sync options
  syncOptions:
    # Create namespace if it doesn't exist
    - CreateNamespace=true
    # Use server-side apply
    - ServerSideApply=true
    # Validate resources before applying
    - Validate=true
    # Apply out of sync only
    - ApplyOutOfSyncOnly=true
    # Prune last (after other resources)
    - PruneLast=true
    # Respect ignore differences
    - RespectIgnoreDifferences=true
    # Fail sync on shared resource
    - FailOnSharedResource=true
    # Replace resources instead of applying
    - Replace=true
    # Prune propagation policy
    - PrunePropagationPolicy=foreground

  # Retry configuration
  retry:
    limit: 5
    backoff:
      duration: 5s
      factor: 2
      maxDuration: 3m

  # Managed namespace metadata
  managedNamespaceMetadata:
    labels:
      env: production
    annotations:
      team: platform

Resource Hooks

# PreSync hook - runs before sync
apiVersion: batch/v1
kind: Job
metadata:
  name: pre-sync-job
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: pre-sync
          image: alpine
          command: ["/bin/sh", "-c", "echo Pre-sync task"]
      restartPolicy: Never

---
# Sync hook - runs during sync
apiVersion: batch/v1
kind: Job
metadata:
  name: sync-job
  annotations:
    argocd.argoproj.io/hook: Sync
    argocd.argoproj.io/sync-wave: "0"
spec:
  template:
    spec:
      containers:
        - name: sync
          image: alpine
          command: ["/bin/sh", "-c", "echo Sync task"]
      restartPolicy: Never

---
# PostSync hook - runs after successful sync
apiVersion: batch/v1
kind: Job
metadata:
  name: post-sync-job
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
  template:
    spec:
      containers:
        - name: post-sync
          image: alpine
          command: ["/bin/sh", "-c", "echo Post-sync task"]
      restartPolicy: Never

---
# SyncFail hook - runs when sync fails
apiVersion: batch/v1
kind: Job
metadata:
  name: sync-fail-job
  annotations:
    argocd.argoproj.io/hook: SyncFail
spec:
  template:
    spec:
      containers:
        - name: notify
          image: curlimages/curl
          command: ["curl", "-X", "POST", "https://hooks.slack.com/..."]
      restartPolicy: Never

Sync Waves

# Wave -1: Create namespace and RBAC first
apiVersion: v1
kind: Namespace
metadata:
  name: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "-1"

---
# Wave 0: Deploy ConfigMaps and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
  namespace: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "0"
data:
  config.yaml: |
    key: value

---
# Wave 1: Deploy main application
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "1"
spec:
  replicas: 3
  # ...

---
# Wave 2: Deploy dependent services
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-worker
  namespace: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "2"
spec:
  replicas: 2
  # ...

Examples

Conservative production sync policy:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    # Manual sync only - no automated
    syncOptions:
      - Validate=true
      - PruneLast=true
      - RespectIgnoreDifferences=true
    retry:
      limit: 3
      backoff:
        duration: 10s
        factor: 2
        maxDuration: 5m

Aggressive staging sync policy:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: staging-app
  namespace: argocd
spec:
  project: staging
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: develop
    path: overlays/staging
  destination:
    server: https://kubernetes.default.svc
    namespace: staging
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - ApplyOutOfSyncOnly=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Health Assessment

ArgoCD monitors the health of application resources to determine overall application health.

Key Concepts

  • Health Status: Healthy, Progressing, Degraded, Suspended, Missing, Unknown
  • Built-in Health Checks: Standard checks for Kubernetes resources
  • Custom Health Checks: User-defined health assessments using Lua scripts
Deployment startedAll conditions metTimeout/ErrorUpdate initiatedResource failureRecovery attemptIssue resolvedProgressingHealthyDegradedDeployment startedAll conditions metTimeout/ErrorUpdate initiatedResource failureRecovery attemptIssue resolvedProgressingHealthyDegraded

Health Status Types

Status Description
Healthy Resource is fully operational
Progressing Resource is in a transitional state (e.g., rolling update)
Degraded Resource is not healthy (e.g., pod crash, failed probe)
Suspended Resource is paused (e.g., suspended deployment)
Missing Resource is not present in the cluster
Unknown Health status cannot be determined

Custom Health Checks

# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  # Custom health check for CronJob
  resource.customizations.health.batch_CronJob: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.lastScheduleTime ~= nil then
        hs.status = "Healthy"
        hs.message = "CronJob is scheduled"
      else
        hs.status = "Progressing"
        hs.message = "Waiting for first schedule"
      end
    else
      hs.status = "Progressing"
      hs.message = "Waiting for status"
    end
    return hs

  # Custom health check for Certificate (cert-manager)
  resource.customizations.health.cert-manager.io_Certificate: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.conditions ~= nil then
        for i, condition in ipairs(obj.status.conditions) do
          if condition.type == "Ready" and condition.status == "False" then
            hs.status = "Degraded"
            hs.message = condition.message
            return hs
          end
          if condition.type == "Ready" and condition.status == "True" then
            hs.status = "Healthy"
            hs.message = condition.message
            return hs
          end
        end
      end
    end
    hs.status = "Progressing"
    hs.message = "Waiting for certificate"
    return hs

  # Custom health check for Rollout (Argo Rollouts)
  resource.customizations.health.argoproj.io_Rollout: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.phase == "Healthy" then
        hs.status = "Healthy"
        hs.message = obj.status.message
      elseif obj.status.phase == "Paused" then
        hs.status = "Suspended"
        hs.message = obj.status.message
      elseif obj.status.phase == "Degraded" then
        hs.status = "Degraded"
        hs.message = obj.status.message
      else
        hs.status = "Progressing"
        hs.message = obj.status.message
      end
    end
    return hs

Ignore Resource Updates

# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  # Ignore specific resource updates for health checks
  resource.customizations.ignoreDifferences.admissionregistration.k8s.io_MutatingWebhookConfiguration: |
    jqPathExpressions:
      - '.webhooks[]?.clientConfig.caBundle'

  resource.customizations.ignoreDifferences.apiextensions.k8s.io_CustomResourceDefinition: |
    jqPathExpressions:
      - '.spec.conversion.webhook.clientConfig.caBundle'

Examples

Check application health via CLI:

# Get application with health details
argocd app get myapp

# Get health status in JSON format
argocd app get myapp -o json | jq '.status.health'

# List only unhealthy applications
argocd app list --health-status Degraded

# Watch application health
argocd app get myapp --watch

# Get resource tree with health
argocd app resources myapp --tree

Diff and Sync Status

Understanding the differences between desired and live state.

Key Concepts

  • Sync Status: Synced, OutOfSync, Unknown
  • Diff: The differences between Git (desired) and cluster (live) state
  • Compare Options: How ArgoCD compares resources

Common Commands/Patterns

# View application diff
argocd app diff myapp

# Diff with local manifests
argocd app diff myapp --local ./manifests/

# Diff specific revision
argocd app diff myapp --revision v1.2.3

# Show diff in JSON
argocd app diff myapp --output json

# Get sync status
argocd app get myapp -o json | jq '.status.sync'

# List out of sync applications
argocd app list --sync-status OutOfSync

# Get detailed resource status
argocd app resources myapp

Ignore Differences

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: manifests/
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  ignoreDifferences:
    # Ignore replica count managed by HPA
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas

    # Ignore specific ConfigMap key
    - group: ""
      kind: ConfigMap
      name: myapp-config
      jsonPointers:
        - /data/dynamic-key

    # Ignore using JQ path expressions
    - group: apps
      kind: Deployment
      jqPathExpressions:
        - .spec.template.spec.containers[].resources

    # Ignore managed fields (server-side apply)
    - group: "*"
      kind: "*"
      managedFieldsManagers:
        - kube-controller-manager

Global Ignore Differences

# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  resource.compareoptions: |
    # Ignore aggregated cluster roles
    ignoreAggregatedRoles: true
    # Respect ignore differences in sync
    respectIgnoreDifferences: true

Examples

Application with extensive ignore differences:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  ignoreDifferences:
    # Ignore HPA-managed replicas
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas

    # Ignore auto-generated fields
    - group: ""
      kind: Service
      jsonPointers:
        - /spec/clusterIP
        - /spec/clusterIPs

    # Ignore webhook CA bundles
    - group: admissionregistration.k8s.io
      kind: MutatingWebhookConfiguration
      jqPathExpressions:
        - .webhooks[]?.clientConfig.caBundle

    # Ignore Kustomize build metadata
    - group: ""
      kind: ConfigMap
      jsonPointers:
        - /metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration

Multi-Cluster Management

Managing applications across multiple Kubernetes clusters.

Key Concepts

  • Cluster Registration: Adding external clusters to ArgoCD
  • Cluster Credentials: ServiceAccount tokens or kubeconfig
  • Destination Server: The API server URL of the target cluster
Development ClusterStaging ClusterProduction ClusterArgoCD Management ClusterArgoCD ServerApp ControllerKubernetes APIProduction AppsKubernetes APIStaging AppsKubernetes APIDev AppsDevelopment ClusterStaging ClusterProduction ClusterArgoCD Management ClusterArgoCD ServerApp ControllerKubernetes APIProduction AppsKubernetes APIStaging AppsKubernetes APIDev Apps

Common Commands/Patterns

# Add a cluster (uses current kubeconfig context)
argocd cluster add my-cluster-context

# Add cluster with specific name
argocd cluster add my-cluster-context --name production

# Add cluster with service account
argocd cluster add my-cluster-context \
  --name production \
  --service-account argocd-manager \
  --system-namespace kube-system

# List clusters
argocd cluster list

# Get cluster details
argocd cluster get https://production-cluster.example.com

# Rotate cluster credentials
argocd cluster rotate-auth https://production-cluster.example.com

# Remove a cluster
argocd cluster rm https://production-cluster.example.com

Cluster Secret Configuration

# External cluster configuration
apiVersion: v1
kind: Secret
metadata:
  name: production-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
stringData:
  name: production
  server: https://production-cluster.example.com
  config: |
    {
      "bearerToken": "eyJhbGciOiJSUzI1NiIs...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "LS0tLS1CRUdJTi..."
      }
    }
# Cluster with AWS IAM authentication
apiVersion: v1
kind: Secret
metadata:
  name: eks-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
stringData:
  name: eks-production
  server: https://XXXXX.gr7.eu-west-1.eks.amazonaws.com
  config: |
    {
      "awsAuthConfig": {
        "clusterName": "my-eks-cluster",
        "roleARN": "arn:aws:iam::123456789012:role/argocd-manager"
      },
      "tlsClientConfig": {
        "insecure": false,
        "caData": "LS0tLS1CRUdJTi..."
      }
    }

Examples

Application targeting external cluster:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: overlays/production
  destination:
    # External cluster server URL
    server: https://production-cluster.example.com
    namespace: myapp
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Multi-environment deployment with app-of-apps:

# Parent application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-all-envs
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: argocd-apps/
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
---
# Child applications in argocd-apps/ directory
# argocd-apps/staging.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-staging
  namespace: argocd
spec:
  project: staging
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: develop
    path: overlays/staging
  destination:
    server: https://staging-cluster.example.com
    namespace: myapp
---
# argocd-apps/production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: overlays/production
  destination:
    server: https://production-cluster.example.com
    namespace: myapp

ApplicationSet Patterns

ApplicationSets enable automated generation of Applications from templates.

Key Concepts

  • Generator: Produces parameters for application templates
  • Template: Defines the Application structure with parameter placeholders
  • Sync Policy: Controls how generated applications are managed
ApplicationSetGeneratorParametersTemplateGeneratedApplicationsApp: dev-myappApp: staging-myappApp: prod-myappApplicationSetGeneratorParametersTemplateGeneratedApplicationsApp: dev-myappApp: staging-myappApp: prod-myapp

Generator Types

List Generator:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: dev
            url: https://dev-cluster.example.com
            namespace: myapp-dev
          - cluster: staging
            url: https://staging-cluster.example.com
            namespace: myapp-staging
          - cluster: production
            url: https://production-cluster.example.com
            namespace: myapp-prod
  template:
    metadata:
      name: 'myapp-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: main
        path: 'overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: '{{namespace}}'

Git Generator (Directory):

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/cluster-addons
        revision: main
        directories:
          - path: addons/*
          - path: addons/exclude/*
            exclude: true
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/cluster-addons
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'

Git Generator (Files):

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-envs
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/myapp
        revision: main
        files:
          - path: "config/**/config.json"
  template:
    metadata:
      name: 'myapp-{{environment}}'
      labels:
        env: '{{environment}}'
        team: '{{team}}'
    spec:
      project: '{{project}}'
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: main
        path: 'overlays/{{environment}}'
        helm:
          values: |
            image:
              tag: {{image.tag}}
      destination:
        server: '{{cluster.server}}'
        namespace: '{{namespace}}'

Cluster Generator:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: monitoring
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            monitoring: enabled
        values:
          revision: main
  template:
    metadata:
      name: 'monitoring-{{name}}'
    spec:
      project: infrastructure
      source:
        repoURL: https://github.com/myorg/monitoring
        targetRevision: '{{values.revision}}'
        path: stack
      destination:
        server: '{{server}}'
        namespace: monitoring

Matrix Generator (Combining Generators):

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-matrix
  namespace: argocd
spec:
  generators:
    - matrix:
        generators:
          - git:
              repoURL: https://github.com/myorg/myapp
              revision: main
              directories:
                - path: apps/*
          - clusters:
              selector:
                matchLabels:
                  environment: production
  template:
    metadata:
      name: '{{path.basename}}-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: main
        path: '{{path}}'
      destination:
        server: '{{server}}'
        namespace: '{{path.basename}}'

Merge Generator:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-merge
  namespace: argocd
spec:
  generators:
    - merge:
        mergeKeys:
          - cluster
        generators:
          - clusters:
              values:
                version: default
          - list:
              elements:
                - cluster: production
                  version: v1.2.3
                - cluster: staging
                  version: latest
  template:
    metadata:
      name: 'myapp-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: '{{values.version}}'
        path: manifests
      destination:
        server: '{{server}}'
        namespace: myapp

ApplicationSet Sync Policy

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - env: dev
          - env: staging
          - env: prod
  template:
    metadata:
      name: 'myapp-{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: main
        path: 'overlays/{{env}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: 'myapp-{{env}}'
  syncPolicy:
    # Preserve resources when ApplicationSet is deleted
    preserveResourcesOnDeletion: true
    # Application sync policy
    applicationsSync: create-update

Examples

Progressive delivery ApplicationSet:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-progressive
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - env: dev
            autosync: "true"
            branch: develop
          - env: staging
            autosync: "true"
            branch: main
          - env: production
            autosync: "false"
            branch: main
  template:
    metadata:
      name: 'myapp-{{env}}'
      annotations:
        notifications.argoproj.io/subscribe.on-sync-succeeded.slack: deployments
    spec:
      project: '{{env}}'
      source:
        repoURL: https://github.com/myorg/myapp
        targetRevision: '{{branch}}'
        path: 'overlays/{{env}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: 'myapp-{{env}}'
      syncPolicy:
        automated:
          prune: '{{autosync}}'
          selfHeal: '{{autosync}}'

Common Troubleshooting Scenarios

Diagnosing and resolving common ArgoCD issues.

Key Concepts

  • Application Logs: Logs from ArgoCD components
  • Events: Kubernetes events related to ArgoCD resources
  • Sync Failures: Common causes and solutions

Common Commands/Patterns

# View ArgoCD server logs
kubectl logs -n argocd deployment/argocd-server -f

# View application controller logs
kubectl logs -n argocd deployment/argocd-application-controller -f

# View repo server logs
kubectl logs -n argocd deployment/argocd-repo-server -f

# Get application events
kubectl get events -n argocd --field-selector involvedObject.name=myapp

# Describe application
kubectl describe application myapp -n argocd

# Get sync operation details
argocd app get myapp --show-operation

# Get resource tree
argocd app resources myapp --tree

# Check cluster connectivity
argocd cluster get https://production-cluster.example.com

# Validate application manifest
argocd app create myapp --dry-run --validate -f application.yaml

# Debug repository access
argocd repo get https://github.com/myorg/myapp

Common Issues and Solutions

Issue Cause Solution
ComparisonError: failed to unmarshal Invalid YAML/JSON in manifests Validate manifests with kubectl apply --dry-run=client
rpc error: code = Unknown desc = authentication required Invalid or expired repository credentials Update repository credentials in ArgoCD
Unable to create application: permission denied Project restrictions Check AppProject source repos and destinations
Sync failed: context deadline exceeded Slow repository clone or large manifests Increase timeout or optimise repository
Namespace not found Target namespace doesn't exist Add CreateNamespace=true sync option
Resource already exists Resource managed by another tool Use Replace=true sync option or resolve ownership
OutOfSync but sync shows no changes Ignored differences or cache issue Hard refresh or check ignoreDifferences
Unknown health status Custom resource without health check Add custom health check in argocd-cm
Cluster connection failed Network or credential issues Check cluster secret and network connectivity
Invalid Helm chart Chart validation failure Run helm lint and helm template locally

Debugging Tips

# Reset application to force re-sync
argocd app sync myapp --force

# Terminate stuck operation
argocd app terminate-op myapp

# Clear repository cache
kubectl delete secret -n argocd -l argocd.argoproj.io/secret-type=repository

# Restart ArgoCD components
kubectl rollout restart deployment -n argocd argocd-server
kubectl rollout restart deployment -n argocd argocd-repo-server
kubectl rollout restart deployment -n argocd argocd-application-controller

# Check Redis connection
kubectl exec -it -n argocd deployment/argocd-redis -- redis-cli ping

# Verify RBAC configuration
argocd admin rbac validate -f argocd-rbac-cm.yaml

# Export application for debugging
argocd app get myapp -o yaml > myapp-export.yaml

# Check application conditions
kubectl get application myapp -n argocd -o jsonpath='{.status.conditions}'

Log Analysis

# Find sync errors in controller logs
kubectl logs -n argocd deployment/argocd-application-controller | grep -i error

# Find authentication issues
kubectl logs -n argocd deployment/argocd-repo-server | grep -i "auth\|permission\|denied"

# Monitor real-time sync activity
kubectl logs -n argocd deployment/argocd-application-controller -f | grep myapp

# Check for resource conflicts
kubectl logs -n argocd deployment/argocd-application-controller | grep "already exists"

Examples

Troubleshooting repository issues:

# Test repository access
argocd repo get https://github.com/myorg/myapp

# Re-add repository with debugging
argocd repo add https://github.com/myorg/myapp \
  --username myuser \
  --password mytoken \
  --insecure-skip-server-verification

# Check repository secret
kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=repository

# View repo server manifest generation
argocd app manifests myapp --source live
argocd app manifests myapp --source git

Troubleshooting sync issues:

# Get detailed sync status
argocd app get myapp --show-operation

# View sync result
kubectl get application myapp -n argocd -o jsonpath='{.status.operationState}'

# Check for resource pruning issues
argocd app sync myapp --prune --dry-run

# Sync with verbose output
argocd app sync myapp --verbose

Quick Reference

Command Description
argocd login <server> Login to ArgoCD server
argocd app create <name> Create an application
argocd app get <name> Get application details
argocd app list List all applications
argocd app sync <name> Sync an application
argocd app diff <name> Show application diff
argocd app delete <name> Delete an application
argocd app history <name> Show application history
argocd app rollback <name> <id> Rollback to previous version
argocd app resources <name> List application resources
argocd app set <name> Update application settings
argocd repo add <url> Add a Git repository
argocd repo list List repositories
argocd cluster add <context> Add a cluster
argocd cluster list List clusters
argocd proj create <name> Create a project
argocd proj list List projects
argocd account list List accounts
argocd admin settings resource-overrides View resource customisations

Useful Flags

Flag Description
--grpc-web Use gRPC-web protocol
--insecure Skip TLS verification
-o json/yaml/wide Output format
--refresh Force cache refresh
--hard-refresh Invalidate cache completely
--prune Remove resources not in Git
--dry-run Preview changes without applying
--force Force resource updates
--async Don't wait for operation to complete
--cascade Delete resources when deleting app

Sync Status Values

Status Description
Synced Live state matches desired state
OutOfSync Live state differs from desired state
Unknown Sync status cannot be determined

Health Status Values

Status Description
Healthy All resources are healthy
Progressing Resources are in progress
Degraded One or more resources are degraded
Suspended Resources are suspended
Missing Resources are missing
Unknown Health cannot be determined

Related Topics

The following topics complement ArgoCD and are commonly used together in GitOps workflows:

  1. Kubernetes - Understanding Kubernetes resources is essential for effective ArgoCD application management
  2. Helm - ArgoCD natively supports Helm charts as application sources for templated deployments
  3. Kustomize - ArgoCD supports Kustomize overlays for environment-specific configurations
  4. git - Fundamental for GitOps workflows and understanding repository operations
  5. Prometheus/Grafana - Monitoring ArgoCD metrics and creating dashboards for deployment visibility
  6. CI/CD Patterns - Understanding continuous integration workflows that feed into ArgoCD deployments