A declarative, GitOps continuous delivery tool for Kubernetes that syncs application state from Git repositories to clusters.

Table of Contents#

  1. Overview
  2. Architecture
  3. Installation
  4. Application Deployment
  5. Sync Policies and Strategies
  6. Multi-Cluster Management
  7. RBAC and Access Control
  8. Notifications and Integrations
  9. CLI Reference
  10. Troubleshooting
  11. See Also
  12. Sources

1. Overview#

Argo CD automates the deployment of applications to Kubernetes clusters by continuously monitoring Git repositories and reconciling live cluster state with the desired state defined in those repositories. It supports plain YAML manifests, Helm charts, Kustomize overlays, and Jsonnet, making it a flexible GitOps operator for teams of any size.

Key features:

  • Declarative GitOps - applications defined entirely in Git, the single source of truth
  • Automated sync - detects drift and optionally self-heals without manual intervention
  • Multi-tool support - renders Helm, Kustomize, Jsonnet, and plain YAML natively
  • Visual dashboard - web UI and CLI for real-time application state and diff visualization
  • Rollback - one-click rollback to any previously synced Git revision
  • SSO integration - OIDC, LDAP, SAML, GitHub, GitLab, and more

2. Architecture#

Argo CD runs as a set of controllers inside the cluster:

ComponentRole
argocd-serverAPI server, UI, and gRPC/REST endpoint
argocd-repo-serverClones repos and renders manifests (Helm, Kustomize, etc.)
argocd-application-controllerWatches Application CRs, compares live vs. desired state, syncs
argocd-applicationset-controllerGenerates Application CRs from templates (cluster generators, Git generators, etc.)
argocd-dex-serverOptional, provides SSO via Dex
argocd-redisCaching layer for repo and cluster state
argocd-notifications-controllerSends notifications on sync/health events

The controller continuously compares the live state of each managed application against the target state defined in Git. When a discrepancy is detected, the application is marked OutOfSync, and depending on the sync policy, Argo CD either alerts the user or automatically reconciles.

3. Installation#

3.1 Prerequisites#

  • A Kubernetes cluster (v1.24+)
  • kubectl configured for the target cluster
  • Optionally, argocd CLI installed

3.2 Install via Manifests#

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

Verify all pods are running:

kubectl get pods -n argocd

3.3 Install via Helm#

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --set server.service.type=LoadBalancer

3.4 Install the 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/

3.5 Access the Dashboard#

Port-forward for quick access:

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

Access https://localhost:8080 in a browser.

Retrieve the initial admin password:

argocd admin initial-password -n argocd

Change it immediately:

argocd login localhost:8080
argocd account update-password

4. Application Deployment#

4.1 Declarative Application (YAML)#

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: <app-name>
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/<org>/<repo>.git
    path: <manifest-path>
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: <target-namespace>
  syncPolicy:
    automated:
      selfHeal: true
      prune: true
    syncOptions:
    - CreateNamespace=true

Apply:

kubectl apply -n argocd -f <app-name>.yaml

4.2 CLI Application Creation#

argocd app create <app-name> \
  --repo https://github.com/<org>/<repo>.git \
  --path <manifest-path> \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace <target-namespace> \
  --sync-policy automated \
  --self-heal \
  --auto-prune

4.3 Helm-Based Application#

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: <app-name>
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://charts.example.com
    chart: <chart-name>
    targetRevision: <chart-version>
    helm:
      releaseName: <release-name>
      valuesObject:
        replicaCount: 3
        image:
          tag: latest
  destination:
    server: https://kubernetes.default.svc
    namespace: <target-namespace>

4.4 ApplicationSets#

ApplicationSets generate multiple Applications from a single template. Useful for deploying the same app across many clusters or environments:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: <appset-name>
  namespace: argocd
spec:
  generators:
  - clusters:
      selector:
        matchLabels:
          env: production
  template:
    metadata:
      name: '{{name}}-<app-name>'
    spec:
      project: default
      source:
        repoURL: https://github.com/<org>/<repo>.git
        path: <manifest-path>
        targetRevision: HEAD
      destination:
        server: '{{server}}'
        namespace: <target-namespace>

5. Sync Policies and Strategies#

5.1 Automated Sync#

Enable automated sync so Argo CD reconciles on every Git change:

OptionDescription
automated.selfHealRe-syncs when live state drifts from Git (manual edits reverted)
automated.pruneDeletes resources removed from Git
automated.allowEmptyAllows syncing when the app has no resources

5.2 Sync Options#

Add under syncPolicy.syncOptions:

syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
- ApplyOutOfSyncOnly=true
- ServerSideApply=true
- RespectIgnoreDifferences=true

5.3 Sync Waves and Hooks#

Control the order of resource creation with annotations:

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "1"

Resources sync in wave order (lowest first). Combine with sync hooks for lifecycle events:

HookTrigger
PreSyncBefore the sync operation begins
SyncDuring the sync (default)
PostSyncAfter all Sync resources are healthy
SyncFailAfter a sync operation fails
SkipResource is ignored by Argo CD

Example hook:

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: <migration-image>
        command: ["./migrate.sh"]
      restartPolicy: Never

5.4 Ignore Differences#

Exclude fields from drift detection:

spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas
  - group: "*"
    kind: "*"
    managedFieldsManagers:
    - kube-controller-manager

6. Multi-Cluster Management#

6.1 Adding External Clusters#

# List available contexts
kubectl config get-contexts

# Add a cluster to Argo CD
argocd cluster add <context-name>

This installs a ServiceAccount in the target cluster and stores credentials in a Secret in the Argo CD namespace.

6.2 Cluster Credentials via Declarative Config#

apiVersion: v1
kind: Secret
metadata:
  name: <cluster-name>
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: <cluster-display-name>
  server: https://<cluster-api-endpoint>
  config: |
    {
      "bearerToken": "<service-account-token>",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "<base64-ca-cert>"
      }
    }

6.3 Projects for Multi-Tenancy#

Argo CD Projects restrict what repositories, clusters, and namespaces an Application can use:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: <project-name>
  namespace: argocd
spec:
  description: <project-description>
  sourceRepos:
  - https://github.com/<org>/*
  destinations:
  - namespace: '<target-namespace>'
    server: https://kubernetes.default.svc
  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  namespaceResourceBlacklist:
  - group: ''
    kind: ResourceQuota
  roles:
  - name: <role-name>
    description: <role-description>
    policies:
    - p, proj:<project-name>:<role-name>, applications, get, <project-name>/*, allow
    - p, proj:<project-name>:<role-name>, applications, sync, <project-name>/*, allow

7. RBAC and Access Control#

7.1 Built-in Roles#

RolePermissions
role:readonlyRead-only access to all resources
role:adminFull access to all resources

7.2 Custom RBAC Policies#

Configure in the argocd-rbac-cm ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    p, role:dev, applications, get, */*, allow
    p, role:dev, applications, sync, */*, allow
    p, role:dev, applications, action/*, */*, allow
    p, role:ops, applications, *, */*, allow
    p, role:ops, clusters, get, *, allow
    g, dev-team, role:dev
    g, ops-team, role:ops

7.3 SSO Integration#

Configure OIDC in the argocd-cm ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  url: https://<argocd-hostname>
  oidc.config: |
    name: <provider-name>
    issuer: https://<oidc-issuer-url>
    clientID: <client-id>
    clientSecret: $oidc.clientSecret
    requestedScopes:
    - openid
    - profile
    - email
    - groups

Store the client secret in argocd-secret:

apiVersion: v1
kind: Secret
metadata:
  name: argocd-secret
  namespace: argocd
stringData:
  oidc.clientSecret: <client-secret>

8. Notifications and Integrations#

8.1 Install Notifications#

Notifications are included in Argo CD 2.6+ by default. For earlier versions, install separately:

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/release-1.2/manifests/install.yaml

8.2 Configure Notification Services#

Edit the argocd-notifications-cm ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  service.webhook.teams: |
    url: https://outlook.office.com/webhook/<webhook-path>
    headers:
    - name: Content-Type
      value: application/json
  service.email: |
    host: <smtp-host>
    port: 587
    from: argocd@<domain>
    username: $email-username
    password: $email-password

8.3 Notification Templates and Triggers#

data:
  template.app-sync-succeeded: |
    message: |
      Application {{.app.metadata.name}} sync succeeded.
      Revision: {{.app.status.sync.revision}}
    slack:
      attachments: |
        [{
          "color": "#18be52",
          "title": "{{.app.metadata.name}} synced",
          "text": "Revision: {{.app.status.sync.revision}}"
        }]
  trigger.on-sync-succeeded: |
    - when: app.status.operationState.phase in ['Succeeded']
      send: [app-sync-succeeded]
  trigger.on-health-degraded: |
    - when: app.status.health.status == 'Degraded'
      send: [app-health-degraded]

8.4 Subscribe Applications to Notifications#

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    notifications.argoproj.io/subscribe.on-sync-succeeded.slack: <channel-name>
    notifications.argoproj.io/subscribe.on-health-degraded.email: <email-address>

9. CLI Reference#

CommandDescription
argocd app listList all applications
argocd app get <app-name>Show application details
argocd app sync <app-name>Manually trigger a sync
argocd app diff <app-name>Show diff between live and desired state
argocd app rollback <app-name> <revision>Roll back to a specific revision
argocd app delete <app-name>Delete an application
argocd app history <app-name>Show sync history
argocd cluster listList managed clusters
argocd cluster add <context>Register a cluster
argocd repo add <url>Register a Git repository
argocd proj listList projects
argocd account listList user accounts

Troubleshooting#

IssueCauseSolution
Application stuck in Unknown healthHealth check not defined for custom resourceAdd a custom health check in argocd-cm under resource.customizations.health
ComparisonError on ApplicationRepo server cannot render manifestsCheck argocd-repo-server logs; verify repo URL, credentials, and chart version
Sync fails with "namespace not found"Target namespace does not existAdd CreateNamespace=true to syncOptions
Application shows OutOfSync after syncExternal controllers modify resources (e.g., HPA changing replicas)Add ignoreDifferences for the mutated fields
rpc error: code = Unavailable on CLITLS or network issue reaching the API serverVerify argocd-server service type; use --insecure for self-signed certs
Repository not accessibleInvalid credentials or SSH keyRun argocd repo add with --ssh-private-key-path or --username/--password
Helm values not appliedWrong valuesObject or values fieldUse valuesObject (inline) or valueFiles (file reference), not both mixed
Sync wave ordering ignoredMissing annotation on resourcesAdd argocd.argoproj.io/sync-wave annotation with numeric string value
Notifications not firingSubscription annotation missing or misconfiguredVerify annotation format: notifications.argoproj.io/subscribe.<trigger>.<service>
Cluster shows ConnectionRefusedServiceAccount token expired or cluster unreachableRe-add the cluster with argocd cluster add or rotate credentials

See Also#

Sources#