41 pages Β· 8 sections
Ctrl K
GitHub Portfolio

GitOps β€” Flux & ArgoCD

GitOps uses Git repositories as the single source of truth for declarative infrastructure and applications, enabling automated synchronization and drift detection. This guide covers both ArgoCD and Flux with production patterns from large-scale Kubernetes deployments.

GitOps Principles

GitOps, coined by Weaveworks in 2017, applies Git workflows to operations. The core principles:

  1. Declarative Configuration: All infrastructure and application state is described declaratively (Kubernetes manifests, Helm charts, Kustomize overlays) and stored in Git.
  2. Git as Single Source of Truth: The Git repository contains the desired state. The running system continuously converges to match Git.
  3. Automated Synchronization: An agent (ArgoCD or Flux) watches Git and automatically applies changes to the cluster.
  4. Drift Detection and Reconciliation: Any manual changes to the cluster are detected and reverted to match Git. The desired state always wins.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        GitOps Flow                           β”‚
β”‚                                                             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    PR/Merge    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Sync          β”‚
β”‚   β”‚ Developer│───────────────▢│  Git     │──────────┐       β”‚
β”‚   β”‚ (kubectl)β”‚                β”‚ (Source  β”‚          β”‚       β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚  of Truthβ”‚          β–Ό       β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚                                               β”‚ ArgoCD / β”‚  β”‚
β”‚                                               β”‚ Flux     β”‚  β”‚
β”‚                                               β”‚ (Agent)  β”‚  β”‚
β”‚                                               β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                    β”‚         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚   β–Ό                                                         β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Drift Detection    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚ β”‚  Kubernetes  │◀─────────────────────│  Reconcileβ”‚         β”‚
β”‚ β”‚  Cluster     β”‚    (auto-heal)       β”‚  Loop     β”‚         β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                                             β”‚
β”‚ Rule: NEVER use kubectl apply manually in production.       β”‚
β”‚ All changes go through Git β†’ PR β†’ Merge β†’ Automated Sync    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Flux vs. ArgoCD Comparison

CapabilityFluxArgoCD
GitOps EngineCNCF Graduated (v2)CNCF Incubating
ArchitectureNative Kubernetes operators (in-cluster)Application controller + API server + UI
Web UIWeave GitOps (separate) or CLI onlyBuilt-in, excellent UI
Multi-clusterCluster API + BootstrapNative multi-cluster management
Multi-sourceOCI + Git + S3 + Helm reposGit + Helm + S3 + GCS + OCI
Helm supportNative Helm ControllerNative Helm support
Image automationImageUpdateAutomation + ImagePolicyArgoCD Image Updater
Progressive deliveryFlagger (separate)Argo Rollouts (separate)
Secrets managementSOPS + External SecretsSealed Secrets + External Secrets + SOPS
NotificationNotification ControllerNotifications + Webhooks
App of Apps patternSupports via KustomizeNative (Applications in Application)
RBACKubernetes RBACBuilt-in RBAC + SSO integration
Drift detectionContinuous reconciliationAuto-sync + Visual diff in UI
Resource health assessmentHealth checks in eventsVisual health indicators + custom health checks
Selection Guidance: Choose ArgoCD if you need a rich UI, multi-cluster management, and progressive delivery (Argo Rollouts). Choose Flux if you prefer a Git-native, operator-only approach with tight Kubernetes integration. Many organizations run both: ArgoCD for application delivery and Flux for infrastructure (cluster add-ons, platform components).

ArgoCD Installation and Setup

Install ArgoCD

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

# Or install with Helm
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd \
    --namespace argocd \
    --create-namespace \
    --set server.service.type=LoadBalancer \
    --set server.ingress.enabled=true \
    --set server.ingress.hostname=argocd.example.com

# Wait for pods
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=120s

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

# Port-forward to access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Access: https://localhost:8080 (login: admin / password from above)

# CLI login
argocd login localhost:8080 --username admin --password $(kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d) --insecure

Configure ArgoCD

# ── Configure SSO (GitHub OAuth) ─────────────────────────
# Edit argocd-cm ConfigMap
kubectl patch configmap argocd-cm -n argocd --type merge -p '{
  "data": {
    "url": "https://argocd.example.com",
    "dex.config": "connectors:\n- type: github\n  id: github\n  name: GitHub\n  config:\n    clientID: $GITHUB_CLIENT_ID\n    clientSecret: $GITHUB_CLIENT_SECRET\n    organizations:\n    - my-org"
  }
}'

# ── Configure RBAC ───────────────────────────────────────
kubectl patch configmap argocd-rbac-cm -n argocd --type merge -p '{
  "data": {
    "policy.default": "role:readonly",
    "policy.csv": "p, role:admin, applications, *, *, allow\np, role:admin, clusters, *, *, allow\np, role:admin, repositories, *, *, allow\ng, my-org:platform-team, role:admin\ng, my-org:developers, role:readonly"
  }
}'

# ── Add a cluster ────────────────────────────────────────
# ArgoCD auto-detects the local cluster
# Add external EKS cluster:
argocd cluster add arn:aws:eks:us-east-1:123456789012:cluster/production-cluster \
    --name production \
    --yes

Complete Application Manifest Example

# argocd/application.yaml β€” ArgoCD Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payment-service
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io  # Cascade delete on app removal
  labels:
    app.kubernetes.io/name: payment-service
    environment: production
    team: payments
spec:
  project: production

  # ── Source ─────────────────────────────────────────────
  source:
    repoURL: https://github.com/my-org/gitops-apps.git
    targetRevision: main
    path: apps/payment-service/overlays/production

    # Helm-specific configuration
    helm:
      valueFiles:
        - values-production.yaml
      parameters:
        - name: replicaCount
          value: "5"
        - name: autoscaling.maxReplicas
          value: "20"

    # Or Kustomize
    # kustomize:
    #   namePrefix: prod-
    #   commonLabels:
    #     environment: production

  # ── Destination ────────────────────────────────────────
  destination:
    server: https://kubernetes.default.svc  # In-cluster
    namespace: production

  # ── Sync Policy ────────────────────────────────────────
  syncPolicy:
    automated:
      prune: true              # Delete resources not in Git
      selfHeal: true           # Revert manual changes to cluster
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
      - ApplyOutOfSyncOnly=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

  # ── Ignore Differences ─────────────────────────────────
  # Fields that ArgoCD should ignore when comparing
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas  # Ignore HPA-managed replicas
    - group: ""
      kind: Secret
      managedFieldsManagers:
        - external-secrets  # Ignore fields managed by ESO

  # ── Revision History ───────────────────────────────────
  revisionHistoryLimit: 10

App of Apps Pattern

The App of Apps pattern creates a single ArgoCD Application that manages other Applications:

# argocd/apps/production-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-apps
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/my-org/gitops-apps.git
    targetRevision: main
    directory:
      recurse: true
      jsonnet: {}
    path: argocd/apps/production
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

# File structure in Git:
# argocd/apps/production/
# β”œβ”€β”€ payment-service.yaml     # Individual Application manifests
# β”œβ”€β”€ user-service.yaml
# β”œβ”€β”€ notification-service.yaml
# └── ingress-controller.yaml

Auto-sync and Prune Settings

SettingDescriptionRecommended
automated.pruneDelete resources removed from Gittrue (with PruneLast sync option)
automated.selfHealRevert manual changes in clustertrue for production
syncOptions.CreateNamespaceAuto-create namespace if missingtrue
syncOptions.Validate=trueRun kubectl validation before applytrue
syncOptions.ApplyOutOfSyncOnlyOnly apply changed resourcestrue (faster syncs)
syncOptions.PruneLastDelete resources after all createstrue (safer rollouts)

Multi-Source Applications

ArgoCD 2.6+ supports multiple sources for a single Application, useful for combining a base Helm chart with environment-specific values:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payment-service
  namespace: argocd
spec:
  project: production
  sources:
    # Source 1: Helm chart from a Helm repository
    - repoURL: https://charts.mycompany.io
      chart: payment-service
      targetRevision: 1.2.3
      helm:
        valueFiles:
          - $values/apps/payment-service/overlays/production/values.yaml

    # Source 2: Environment-specific values from Git
    - repoURL: https://github.com/my-org/gitops-values.git
      targetRevision: main
      ref: values

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

  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

ArgoCD with Helm

# ArgoCD Application for a Helm chart stored in Git
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ingress-nginx
  namespace: argocd
spec:
  project: infrastructure
  source:
    repoURL: https://kubernetes.github.io/ingress-nginx
    chart: ingress-nginx
    targetRevision: 4.8.0
    helm:
      releaseName: ingress-nginx
      values: |
        controller:
          replicaCount: 2
          service:
            annotations:
              service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
              service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 1000m
              memory: 512Mi
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

ArgoCD Image Updater for Automated Deployments

ArgoCD Image Updater automatically updates container image tags in Git when new images are pushed:

# Install ArgoCD Image Updater
helm install argocd-image-updater argo/argocd-image-updater \
    --namespace argocd \
    --set config.registries[0].name=ECR \
    --set config.registries[0].api_url=https://123456789012.dkr.ecr.us-east-1.amazonaws.com \
    --set config.registries[0].prefix=123456789012.dkr.ecr.us-east-1.amazonaws.com \
    --set config.registries[0].credentials=ext:/scripts/ecr-login.sh

# Annotate the Application to enable image updates
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payment-service
  namespace: argocd
  annotations:
    # Enable image updater for this application
    argocd-image-updater.argoproj.io/image-list: "payment=123456789012.dkr.ecr.us-east-1.amazonaws.com/payment-service"

    # Update strategy: semver (semantic versioning)
    argocd-image-updater.argoproj.io/payment.update-strategy: semver
    argocd-image-updater.argoproj.io/payment.allow-tags: regexp:^v[0-9]+\.[0-9]+\.[0-9]+$

    # Write updates back to Git (write-back method)
    argocd-image-updater.argoproj.io/write-back-method: git:secret:argocd/git-creds
    argocd-image-updater.argoproj.io/git-branch: main

    # Alternatively: update the image tag in the Application spec (Kustomize)
    # argocd-image-updater.argoproj.io/write-back-method: kustomize
spec:
  source:
    repoURL: https://github.com/my-org/gitops-apps.git
    targetRevision: main
    path: apps/payment-service/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Progressive Delivery with Argo Rollouts

Argo Rollouts extends Deployment capabilities with progressive delivery strategies:

# Install Argo Rollouts
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# Canary deployment with automated analysis
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: payment-service
  namespace: production
spec:
  replicas: 10
  strategy:
    canary:
      maxSurge: "25%"
      maxUnavailable: 0
      steps:
        # Step 1: Deploy 10% of traffic to canary
        - setWeight: 10
        # Step 2: Wait 10 minutes and run analysis
        - pause: { duration: 10m }
        - analysis:
            templates:
              - templateName: success-rate
        # Step 3: Increase to 25%
        - setWeight: 25
        - pause: { duration: 10m }
        - analysis:
            templates:
              - templateName: success-rate
              - templateName: latency-check
        # Step 4: Increase to 50%
        - setWeight: 50
        - pause: { duration: 10m }
        - analysis:
            templates:
              - templateName: success-rate
        # Step 5: Full rollout (100%)
        - setWeight: 100

      # Traffic routing with Istio or ALB Ingress
      canaryService: payment-service-canary
      stableService: payment-service-stable
      trafficRouting:
        alb:
          ingress:
            ingressName: payment-service
            servicePort: 80

  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
        - name: payment-service
          image: "123456789012.dkr.ecr.us-east-1.amazonaws.com/payment-service:v1.2.3"
          ports:
            - containerPort: 8080

---
# Automated analysis template
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
  namespace: production
spec:
  metrics:
    - name: success-rate
      interval: 5m
      successCondition: result[0] >= 0.99
      provider:
        prometheus:
          address: http://prometheus.monitoring.svc.cluster.local:9090
          query: |
            sum(rate(http_requests_total{service="payment-service",status!~"5.."}[5m]))
            /
            sum(rate(http_requests_total{service="payment-service"}[5m]))

Secrets Management in GitOps

Secrets must not be stored in plain text in Git. Three approaches for GitOps environments:

SolutionEncryptionRotationSetup ComplexityBest For
Sealed SecretsClient-side (asymmetric)ManualLowSimple clusters, few secrets
External Secrets OperatorExternal vaultAutomaticMediumEnterprise, many secrets
SOPSCloud KMS (AWS/GCP/Azure)ManualMediumFlux-native, multi-key access

Sealed Secrets

# Install Sealed Secrets controller
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets \
    --namespace kube-system

# Install kubeseal CLI
brew install kubeseal  # macOS
# wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/kubeseal-0.24.0-linux-amd64.tar.gz

# Create a secret and seal it
cat <<EOF > secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: payment-service-secrets
  namespace: production
type: Opaque
stringData:
  db_password: "SuperSecret123!"
  api_key: "sk-live-abcdef123456"
EOF

kubeseal --controller-namespace=kube-system \
    --controller-name=sealed-secrets \
    --format yaml < secret.yaml > sealed-secret.yaml

# sealed-secret.yaml is safe to commit to Git!
kubectl apply -f sealed-secret.yaml

# The controller automatically decrypts to a regular Secret

External Secrets Operator

# Install External Secrets Operator
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
    --namespace external-secrets \
    --create-namespace \
    --set installCRDs=true

# Create a SecretStore (namespace-scoped) or ClusterSecretStore
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets

---
# Create an ExternalSecret that syncs from AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-service-secrets
  namespace: production
spec:
  refreshInterval: 1h  # Sync every hour
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-secrets-manager
  target:
    name: payment-service-secrets
    creationPolicy: Owner
    template:
      type: Opaque
  data:
    - secretKey: db_password
      remoteRef:
        key: production/payment-service/db
        property: password
    - secretKey: db_host
      remoteRef:
        key: production/payment-service/db
        property: host
    - secretKey: api_key
      remoteRef:
        key: production/payment-service/api
        property: key

End-to-End Example: Git Repo to ArgoCD to Kubernetes

This complete example shows the full GitOps workflow from code commit to production deployment:

Git Repository Structure

gitops-platform/                    # Infrastructure (managed by platform team)
β”œβ”€β”€ argocd/
β”‚   β”œβ”€β”€ projects/                   # ArgoCD projects
β”‚   β”‚   β”œβ”€β”€ production.yaml
β”‚   β”‚   └── staging.yaml
β”‚   └── clusters/                   # Cluster registrations
β”‚       β”œβ”€β”€ production.yaml
β”‚       └── staging.yaml
β”œβ”€β”€ system/                         # Cluster-level components
β”‚   β”œβ”€β”€ ingress-nginx/
β”‚   β”‚   β”œβ”€β”€ application.yaml
β”‚   β”‚   └── values.yaml
β”‚   β”œβ”€β”€ cert-manager/
β”‚   β”œβ”€β”€ external-secrets/
β”‚   β”œβ”€β”€ monitoring/
β”‚   └── kyverno/
└── policies/
    └── kyverno-policies.yaml

gitops-apps/                        # Application manifests (managed by dev teams)
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ payment-service/
β”‚   β”‚   β”œβ”€β”€ base/                   # Base Kustomize layer
β”‚   β”‚   β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ service.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ ingress.yaml
β”‚   β”‚   β”‚   └── kustomization.yaml
β”‚   β”‚   └── overlays/
β”‚   β”‚       β”œβ”€β”€ production/
β”‚   β”‚       β”‚   β”œβ”€β”€ replicas.yaml
β”‚   β”‚       β”‚   β”œβ”€β”€ resource-limits.yaml
β”‚   β”‚       β”‚   β”œβ”€β”€ ingress-patch.yaml
β”‚   β”‚       β”‚   └── kustomization.yaml
β”‚   β”‚       └── staging/
β”‚   β”‚           └── ...
β”‚   β”œβ”€β”€ user-service/
β”‚   └── notification-service/
└── argocd/
    └── apps/
        β”œβ”€β”€ production/             # App of Apps
        β”‚   β”œβ”€β”€ payment-service.yaml
        β”‚   β”œβ”€β”€ user-service.yaml
        β”‚   └── kustomization.yaml
        └── staging/
            └── ...

Kustomize Configuration

# apps/payment-service/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: payment-service

resources:
  - deployment.yaml
  - service.yaml
  - ingress.yaml

commonLabels:
  app.kubernetes.io/name: payment-service
  app.kubernetes.io/part-of: payments-platform

images:
  - name: payment-service
    newName: 123456789012.dkr.ecr.us-east-1.amazonaws.com/payment-service

# apps/payment-service/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: production

namePrefix: prod-

resources:
  - ../../base

patches:
  - path: replicas.yaml
  - path: resource-limits.yaml
  - path: ingress-patch.yaml

replicas:
  - name: payment-service
    count: 5

images:
  - name: payment-service
    newTag: v1.2.3

Complete Workflow

  1. Developer commits code change to payment-service repository
  2. CI pipeline triggers: GitHub Actions builds Docker image, runs tests, pushes to ECR with tag v1.2.4
  3. Git commit: Developer updates image tag in gitops-apps/apps/payment-service/overlays/production/kustomization.yaml from v1.2.3 to v1.2.4
  4. Pull Request created: Changes reviewed by team; GitOps diff shown in PR via ArgoCD integration
  5. PR merged to main: ArgoCD detects change within 3 minutes (default poll interval)
  6. ArgoCD syncs: Application shows OutOfSync β†’ Syncing β†’ Synced β†’ Healthy
  7. Rolling update: Kubernetes gradually replaces pods with new version; HPA maintains desired replicas
  8. Monitoring confirms: Grafana dashboards show healthy deployment; Prometheus alerts verify SLOs
# Verify sync in ArgoCD UI or CLI
argocd app get payment-service
# Name:               payment-service
# Project:            production
# Server:             https://kubernetes.default.svc
# Namespace:          production
# URL:                https://argocd.example.com/applications/payment-service
# Repo:               https://github.com/my-org/gitops-apps
# Target:             main
# Path:               apps/payment-service/overlays/production
# SyncWindow:         Sync Allowed
# Sync Policy:        Automated (Prune Resources, Self Heal)
# Sync Status:        Synced to main (abc1234)
# Health Status:      Healthy

# Last sync details
argocd app history payment-service
# ID  DATE                           REVISION
# 0   2024-01-15 10:30:00 UTC        main (abc1234)
# 1   2024-01-15 08:15:00 UTC        main (def5678)

# Force a sync (if needed)
argocd app sync payment-service