Skip to content
Vladimir Chavkov
Go back

ArgoCD: Complete GitOps Continuous Delivery Guide for Kubernetes

Edit page

ArgoCD: Complete GitOps Continuous Delivery Guide for Kubernetes

ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes that keeps your applications synchronized with Git repositories. This comprehensive guide covers ArgoCD installation, application management, and production deployment strategies.

What is ArgoCD?

ArgoCD implements GitOps principles for Kubernetes:

Key Features

  1. Declarative GitOps: Git as single source of truth
  2. Automated Deployment: Automatic sync from Git to Kubernetes
  3. Multi-Cluster Management: Manage multiple clusters from single instance
  4. Application Health: Real-time health status monitoring
  5. Rollback: Easy rollback to any Git commit
  6. SSO Integration: OIDC, SAML, GitHub, GitLab authentication
  7. RBAC: Fine-grained access control
  8. Webhooks: GitHub/GitLab integration for automated sync
  9. CLI & UI: Both command-line and web interface
  10. Helm/Kustomize Support: Native support for packaging tools

GitOps Workflow

┌─────────────────────────────────────────────────────────────┐
│ Developer Workflow │
│ │
│ Developer → Git Push → Git Repository │
│ (Single Source of Truth) │
└────────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ArgoCD │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Git Repo │──────►│ ArgoCD │ │
│ │ Monitor │ │ Controller │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Sync │ │
│ │ Engine │ │
│ └──────┬───────┘ │
└──────────────────────────────┼──────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster(s) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ App 1 │ │ App 2 │ │ App 3 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘

Installation

Using Kubectl

Terminal window
# Create namespace
kubectl create namespace argocd
# Install ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods to be ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d
# Port forward to access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Access at https://localhost:8080
# Username: admin
# Password: (from above command)

Using Helm

Terminal window
# Add ArgoCD Helm repository
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
# Create values file
cat > argocd-values.yaml << 'EOF'
global:
domain: argocd.example.com
configs:
params:
server.insecure: true # Use with ingress TLS termination
server:
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- argocd.example.com
tls:
- secretName: argocd-tls
hosts:
- argocd.example.com
# High availability
replicas: 3
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
repoServer:
replicas: 2
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
applicationSet:
enabled: true
notifications:
enabled: true
EOF
# Install with Helm
helm install argocd argo/argo-cd \
-n argocd \
--create-namespace \
-f argocd-values.yaml

Install CLI

Terminal window
# macOS
brew install argocd
# Linux
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
# Windows (PowerShell)
$version = (Invoke-RestMethod https://api.github.com/repos/argoproj/argo-cd/releases/latest).tag_name
$url = "https://github.com/argoproj/argo-cd/releases/download/" + $version + "/argocd-windows-amd64.exe"
Invoke-WebRequest -Uri $url -OutFile argocd.exe
# Verify
argocd version

CLI Configuration

Terminal window
# Login to ArgoCD
argocd login argocd.example.com
# Or with port-forward
argocd login localhost:8080 --insecure
# Login with token
argocd login argocd.example.com --auth-token $TOKEN
# Update admin password
argocd account update-password
# List clusters
argocd cluster list
# Add cluster
argocd cluster add my-cluster-context
# List repositories
argocd repo list
# Add repository
argocd repo add https://github.com/example/gitops-repo \
--username git \
--password $GITHUB_TOKEN

Creating Applications

Using CLI

Terminal window
# Create application
argocd app create my-app \
--repo https://github.com/example/gitops-repo \
--path kubernetes/apps/my-app \
--dest-server https://kubernetes.default.svc \
--dest-namespace production
# Create with Helm
argocd app create my-helm-app \
--repo https://github.com/example/helm-charts \
--path charts/my-app \
--helm-set replicaCount=3 \
--helm-set-string image.tag=v1.2.3 \
--dest-server https://kubernetes.default.svc \
--dest-namespace production
# Create with Kustomize
argocd app create my-kustomize-app \
--repo https://github.com/example/kustomize-repo \
--path overlays/production \
--dest-server https://kubernetes.default.svc \
--dest-namespace production
# Sync application
argocd app sync my-app
# Get application status
argocd app get my-app
# List applications
argocd app list
# Delete application
argocd app delete my-app

Using YAML Manifest

application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
# Finalizer that ensures cascading deletes
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
# Project the application belongs to
project: default
# Source of the application manifests
source:
repoURL: https://github.com/example/gitops-repo
targetRevision: main
path: kubernetes/apps/my-app
# Kustomize specific config
kustomize:
namePrefix: prod-
images:
- my-app:v1.2.3
# Helm specific config
helm:
releaseName: my-app
values: |
replicaCount: 3
image:
tag: v1.2.3
parameters:
- name: service.type
value: LoadBalancer
# Destination cluster and namespace
destination:
server: https://kubernetes.default.svc
namespace: production
# Sync policy
syncPolicy:
automated:
prune: true # Delete resources not in Git
selfHeal: true # Sync when cluster state changes
allowEmpty: false # Don't delete all resources
syncOptions:
- CreateNamespace=true
- PruneLast=true
- ApplyOutOfSyncOnly=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# Ignore differences
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
# Health assessment
revisionHistoryLimit: 10

Apply the application:

Terminal window
kubectl apply -f application.yaml

Application Management

Sync Strategies

Terminal window
# Manual sync
argocd app sync my-app
# Auto-sync (in application manifest)
syncPolicy:
automated:
prune: true
selfHeal: true
# Sync with options
argocd app sync my-app \
--prune \
--force \
--async
# Sync specific resources
argocd app sync my-app \
--resource apps:Deployment:my-deployment
# Selective sync
argocd app sync my-app \
--label app.kubernetes.io/component=api

Rollback

Terminal window
# List revision history
argocd app history my-app
# Rollback to previous revision
argocd app rollback my-app
# Rollback to specific revision
argocd app rollback my-app 5
# Sync to specific Git commit
argocd app sync my-app --revision abc123

Health and Status

Terminal window
# Get application health
argocd app get my-app
# Watch application status
argocd app wait my-app --health
# Get sync status
argocd app sync-status my-app
# List application resources
argocd app resources my-app
# Get application logs
argocd app logs my-app
# Diff between Git and cluster
argocd app diff my-app

Projects

Create Project

project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Production applications
# Source repositories
sourceRepos:
- 'https://github.com/example/*'
- 'https://charts.bitnami.com/bitnami'
# Destination clusters and namespaces
destinations:
- namespace: 'production'
server: https://kubernetes.default.svc
- namespace: 'staging'
server: https://kubernetes.default.svc
# Cluster resource whitelist
clusterResourceWhitelist:
- group: ''
kind: Namespace
- group: 'rbac.authorization.k8s.io'
kind: ClusterRole
# Namespace resource whitelist
namespaceResourceWhitelist:
- group: 'apps'
kind: Deployment
- group: ''
kind: Service
- group: ''
kind: ConfigMap
- group: ''
kind: Secret
# Roles for RBAC
roles:
- name: developer
description: Developer role
policies:
- p, proj:production:developer, applications, get, production/*, allow
- p, proj:production:developer, applications, sync, production/*, allow
groups:
- developers
- name: admin
description: Admin role
policies:
- p, proj:production:admin, applications, *, production/*, allow
- p, proj:production:admin, repositories, *, *, allow
groups:
- admins

Apply project:

Terminal window
kubectl apply -f project.yaml

Multi-Cluster Management

Add Cluster

Terminal window
# List available contexts
kubectl config get-contexts
# Add cluster to ArgoCD
argocd cluster add staging-cluster
# Add with specific namespace
argocd cluster add prod-cluster \
--namespace argocd \
--name production-cluster
# Add with labels
argocd cluster add cluster-name \
--label env=production \
--label region=us-east

Deploy to Multiple Clusters

# Application for multiple clusters
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: multi-cluster-app
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: staging
url: https://staging-cluster
namespace: app
- cluster: production
url: https://production-cluster
namespace: app
template:
metadata:
name: '{{cluster}}-app'
spec:
project: default
source:
repoURL: https://github.com/example/gitops-repo
targetRevision: HEAD
path: apps/my-app
kustomize:
namePrefix: '{{cluster}}-'
destination:
server: '{{url}}'
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true

ApplicationSet

Git Generator

applicationset-git.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/example/gitops-repo
revision: HEAD
directories:
- path: apps/*
template:
metadata:
name: '{{path.basename}}'
spec:
project: default
source:
repoURL: https://github.com/example/gitops-repo
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

Cluster Generator

applicationset-cluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-clusters-app
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: '{{name}}-my-app'
spec:
project: default
source:
repoURL: https://github.com/example/gitops-repo
targetRevision: HEAD
path: apps/my-app
destination:
server: '{{server}}'
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true

SSO Integration

GitHub OAuth

# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
url: https://argocd.example.com
dex.config: |
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $github_client_id
clientSecret: $github_client_secret
orgs:
- name: my-org
teams:
- developers
- admins

OIDC (Okta, Keycloak)

data:
oidc.config: |
name: Okta
issuer: https://dev-123456.okta.com
clientID: $oidc_client_id
clientSecret: $oidc_client_secret
requestedScopes:
- openid
- profile
- email
- groups

RBAC

Configure RBAC

# argocd-rbac-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.default: role:readonly
policy.csv: |
# Developers - can sync apps in dev project
p, role:developer, applications, get, dev/*, allow
p, role:developer, applications, sync, dev/*, allow
g, developers, role:developer
# Operators - can manage apps in production
p, role:operator, applications, *, production/*, allow
p, role:operator, clusters, get, *, allow
g, operators, role:operator
# Admins - full access
p, role:admin, *, *, *, allow
g, admins, role:admin
# GitHub team mapping
g, my-org:developers, role:developer
g, my-org:operators, role:operator
g, my-org:admins, role:admin

Notifications

Configure Notifications

Terminal window
# Install notifications controller (if not installed)
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/stable/manifests/install.yaml

Slack Notifications

# argocd-notifications-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: $slack-token
template.app-deployed: |
message: |
Application {{.app.metadata.name}} is now running new version.
slack:
attachments: |
[{
"title": "{{ .app.metadata.name}}",
"title_link":"{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"color": "#18be52",
"fields": [
{
"title": "Sync Status",
"value": "{{.app.status.sync.status}}",
"short": true
},
{
"title": "Repository",
"value": "{{.app.spec.source.repoURL}}",
"short": true
}
]
}]
trigger.on-deployed: |
- when: app.status.operationState.phase in ['Succeeded']
send: [app-deployed]
subscriptions: |
- recipients:
- slack:deployments
triggers:
- on-deployed

Application Annotation

metadata:
annotations:
notifications.argoproj.io/subscribe.on-deployed.slack: deployments

Best Practices

Repository Structure

Terminal window
gitops-repo/
├── apps/
├── frontend/
├── base/
├── kustomization.yaml
├── deployment.yaml
└── service.yaml
└── overlays/
├── dev/
├── staging/
└── production/
└── backend/
└── ...
├── infrastructure/
├── ingress-nginx/
├── cert-manager/
└── monitoring/
└── argocd/
├── applications/
├── frontend.yaml
└── backend.yaml
└── projects/
├── production.yaml
└── development.yaml

App of Apps Pattern

root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/gitops-repo
targetRevision: HEAD
path: argocd/applications
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true

Health Checks

# Custom health check
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

Monitoring

Prometheus Metrics

# ServiceMonitor for ArgoCD
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argocd-metrics
namespace: argocd
spec:
selector:
matchLabels:
app.kubernetes.io/name: argocd-metrics
endpoints:
- port: metrics

Key Metrics

Troubleshooting

Terminal window
# Check ArgoCD pods
kubectl get pods -n argocd
# View application controller logs
kubectl logs -n argocd deployment/argocd-application-controller
# View repo server logs
kubectl logs -n argocd deployment/argocd-repo-server
# View server logs
kubectl logs -n argocd deployment/argocd-server
# Debug application sync
argocd app sync my-app --dry-run
argocd app diff my-app
# Refresh application
argocd app get my-app --refresh
# Hard refresh (bypass cache)
argocd app get my-app --hard-refresh

Conclusion

ArgoCD provides a robust, GitOps-based approach to continuous delivery on Kubernetes. By treating Git as the single source of truth and automatically synchronizing cluster state, ArgoCD simplifies application deployment while improving security, reliability, and auditability.


Master GitOps and Kubernetes with our comprehensive training programs. Contact us for customized training.


Edit page
Share this post on:

Previous Post
Proxmox Backup Server: Enterprise Backup and Recovery Guide
Next Post
Helm: Complete Kubernetes Package Manager and Chart Development Guide