GitOps with ArgoCD
A comprehensive guide to GitOps with ArgoCD covering declarative deployments, continuous delivery, sync policies, rollbacks, multi-cluster management, and practical implementation strategies for modern Kubernetes operations.
GitOps is a paradigm for managing Kubernetes and infrastructure using Git as the single source of truth. In GitOps, Git repositories contain the entire desired state of the system, including application configurations, infrastructure definitions, and policies. An operator (like ArgoCD) continuously reconciles the actual cluster state with the desired state stored in Git.
GitOps brings several benefits: improved auditability (all changes are tracked in Git), faster recovery (rollback is just reverting a commit), and simplified operations (everything is declarative).
- Declarative Configuration: Everything is defined as code in Git
- Version Control: Git is the single source of truth
- Automated Reconciliation: Continuous sync between Git and clusters
- Observability: Monitoring and alerting on drift detection
- Auditability: All changes are tracked and reviewable
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It follows a pull-based model where the controller continuously monitors Git repositories and synchronizes cluster state.
ArgoCD API Server
Application Controller
Redis Cache
Repo Server
# Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Install CLI
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/
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Access ArgoCD UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Login via CLI
argocd login localhost:8080 --username admin --password --insecure
# Change password
argocd account update-password
An Application in ArgoCD defines which Git repository to source, which cluster to deploy to, and how to sync. Applications are the core unit of management in ArgoCD.
# Basic Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: ./nginx
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
# Application with Helm
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/helm-charts
targetRevision: main
path: ./myapp
helm:
valueFiles:
- values-production.yaml
parameters:
- name: replicaCount
value: "3"
- name: image.tag
value: "1.2.3"
destination:
server: https://kubernetes.default.svc
namespace: production
# Application with Kustomize
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-kustomize
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/kustomize-apps
targetRevision: main
path: ./myapp/overlays/production
kustomize:
images:
- myapp:1.2.3
destination:
server: https://kubernetes.default.svc
namespace: production
- Use separate applications for different services (microservices approach)
- Use Projects to group applications and enforce policies
- Use
targetRevisionto pin to specific versions (use branches for dev, tags for production) - Set appropriate sync policies for your environment
Sync policies define how ArgoCD applies changes from Git to the cluster. You can choose between automated sync (continuous reconciliation) and manual sync (controlled by user).
Automated Sync
Manual Sync
Sync Waves
Sync Policies
prune removes resources not in Git. selfHeal corrects drift. allowEmpty permits zero resources.# Automated sync with PRUNE
syncPolicy:
automated:
prune: true # Delete resources not in Git
selfHeal: true # Correct drift automatically
allowEmpty: false # Don't allow empty applications
# Manual sync with specific sync options
syncPolicy:
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
- PruneLast=true # Prune only after successful sync
- Validate=true # Validate before apply
# Sync Waves (annotate resources)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
annotations:
argocd.argoproj.io/sync-wave: "5" # Lower numbers first
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config
annotations:
argocd.argoproj.io/sync-wave: "1" # Config before deployment
# Sync Hooks (pre/post-sync actions)
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: migration
image: myapp:latest
command: ["/bin/sh", "-c", "python migrate.py"]
restartPolicy: Never
ArgoCD supports managing multiple clusters from a single control plane. This enables consistent deployments across development, staging, and production clusters.
# Register a cluster
argocd cluster add cluster-context-name
# or using kubeconfig
argocd cluster add --kubeconfig ~/.kube/config --context cluster-context-name
# List clusters
argocd cluster list
# Application with cluster-specific configuration
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: ./nginx
helm:
valueFiles:
- values-prod.yaml
destination:
server: https://api.prod-cluster.example.com
namespace: production
# ApplicationSet for multi-cluster deployments
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: nginx-multi
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
environment: production
template:
metadata:
name: '{{name}}-nginx'
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: ./nginx
helm:
valueFiles:
- values-{{name}}.yaml
destination:
server: '{{server}}'
namespace: production
ApplicationSets are a powerful feature that generates applications using a template and generators. They enable dynamic, scalable management of applications across clusters and environments.
# Cluster Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: multi-cluster-apps
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
environment: production
values:
clusterName: '{{name}}'
clusterEnv: '{{metadata.labels.environment}}'
template:
metadata:
name: '{{name}}-app'
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: ./app
helm:
values: |
environment: {{values.clusterEnv}}
clusterName: {{values.clusterName}}
destination:
server: '{{server}}'
namespace: production
# Git Generator (directory-based)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: directory-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/myorg/apps
revision: HEAD
directories:
- path: ./apps/*
template:
metadata:
name: '{{path.basename}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: production
# List Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: list-apps
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: dev-cluster
namespace: dev
- cluster: staging-cluster
namespace: staging
- cluster: prod-cluster
namespace: production
template:
metadata:
name: '{{cluster}}-app'
spec:
project: default
source:
repoURL: https://github.com/myorg/apps
targetRevision: HEAD
path: ./app
destination:
server: 'https://{{cluster}}.example.com'
namespace: '{{namespace}}'
- Use ApplicationSets for managing many similar applications
- Combine cluster and git generators for flexible patterns
- Use label selectors to target specific clusters
- Keep templates simple and use values for customization
- Use the
preserveResourcesOnDeletionflag carefully
GitOps makes rollbacks simple—just revert to a previous Git commit. ArgoCD also provides features for history and manual rollback.
# Rollback via Git
# Revert to previous commit
git revert HEAD
git push
# Rollback via ArgoCD UI/CLI
# View application history
argocd app history nginx-app
# Rollback to specific revision
argocd app rollback nginx-app
# Force sync to specific commit
argocd app sync nginx-app --revision
# Configure sync with automatic rollback
syncPolicy:
automated:
prune: true
selfHeal: true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 30s
# Disaster Recovery - Backup Applications
# Export all applications
argocd app list -o yaml > apps-backup.yaml
# Export ApplicationSets
kubectl get applicationsets -n argocd -o yaml > appsets-backup.yaml
# Restore from backup
kubectl apply -f apps-backup.yaml
kubectl apply -f appsets-backup.yaml
# Disaster Recovery - Cluster Restore
# Export ArgoCD state
kubectl get all -n argocd -o yaml > argocd-backup.yaml
# Restore after cluster failure
kubectl apply -f argocd-backup.yaml
- Always keep a clean Git history with meaningful commit messages
- Use tags for releases to easily identify deployable versions
- Test rollback procedures regularly in staging
- Document rollback procedures for each application
- Implement automated rollback on deployment failures
ArgoCD provides comprehensive RBAC (Role-Based Access Control) to manage access to applications, projects, and cluster resources.
# ArgoCD RBAC Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.default: role:readonly
policy.csv: |
# Project-level access
p, role:admin, applications, *, default/*, allow
p, role:dev, applications, get, default/nginx, allow
p, role:dev, applications, sync, default/nginx, allow
p, role:dev, applications, create, default/nginx, allow
p, role:dev, applications, delete, default/nginx, allow
# Global access
p, role:viewer, applications, get, *, allow
p, role:viewer, projects, get, *, allow
# User bindings
g, developer-team, role:dev
g, operations-team, role:admin
g, external-auditors, role:viewer# Single Sign-On with OIDC
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
url: https://argocd.example.com
oidc.config: |
name: Okta
issuer: https://dev-123456.okta.com/oauth2/default
clientID: 0oa1abcdefghijkl
clientSecret: $oidc.okta.clientSecret
requestedScopes: ["openid", "profile", "email", "groups"]
groupsClaim: groups
# Project definitions
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Production applications
sourceRepos:
- https://github.com/myorg/production-apps
destinations:
- namespace: production
server: https://kubernetes.default.svc
- namespace: staging
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: '*'
kind: '*'
namespaceResourceBlacklist:
- group: ''
kind: ResourceQuota
roles:
- name: team-lead
policies:
- p, proj:production:team-lead, applications, get, production/*, allow
- p, proj:production:team-lead, applications, sync, production/*, allow
groups:
- lead-team
- Use the principle of least privilege for RBAC policies
- Enable OIDC or SAML for SSO integration
- Use ArgoCD Projects to enforce policies at the project level
- Restrict source repositories to trusted Git sources
- Enable audit logging for compliance
selfHeal: true, it automatically corrects drift. With manual sync, it shows the drift and allows users to decide whether to sync.argocd cluster add and deploy applications to any registered cluster. ApplicationSets make multi-cluster deployments even easier.GitOps with ArgoCD transforms Kubernetes operations by making Git the source of truth. Embrace declarative deployments, automated reconciliation, and auditability for modern, reliable infrastructure management.