Skip to content

ArgoCD Setup

This guide provides comprehensive setup instructions for ArgoCD to enable GitOps deployment of Temporal.io infrastructure and applications in enterprise environments.

Overview

ArgoCD enables GitOps deployment patterns for Temporal.io infrastructure, providing: - Declarative configuration management - Automated deployment and rollback - Multi-environment management - RBAC and security controls - Application monitoring and health checks - Integration with CI/CD pipelines

Architecture

graph TB
    subgraph "Git Repositories"
        INFRA[Infrastructure Repo]
        APP[Application Repo]
        CONFIG[Config Repo]
    end

    subgraph "ArgoCD"
        ARGOCD[ArgoCD Server]
        APPCONTROLLER[Application Controller]
        REPOSERVER[Repository Server]
        REDIS[(Redis)]
    end

    subgraph "Kubernetes Clusters"
        DEV[Development Cluster]
        STAGE[Staging Cluster]
        PROD[Production Cluster]
    end

    subgraph "Temporal Services"
        TEMPORAL[Temporal Cluster]
        WORKERS[Application Workers]
        API[FastAPI Services]
    end

    INFRA --> ARGOCD
    APP --> ARGOCD
    CONFIG --> ARGOCD

    ARGOCD --> APPCONTROLLER
    APPCONTROLLER --> REPOSERVER
    REPOSERVER --> REDIS

    APPCONTROLLER --> DEV
    APPCONTROLLER --> STAGE
    APPCONTROLLER --> PROD

    DEV --> TEMPORAL
    STAGE --> TEMPORAL
    PROD --> TEMPORAL

    TEMPORAL --> WORKERS
    TEMPORAL --> API

Prerequisites

Required Tools

# Install ArgoCD CLI on macOS
brew install argocd

# Install kubectl and helm
brew install kubectl helm

# Verify installations
argocd version --client
kubectl version --client
helm version

Cluster Requirements

  • Kubernetes 1.20+
  • 4GB+ available memory
  • 2 CPU cores minimum
  • LoadBalancer or Ingress controller
  • Persistent storage support

ArgoCD Installation

Install ArgoCD Server

Namespace Setup

# argocd/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: argocd
  labels:
    name: argocd
    app.kubernetes.io/part-of: argocd

Core Installation

#!/bin/bash
# scripts/install-argocd.sh

set -euo pipefail

NAMESPACE="argocd"
ARGOCD_VERSION="v2.8.4"

log() {
    echo -e "\033[0;32m[$(date +'%Y-%m-%d %H:%M:%S')] $1\033[0m"
}

error() {
    echo -e "\033[0;31m[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1\033[0m"
    exit 1
}

log "Installing ArgoCD ${ARGOCD_VERSION}..."

# Create namespace
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -

# Install ArgoCD
kubectl apply -n "$NAMESPACE" -f "https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml"

# Wait for ArgoCD to be ready
log "Waiting for ArgoCD to be ready..."
kubectl wait --for=condition=available deployment/argocd-server -n "$NAMESPACE" --timeout=300s
kubectl wait --for=condition=available deployment/argocd-repo-server -n "$NAMESPACE" --timeout=300s
kubectl wait --for=condition=available deployment/argocd-application-controller -n "$NAMESPACE" --timeout=300s

log "ArgoCD installation completed successfully!"

Custom ArgoCD Configuration

ArgoCD Server Configuration

# argocd/argocd-server-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-server-config
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-server-config
    app.kubernetes.io/part-of: argocd
data:
  # Server configuration
  server.config: |
    url: https://argocd.temporal.company.com
    insecure: false
    grpc.web: true

    # OIDC configuration
    oidc.config: |
      name: Company SSO
      issuer: https://auth.company.com
      clientId: argocd
      clientSecret: $argocd-oidc-secret:clientSecret
      requestedScopes: ["openid", "profile", "email", "groups"]
      requestedIDTokenClaims: {"groups": {"essential": true}}

    # URL aliases for multiple clusters
    application.instanceLabelKey: argocd.argoproj.io/instance

    # Resource customizations
    resource.customizations: |
      argoproj.io/Rollout:
        health.lua: |
          hs = {}
          if obj.status ~= nil then
            if obj.status.replicas ~= nil and obj.status.updatedReplicas ~= nil and obj.status.readyReplicas ~= nil and obj.status.availableReplicas ~= nil then
              if obj.status.replicas == obj.status.updatedReplicas and obj.status.replicas == obj.status.readyReplicas and obj.status.replicas == obj.status.availableReplicas then
                hs.status = "Healthy"
                hs.message = "Rollout is healthy"
                return hs
              end
            end
          end
          hs.status = "Progressing"
          hs.message = "Waiting for rollout to finish"
          return hs

    # Additional configuration
    accounts.temporal-admin: login
    accounts.temporal-admin.enabled: "true"

  # RBAC configuration
  policy.default: role:readonly
  policy.csv: |
    p, role:admin, applications, *, */*, allow
    p, role:admin, clusters, *, *, allow
    p, role:admin, repositories, *, *, allow

    p, role:developer, applications, get, */*, allow
    p, role:developer, applications, sync, */*, allow
    p, role:developer, applications, action/*, */*, allow
    p, role:developer, repositories, get, *, allow

    p, role:readonly, applications, get, */*, allow
    p, role:readonly, repositories, get, *, allow
    p, role:readonly, clusters, get, *, allow

    g, temporal-admins, role:admin
    g, temporal-developers, role:developer
    g, temporal-users, role:readonly

    g, temporal-admin, role:admin

ArgoCD CMD Parameters

# argocd/argocd-cmd-params.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-cmd-params-cm
    app.kubernetes.io/part-of: argocd
data:
  # Server parameters
  server.insecure: "false"
  server.grpc.web: "true"
  server.enable.proxy.extension: "true"

  # Application controller parameters
  application.controller.self.heal.timeout.seconds: "30"
  application.controller.operation.processors: "20"
  application.controller.status.processors: "20"
  application.controller.repo.server.timeout.seconds: "120"

  # Repository server parameters
  reposerver.parallelism.limit: "10"
  reposerver.init.timeout: "600"

High Availability Configuration

ArgoCD HA Setup

# argocd/argocd-ha.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-server
  namespace: argocd
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-server
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-server
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
              topologyKey: kubernetes.io/hostname
      containers:
      - name: argocd-server
        image: quay.io/argoproj/argocd:v2.8.4
        args:
        - argocd-server
        - --staticassets
        - /shared/app
        - --redis
        - argocd-redis:6379
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
  namespace: argocd
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-repo-server
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-repo-server
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-repo-server
              topologyKey: kubernetes.io/hostname
      containers:
      - name: argocd-repo-server
        image: quay.io/argoproj/argocd:v2.8.4
        args:
        - argocd-repo-server
        - --redis
        - argocd-redis:6379
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: argocd-application-controller
  namespace: argocd
spec:
  replicas: 1  # Application controller should be single instance
  serviceName: argocd-application-controller
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-application-controller
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-application-controller
    spec:
      containers:
      - name: argocd-application-controller
        image: quay.io/argoproj/argocd:v2.8.4
        args:
        - argocd-application-controller
        - --redis
        - argocd-redis:6379
        - --operation-processors
        - "20"
        - --status-processors
        - "20"
        - --kubectl-parallelism-limit
        - "30"
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"

Ingress and SSL Configuration

ArgoCD Ingress

# argocd/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-west-2:ACCOUNT:certificate/CERT-ID
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/backend-protocol: HTTPS
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
    nginx.ingress.kubernetes.io/grpc-backend: "true"
spec:
  tls:
  - hosts:
    - argocd.temporal.company.com
    secretName: argocd-server-tls
  rules:
  - host: argocd.temporal.company.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              number: 443

Security Configuration

RBAC Setup

# argocd/rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: argocd-admin
  namespace: argocd

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: argocd-admin
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
- nonResourceURLs: ["*"]
  verbs: ["*"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argocd-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argocd-admin
subjects:
- kind: ServiceAccount
  name: argocd-admin
  namespace: argocd

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: argocd-temporal-admin
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["extensions"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["argoproj.io"]
  resources: ["*"]
  verbs: ["*"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argocd-temporal-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argocd-temporal-admin
subjects:
- kind: User
  name: temporal-admin@company.com
  apiGroup: rbac.authorization.k8s.io

Secrets Management

# argocd/secrets.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: argocd-oidc-secret
  namespace: argocd
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: argocd-oidc-secret
    creationPolicy: Owner
  data:
  - secretKey: clientSecret
    remoteRef:
      key: argocd/oidc
      property: client_secret

---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: argocd-repo-credentials
  namespace: argocd
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: argocd-repo-credentials
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        type: git
        url: "{{ .repo_url }}"
        username: "{{ .username }}"
        password: "{{ .password }}"
  data:
  - secretKey: repo_url
    remoteRef:
      key: argocd/git
      property: repo_url
  - secretKey: username
    remoteRef:
      key: argocd/git
      property: username
  - secretKey: password
    remoteRef:
      key: argocd/git
      property: password

Repository Configuration

Git Repository Setup

# argocd/repositories.yaml
apiVersion: v1
kind: Secret
metadata:
  name: temporal-infrastructure-repo
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
type: Opaque
stringData:
  type: git
  url: https://github.com/company/temporal-infrastructure
  username: argocd-bot
  password: ghp_xxxxxxxxxxxxxxxxxxxx

---
apiVersion: v1
kind: Secret
metadata:
  name: temporal-applications-repo
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
type: Opaque
stringData:
  type: git
  url: https://github.com/company/temporal-applications
  username: argocd-bot
  password: ghp_xxxxxxxxxxxxxxxxxxxx

---
apiVersion: v1
kind: Secret
metadata:
  name: temporal-config-repo
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
type: Opaque
stringData:
  type: git
  url: https://github.com/company/temporal-config
  username: argocd-bot
  password: ghp_xxxxxxxxxxxxxxxxxxxx

Cluster Credentials

# argocd/clusters.yaml
apiVersion: v1
kind: Secret
metadata:
  name: temporal-development-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: temporal-development
  server: https://eks-dev.us-west-2.eks.amazonaws.com
  config: |
    {
      "bearerToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "LS0tLS1CRUdJTi..."
      }
    }

---
apiVersion: v1
kind: Secret
metadata:
  name: temporal-staging-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: temporal-staging
  server: https://eks-staging.us-west-2.eks.amazonaws.com
  config: |
    {
      "bearerToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "LS0tLS1CRUdJTi..."
      }
    }

---
apiVersion: v1
kind: Secret
metadata:
  name: temporal-production-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: temporal-production
  server: https://eks-prod.us-west-2.eks.amazonaws.com
  config: |
    {
      "bearerToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "LS0tLS1CRUdJTi..."
      }
    }

Application Projects

Temporal Project

# argocd/projects/temporal-project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: temporal
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  description: Temporal.io deployment project

  sourceRepos:
  - 'https://github.com/company/temporal-infrastructure'
  - 'https://github.com/company/temporal-applications'
  - 'https://github.com/company/temporal-config'
  - 'https://helm.temporal.io'

  destinations:
  - namespace: 'temporal-*'
    server: '*'
  - namespace: 'argocd'
    server: '*'

  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  - group: 'rbac.authorization.k8s.io'
    kind: ClusterRole
  - group: 'rbac.authorization.k8s.io'
    kind: ClusterRoleBinding
  - group: 'networking.k8s.io'
    kind: NetworkPolicy
  - group: 'policy'
    kind: PodSecurityPolicy
  - group: 'apiextensions.k8s.io'
    kind: CustomResourceDefinition

  namespaceResourceWhitelist:
  - group: ''
    kind: '*'
  - group: 'apps'
    kind: '*'
  - group: 'extensions'
    kind: '*'
  - group: 'networking.k8s.io'
    kind: '*'
  - group: 'policy'
    kind: '*'
  - group: 'autoscaling'
    kind: '*'
  - group: 'monitoring.coreos.com'
    kind: '*'
  - group: 'external-secrets.io'
    kind: '*'

  roles:
  - name: temporal-admin
    description: Admin access to Temporal project
    policies:
    - p, proj:temporal:temporal-admin, applications, *, temporal/*, allow
    - p, proj:temporal:temporal-admin, repositories, *, *, allow
    groups:
    - temporal-admins

  - name: temporal-developer
    description: Developer access to Temporal project
    policies:
    - p, proj:temporal:temporal-developer, applications, get, temporal/*, allow
    - p, proj:temporal:temporal-developer, applications, sync, temporal/*, allow
    - p, proj:temporal:temporal-developer, applications, action/*, temporal/*, allow
    groups:
    - temporal-developers

  - name: temporal-readonly
    description: Read-only access to Temporal project
    policies:
    - p, proj:temporal:temporal-readonly, applications, get, temporal/*, allow
    groups:
    - temporal-users

  syncWindows:
  - kind: allow
    schedule: '0 2 * * *'
    duration: 1h
    applications:
    - '*'
    manualSync: true

  - kind: deny
    schedule: '0 12 * * 1-5'
    duration: 8h
    applications:
    - 'temporal-production-*'
    manualSync: false

Monitoring and Observability

Prometheus ServiceMonitor

# argocd/monitoring/service-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-metrics
  namespace: argocd
  labels:
    app.kubernetes.io/component: metrics
    app.kubernetes.io/name: argocd-metrics
    app.kubernetes.io/part-of: argocd
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-metrics
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
  - port: metrics
    interval: 30s
    path: /metrics
    targetPort: 8083

---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-server-metrics
  namespace: argocd
  labels:
    app.kubernetes.io/component: server
    app.kubernetes.io/name: argocd-server-metrics
    app.kubernetes.io/part-of: argocd
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: server
      app.kubernetes.io/name: argocd-server-metrics
      app.kubernetes.io/part-of: argocd
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-repo-server-metrics
  namespace: argocd
  labels:
    app.kubernetes.io/component: repo-server
    app.kubernetes.io/name: argocd-repo-server
    app.kubernetes.io/part-of: argocd
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: repo-server
      app.kubernetes.io/name: argocd-repo-server
      app.kubernetes.io/part-of: argocd
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Grafana Dashboard

{
  "dashboard": {
    "id": null,
    "title": "ArgoCD Dashboard",
    "tags": ["argocd", "gitops"],
    "style": "dark",
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "Application Health",
        "type": "stat",
        "targets": [
          {
            "expr": "argocd_app_health_status{name=~\"temporal.*\"}",
            "legendFormat": "{{name}}"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
      },
      {
        "id": 2,
        "title": "Sync Status",
        "type": "stat",
        "targets": [
          {
            "expr": "argocd_app_sync_total{name=~\"temporal.*\"}",
            "legendFormat": "{{name}}"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0}
      },
      {
        "id": 3,
        "title": "Repository Activity",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(argocd_git_request_total[5m])",
            "legendFormat": "Git Requests/sec"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 8}
      },
      {
        "id": 4,
        "title": "Controller Performance",
        "type": "graph",
        "targets": [
          {
            "expr": "argocd_app_reconcile_bucket",
            "legendFormat": "Reconcile Time"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 8}
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "30s"
  }
}

Operations and Maintenance

Backup Configuration

#!/bin/bash
# scripts/backup-argocd.sh

set -euo pipefail

NAMESPACE="argocd"
BACKUP_DIR="/backups/argocd"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

log() {
    echo -e "\033[0;32m[$(date +'%Y-%m-%d %H:%M:%S')] $1\033[0m"
}

log "Starting ArgoCD backup..."

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Backup ArgoCD resources
kubectl get applications -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/applications-${TIMESTAMP}.yaml"
kubectl get appprojects -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/appprojects-${TIMESTAMP}.yaml"
kubectl get secrets -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/secrets-${TIMESTAMP}.yaml"
kubectl get configmaps -n "$NAMESPACE" -o yaml > "$BACKUP_DIR/configmaps-${TIMESTAMP}.yaml"

# Backup to S3
tar -czf "$BACKUP_DIR/argocd-backup-${TIMESTAMP}.tar.gz" -C "$BACKUP_DIR" .
aws s3 cp "$BACKUP_DIR/argocd-backup-${TIMESTAMP}.tar.gz" "s3://temporal-backups/argocd/"

# Clean up local files older than 7 days
find "$BACKUP_DIR" -name "*.yaml" -mtime +7 -delete
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete

log "ArgoCD backup completed: argocd-backup-${TIMESTAMP}.tar.gz"

Health Check Script

#!/bin/bash
# scripts/health-check-argocd.sh

set -euo pipefail

NAMESPACE="argocd"

log() {
    echo -e "\033[0;32m[$(date +'%Y-%m-%d %H:%M:%S')] $1\033[0m"
}

error() {
    echo -e "\033[0;31m[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1\033[0m"
}

warn() {
    echo -e "\033[1;33m[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1\033[0m"
}

log "Running ArgoCD health checks..."

# Check pod status
PODS_NOT_READY=$(kubectl get pods -n "$NAMESPACE" -o jsonpath='{.items[?(@.status.phase!="Running")].metadata.name}')
if [[ -n "$PODS_NOT_READY" ]]; then
    warn "Pods not ready: $PODS_NOT_READY"
else
    log "✓ All pods are running"
fi

# Check ArgoCD server health
kubectl run argocd-health --image=curlimages/curl:latest --rm -i --restart=Never -- \
    curl -f "http://argocd-server.${NAMESPACE}.svc.cluster.local:80/healthz" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
    log "✓ ArgoCD server health check passed"
else
    error "✗ ArgoCD server health check failed"
fi

# Check application sync status
UNHEALTHY_APPS=$(argocd app list --output json | jq -r '.[] | select(.status.health.status != "Healthy") | .metadata.name' || true)
if [[ -n "$UNHEALTHY_APPS" ]]; then
    warn "Unhealthy applications: $UNHEALTHY_APPS"
else
    log "✓ All applications are healthy"
fi

log "ArgoCD health check completed"

This comprehensive ArgoCD setup guide provides enterprise-grade GitOps capabilities for Temporal.io deployments with high availability, security, monitoring, and operational automation.