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
- Declarative GitOps: Git as single source of truth
- Automated Deployment: Automatic sync from Git to Kubernetes
- Multi-Cluster Management: Manage multiple clusters from single instance
- Application Health: Real-time health status monitoring
- Rollback: Easy rollback to any Git commit
- SSO Integration: OIDC, SAML, GitHub, GitLab authentication
- RBAC: Fine-grained access control
- Webhooks: GitHub/GitLab integration for automated sync
- CLI & UI: Both command-line and web interface
- 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
# Create namespacekubectl create namespace argocd
# Install ArgoCDkubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods to be readykubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
# Get initial admin passwordkubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -d
# Port forward to access UIkubectl port-forward svc/argocd-server -n argocd 8080:443
# Access at https://localhost:8080# Username: admin# Password: (from above command)Using Helm
# Add ArgoCD Helm repositoryhelm repo add argo https://argoproj.github.io/argo-helmhelm repo update
# Create values filecat > 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: trueEOF
# Install with Helmhelm install argocd argo/argo-cd \ -n argocd \ --create-namespace \ -f argocd-values.yamlInstall CLI
# macOSbrew install argocd
# Linuxcurl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64chmod +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
# Verifyargocd versionCLI Configuration
# Login to ArgoCDargocd login argocd.example.com
# Or with port-forwardargocd login localhost:8080 --insecure
# Login with tokenargocd login argocd.example.com --auth-token $TOKEN
# Update admin passwordargocd account update-password
# List clustersargocd cluster list
# Add clusterargocd cluster add my-cluster-context
# List repositoriesargocd repo list
# Add repositoryargocd repo add https://github.com/example/gitops-repo \ --username git \ --password $GITHUB_TOKENCreating Applications
Using CLI
# Create applicationargocd 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 Helmargocd 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 Kustomizeargocd 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 applicationargocd app sync my-app
# Get application statusargocd app get my-app
# List applicationsargocd app list
# Delete applicationargocd app delete my-appUsing YAML Manifest
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: my-app namespace: argocd # Finalizer that ensures cascading deletes finalizers: - resources-finalizer.argocd.argoproj.iospec: # 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: 10Apply the application:
kubectl apply -f application.yamlApplication Management
Sync Strategies
# Manual syncargocd app sync my-app
# Auto-sync (in application manifest)syncPolicy: automated: prune: true selfHeal: true
# Sync with optionsargocd app sync my-app \ --prune \ --force \ --async
# Sync specific resourcesargocd app sync my-app \ --resource apps:Deployment:my-deployment
# Selective syncargocd app sync my-app \ --label app.kubernetes.io/component=apiRollback
# List revision historyargocd app history my-app
# Rollback to previous revisionargocd app rollback my-app
# Rollback to specific revisionargocd app rollback my-app 5
# Sync to specific Git commitargocd app sync my-app --revision abc123Health and Status
# Get application healthargocd app get my-app
# Watch application statusargocd app wait my-app --health
# Get sync statusargocd app sync-status my-app
# List application resourcesargocd app resources my-app
# Get application logsargocd app logs my-app
# Diff between Git and clusterargocd app diff my-appProjects
Create Project
apiVersion: argoproj.io/v1alpha1kind: AppProjectmetadata: name: production namespace: argocdspec: 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: - adminsApply project:
kubectl apply -f project.yamlMulti-Cluster Management
Add Cluster
# List available contextskubectl config get-contexts
# Add cluster to ArgoCDargocd cluster add staging-cluster
# Add with specific namespaceargocd cluster add prod-cluster \ --namespace argocd \ --name production-cluster
# Add with labelsargocd cluster add cluster-name \ --label env=production \ --label region=us-eastDeploy to Multiple Clusters
# Application for multiple clustersapiVersion: argoproj.io/v1alpha1kind: ApplicationSetmetadata: name: multi-cluster-app namespace: argocdspec: 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: trueApplicationSet
Git Generator
apiVersion: argoproj.io/v1alpha1kind: ApplicationSetmetadata: name: cluster-apps namespace: argocdspec: 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=trueCluster Generator
apiVersion: argoproj.io/v1alpha1kind: ApplicationSetmetadata: name: all-clusters-app namespace: argocdspec: 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: trueSSO Integration
GitHub OAuth
# argocd-cm ConfigMapapiVersion: v1kind: ConfigMapmetadata: name: argocd-cm namespace: argocddata: 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 - adminsOIDC (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 - groupsRBAC
Configure RBAC
# argocd-rbac-cm ConfigMapapiVersion: v1kind: ConfigMapmetadata: name: argocd-rbac-cm namespace: argocddata: 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:adminNotifications
Configure Notifications
# Install notifications controller (if not installed)kubectl apply -n argocd \ -f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/stable/manifests/install.yamlSlack Notifications
# argocd-notifications-cm ConfigMapapiVersion: v1kind: ConfigMapmetadata: name: argocd-notifications-cm namespace: argocddata: 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-deployedApplication Annotation
metadata: annotations: notifications.argoproj.io/subscribe.on-deployed.slack: deploymentsBest Practices
Repository Structure
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.yamlApp of Apps Pattern
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: root-app namespace: argocdspec: 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: trueHealth Checks
# Custom health checkresource.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 hsMonitoring
Prometheus Metrics
# ServiceMonitor for ArgoCDapiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: argocd-metrics namespace: argocdspec: selector: matchLabels: app.kubernetes.io/name: argocd-metrics endpoints: - port: metricsKey Metrics
argocd_app_info: Application infoargocd_app_sync_total: Sync countargocd_app_k8s_request_total: API requestsargocd_cluster_info: Cluster infoargocd_git_request_total: Git requests
Troubleshooting
# Check ArgoCD podskubectl get pods -n argocd
# View application controller logskubectl logs -n argocd deployment/argocd-application-controller
# View repo server logskubectl logs -n argocd deployment/argocd-repo-server
# View server logskubectl logs -n argocd deployment/argocd-server
# Debug application syncargocd app sync my-app --dry-runargocd app diff my-app
# Refresh applicationargocd app get my-app --refresh
# Hard refresh (bypass cache)argocd app get my-app --hard-refreshConclusion
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.