GitOps Workflow Patterns
Implement advanced GitOps workflows for multi-cluster, multi-environment deployments. Covers ArgoCD ApplicationSets, Flux Kustomizations, progressive delivery, drift detection, secret management, and the operational patterns that make GitOps reliable at scale.
GitOps uses Git as the single source of truth for declarative infrastructure and applications. Every change goes through a PR, is reviewed, merged, and then automatically reconciled by a GitOps controller. The running state always matches the desired state in Git.
GitOps Principles
1. Declarative: Desired state is described, not scripted
2. Versioned: All changes tracked in Git history
3. Automated: Reconciliation happens continuously
4. Observable: Drift detection and real-time status
Git Repository (desired state)
↓ (controller watches)
GitOps Controller (ArgoCD / Flux)
↓ (reconciles)
Kubernetes Cluster (actual state)
↓ (if drift detected)
Auto-remediate OR alert
ArgoCD Configuration
Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service
namespace: argocd
spec:
project: production
source:
repoURL: https://github.com/org/k8s-manifests.git
targetRevision: main
path: clusters/production/order-service
destination:
server: https://kubernetes.default.svc
namespace: order-service
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual changes
syncOptions:
- CreateNamespace=true
retry:
limit: 3
backoff:
duration: 5s
maxDuration: 3m
ApplicationSet (Multi-Cluster)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: order-service
spec:
generators:
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: 'order-service-{{name}}'
spec:
source:
repoURL: https://github.com/org/k8s-manifests.git
path: 'clusters/{{name}}/order-service'
destination:
server: '{{server}}'
namespace: order-service
Repository Structure
k8s-manifests/
├── base/ # Shared base manifests
│ ├── order-service/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ ├── hpa.yaml
│ │ └── kustomization.yaml
│ └── payment-service/
│ └── ...
├── clusters/
│ ├── production/
│ │ ├── order-service/
│ │ │ ├── kustomization.yaml # Patches for production
│ │ │ └── patches/
│ │ │ ├── replicas.yaml # 5 replicas
│ │ │ └── resources.yaml # 2Gi memory
│ │ └── ...
│ ├── staging/
│ │ └── order-service/
│ │ ├── kustomization.yaml # Patches for staging
│ │ └── patches/
│ │ ├── replicas.yaml # 2 replicas
│ │ └── resources.yaml # 512Mi memory
│ └── development/
│ └── ...
└── apps/
├── production.yaml # ArgoCD Application definitions
└── staging.yaml
Progressive Delivery
# ArgoCD Rollouts (Canary)
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: order-service
spec:
strategy:
canary:
steps:
- setWeight: 5 # 5% traffic to new version
- pause: {duration: 5m}
- analysis:
templates:
- templateName: error-rate
- setWeight: 25
- pause: {duration: 10m}
- analysis:
templates:
- templateName: error-rate
- setWeight: 50
- pause: {duration: 15m}
- setWeight: 100
analysisRunArgs:
- name: service-name
value: order-service
Drift Detection
ArgoCD sync states:
Synced: Actual matches desired (green)
OutOfSync: Actual differs from desired (yellow)
Degraded: Resources unhealthy (red)
Missing: Resources not yet created (yellow)
Self-heal behavior:
Manual kubectl edit → ArgoCD reverts to Git state within seconds
This enforces "Git is the source of truth" absolutely
Exception: Some resources have ignoreDifferences
(e.g., HPA replicas that auto-scale independently)
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| kubectl apply in production | Bypass Git, no audit trail | Self-heal mode reverts manual changes |
| Secrets in Git | Credential exposure | Sealed Secrets, External Secrets Operator |
| One repo for all (mono-repo overwhelm) | Slow sync, blast radius | Per-team or per-cluster repos |
| No automated sync | GitOps = manual kubectl | Enable automated sync + prune |
| No progressive delivery | All-or-nothing deployments | Canary analysis with automatic rollback |
GitOps makes deployments boring. That is the goal. Every change is a merge, every environment matches Git, and every deviation is automatically corrected.