A declarative, GitOps continuous delivery tool for Kubernetes that syncs application state from Git repositories to clusters.
Table of Contents#
- Overview
- Architecture
- Installation
- Application Deployment
- Sync Policies and Strategies
- Multi-Cluster Management
- RBAC and Access Control
- Notifications and Integrations
- CLI Reference
- Troubleshooting
- See Also
- 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:
| Component | Role |
|---|---|
argocd-server | API server, UI, and gRPC/REST endpoint |
argocd-repo-server | Clones repos and renders manifests (Helm, Kustomize, etc.) |
argocd-application-controller | Watches Application CRs, compares live vs. desired state, syncs |
argocd-applicationset-controller | Generates Application CRs from templates (cluster generators, Git generators, etc.) |
argocd-dex-server | Optional, provides SSO via Dex |
argocd-redis | Caching layer for repo and cluster state |
argocd-notifications-controller | Sends 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+)
kubectlconfigured for the target cluster- Optionally,
argocdCLI 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.yamlVerify all pods are running:
kubectl get pods -n argocd3.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=LoadBalancer3.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:443Access https://localhost:8080 in a browser.
Retrieve the initial admin password:
argocd admin initial-password -n argocdChange it immediately:
argocd login localhost:8080
argocd account update-password4. 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=trueApply:
kubectl apply -n argocd -f <app-name>.yaml4.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-prune4.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:
| Option | Description |
|---|---|
automated.selfHeal | Re-syncs when live state drifts from Git (manual edits reverted) |
automated.prune | Deletes resources removed from Git |
automated.allowEmpty | Allows 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=true5.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:
| Hook | Trigger |
|---|---|
PreSync | Before the sync operation begins |
Sync | During the sync (default) |
PostSync | After all Sync resources are healthy |
SyncFail | After a sync operation fails |
Skip | Resource 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: Never5.4 Ignore Differences#
Exclude fields from drift detection:
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
- group: "*"
kind: "*"
managedFieldsManagers:
- kube-controller-manager6. 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>/*, allow7. RBAC and Access Control#
7.1 Built-in Roles#
| Role | Permissions |
|---|---|
role:readonly | Read-only access to all resources |
role:admin | Full 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:ops7.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
- groupsStore 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.yaml8.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-password8.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#
| Command | Description |
|---|---|
argocd app list | List 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 list | List managed clusters |
argocd cluster add <context> | Register a cluster |
argocd repo add <url> | Register a Git repository |
argocd proj list | List projects |
argocd account list | List user accounts |
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
Application stuck in Unknown health | Health check not defined for custom resource | Add a custom health check in argocd-cm under resource.customizations.health |
ComparisonError on Application | Repo server cannot render manifests | Check argocd-repo-server logs; verify repo URL, credentials, and chart version |
| Sync fails with "namespace not found" | Target namespace does not exist | Add CreateNamespace=true to syncOptions |
Application shows OutOfSync after sync | External controllers modify resources (e.g., HPA changing replicas) | Add ignoreDifferences for the mutated fields |
rpc error: code = Unavailable on CLI | TLS or network issue reaching the API server | Verify argocd-server service type; use --insecure for self-signed certs |
| Repository not accessible | Invalid credentials or SSH key | Run argocd repo add with --ssh-private-key-path or --username/--password |
| Helm values not applied | Wrong valuesObject or values field | Use valuesObject (inline) or valueFiles (file reference), not both mixed |
| Sync wave ordering ignored | Missing annotation on resources | Add argocd.argoproj.io/sync-wave annotation with numeric string value |
| Notifications not firing | Subscription annotation missing or misconfigured | Verify annotation format: notifications.argoproj.io/subscribe.<trigger>.<service> |
Cluster shows ConnectionRefused | ServiceAccount token expired or cluster unreachable | Re-add the cluster with argocd cluster add or rotate credentials |