DevOps 6 min read

ArgoCD: GitOps Continuous Delivery for Kubernetes

Master ArgoCD for Kubernetes deployments. Learn GitOps principles, application management, sync strategies, and automate your deployment pipeline.

MR

Moshiour Rahman

Advertisement

What is ArgoCD?

ArgoCD is a declarative GitOps continuous delivery tool for Kubernetes. It automates application deployment by continuously monitoring Git repositories and syncing the desired state to your cluster.

GitOps Principles

PrincipleDescription
DeclarativeDefine desired state in Git
VersionedAll changes tracked
AutomatedAuto-sync to cluster
AuditableComplete change history

Getting Started

Installation

# Create namespace
kubectl create namespace argocd

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

# Install CLI
# macOS
brew install argocd

# Linux
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd

Access ArgoCD

# Port forward to UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

# Login via CLI
argocd login localhost:8080 --username admin --password <password> --insecure

# Change password
argocd account update-password

Application Management

Create Application (CLI)

# Create app from Git repository
argocd app create my-app \
  --repo https://github.com/org/repo.git \
  --path kubernetes \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace default

# From Helm chart
argocd app create my-helm-app \
  --repo https://charts.example.com \
  --helm-chart my-chart \
  --revision 1.0.0 \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace default

# With Helm values
argocd app create my-helm-app \
  --repo https://github.com/org/repo.git \
  --path charts/my-app \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace production \
  --values values-prod.yaml

Application YAML

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://github.com/org/repo.git
    targetRevision: HEAD
    path: kubernetes

  destination:
    server: https://kubernetes.default.svc
    namespace: default

  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
kubectl apply -f application.yaml

Helm Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prometheus
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://prometheus-community.github.io/helm-charts
    chart: prometheus
    targetRevision: 15.0.0

    helm:
      releaseName: prometheus
      values: |
        server:
          persistentVolume:
            enabled: true
            size: 10Gi
        alertmanager:
          enabled: false

  destination:
    server: https://kubernetes.default.svc
    namespace: monitoring

Kustomize Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-kustomize-app
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://github.com/org/repo.git
    targetRevision: HEAD
    path: overlays/production

    kustomize:
      images:
        - myapp=registry.example.com/myapp:v1.0.0

  destination:
    server: https://kubernetes.default.svc
    namespace: production

Sync Strategies

Manual Sync

# Sync application
argocd app sync my-app

# Sync with prune
argocd app sync my-app --prune

# Dry run
argocd app sync my-app --dry-run

# Force sync
argocd app sync my-app --force

Automated Sync

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  # ... source and destination

  syncPolicy:
    automated:
      prune: true        # Delete resources not in Git
      selfHeal: true     # Revert manual changes
      allowEmpty: false  # Don't sync empty manifests

    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true

Sync Waves and Hooks

# Namespace first (wave -1)
apiVersion: v1
kind: Namespace
metadata:
  name: my-app
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
---
# ConfigMap second (wave 0)
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  annotations:
    argocd.argoproj.io/sync-wave: "0"
---
# Deployment last (wave 1)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  annotations:
    argocd.argoproj.io/sync-wave: "1"

Sync Hooks

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: myapp:latest
          command: ["./migrate.sh"]
      restartPolicy: Never

Projects

Create Project

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  description: Production applications

  # Source repositories
  sourceRepos:
    - 'https://github.com/org/*'
    - 'https://charts.example.com'

  # Destination clusters/namespaces
  destinations:
    - namespace: 'production-*'
      server: https://kubernetes.default.svc
    - namespace: production
      server: https://kubernetes.default.svc

  # Allowed cluster resources
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
    - group: 'rbac.authorization.k8s.io'
      kind: ClusterRole
    - group: 'rbac.authorization.k8s.io'
      kind: ClusterRoleBinding

  # Denied namespace resources
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota
    - group: ''
      kind: LimitRange

  # Role definitions
  roles:
    - name: developer
      description: Developer access
      policies:
        - p, proj:production:developer, applications, get, production/*, allow
        - p, proj:production:developer, applications, sync, production/*, allow
      groups:
        - developers

Multi-Cluster

Add Cluster

# Add cluster
argocd cluster add my-cluster-context

# List clusters
argocd cluster list

# Remove cluster
argocd cluster rm https://my-cluster-api-server

Multi-Cluster Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app-prod
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://github.com/org/repo.git
    targetRevision: HEAD
    path: overlays/production

  destination:
    server: https://production-cluster-api:6443
    namespace: my-app

ApplicationSets

Generator Types

# List Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-app
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: staging
            url: https://staging-cluster:6443
          - cluster: production
            url: https://production-cluster:6443

  template:
    metadata:
      name: 'my-app-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/repo.git
        targetRevision: HEAD
        path: 'overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: my-app
# Git Generator (directories)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/org/apps.git
        revision: HEAD
        directories:
          - path: apps/*

  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/apps.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'
# Cluster Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            environment: production

  template:
    metadata:
      name: 'addons-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/cluster-addons.git
        targetRevision: HEAD
        path: addons
      destination:
        server: '{{server}}'
        namespace: kube-system

Secrets Management

Sealed Secrets

# Install Sealed Secrets controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml

# Install kubeseal CLI
brew install kubeseal

# Create sealed secret
kubectl create secret generic my-secret \
  --from-literal=password=mysecret \
  --dry-run=client -o yaml | \
  kubeseal --format yaml > sealed-secret.yaml

External Secrets

# Install External Secrets Operator
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: external-secrets
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://charts.external-secrets.io
    chart: external-secrets
    targetRevision: 0.9.0
  destination:
    server: https://kubernetes.default.svc
    namespace: external-secrets
  syncPolicy:
    automated:
      selfHeal: true
# External Secret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-secret
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: my-secret
  data:
    - secretKey: password
      remoteRef:
        key: my-app/credentials
        property: password

Notifications

# ConfigMap for notifications
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  template.app-sync-succeeded: |
    message: |
      Application {{.app.metadata.name}} sync succeeded.
      Revision: {{.app.status.sync.revision}}
  trigger.on-sync-succeeded: |
    - when: app.status.operationState.phase in ['Succeeded']
      send: [app-sync-succeeded]

CLI Commands

# App management
argocd app list
argocd app get my-app
argocd app sync my-app
argocd app delete my-app

# History and rollback
argocd app history my-app
argocd app rollback my-app <revision>

# Diff
argocd app diff my-app

# Logs
argocd app logs my-app

# Resource tree
argocd app resources my-app

Summary

FeatureDescription
ApplicationsDefine deployments
ProjectsRBAC and restrictions
ApplicationSetsMulti-cluster/app
Sync PoliciesAuto/manual sync
HooksPre/post sync jobs
NotificationsSlack, email alerts

ArgoCD enables declarative, GitOps-based continuous delivery for Kubernetes applications.

Advertisement

MR

Moshiour Rahman

Software Architect & AI Engineer

Share:
MR

Moshiour Rahman

Software Architect & AI Engineer

Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.

Related Articles

Comments

Comments are powered by GitHub Discussions.

Configure Giscus at giscus.app to enable comments.