Loading learning content...
GitOps and Kubernetes were made for each other. Kubernetes' declarative API, desired-state model, and controller pattern align perfectly with GitOps principles. In fact, GitOps essentially extends the Kubernetes reconciliation model to include Git as the ultimate source of truth.
This page synthesizes everything from the previous pages into a comprehensive, practical guide for implementing GitOps in Kubernetes environments. We'll cover real-world patterns, common pitfalls, migration strategies, and the operational practices that make GitOps successful at scale.
This capstone page provides actionable patterns for Kubernetes GitOps implementation. You'll learn end-to-end workflows, environment promotion strategies, developer experience considerations, and the organizational practices that determine whether GitOps succeeds or fails.
A complete GitOps workflow connects code changes to production deployments through automated, auditable pipelines. Understanding the full flow is essential for designing effective processes.
The Two-Repository Pattern:
Most GitOps implementations use two repositories:
Application Repository — Contains source code for applications
Configuration Repository — Contains Kubernetes manifests
Why Separate Repositories?
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
# End-to-End GitOps Workflow 1. DEVELOP - Developer creates feature branch in app repository - Writes code, tests locally - Opens pull request 2. REVIEW - Peers review code changes - Automated checks run (linting, unit tests) - Security scanning on dependencies - Approval required before merge 3. BUILD (CI Pipeline) - Merge triggers CI pipeline - Build application - Run full test suite - Scan for vulnerabilities - Build container image - Push to registry with commit SHA tag Example: registry.example.com/my-app:a1b2c3d 4. UPDATE CONFIG (Automated or Manual) Option A - Automated (recommended for dev/staging): - CI updates image tag in config repo - Commits directly to development branch - GitOps operator detects and deploys Option B - Pull Request (recommended for production): - CI opens PR to update production image tag - Requires human approval - Merge triggers deployment 5. RECONCILE - GitOps operator detects config change - Renders manifests (Kustomize/Helm) - Computes diff against live state - Applies changes to cluster - Waits for health check 6. VERIFY - Deployment rolls out new pods - Health checks pass - Metrics confirm normal operation - Alerts if anomalies detected 7. ROLLBACK (if needed) - Revert commit in config repo - OR: Update image tag to previous version - GitOps operator applies reverted state - Deployment rolls backUse immutable tags like Git commit SHAs (a1b2c3d) or semantic versions (v1.5.3), never 'latest'. Immutable tags ensure reproducibility—a manifest with image:a1b2c3d always deploys the same code. With 'latest', the same manifest could deploy different code depending on when it runs.
Promoting changes through environments (dev → staging → production) is a critical workflow. GitOps provides several patterns for environment promotion, each with trade-offs.
Directory-based promotion uses separate directories for each environment. Promotion is literally copying/updating files from one directory to another.
123456789101112131415161718192021222324252627282930
# Directory structureapps/my-app/├── base/│ ├── kustomization.yaml│ ├── deployment.yaml│ └── service.yaml└── overlays/ ├── development/ │ ├── kustomization.yaml │ └── image.yaml # image: my-app:dev-abc123 ├── staging/ │ ├── kustomization.yaml │ └── image.yaml # image: my-app:stg-def456 └── production/ ├── kustomization.yaml └── image.yaml # image: my-app:v1.5.2 # Promotion workflow:# 1. CI deploys to dev with new image tag# 2. After testing, PR to copy image.yaml to staging# 3. After staging validation, PR to copy to production # Pros:# - Clear visibility into each environment's state# - Easy to diff between environments# - Pull requests for promotions enable review # Cons:# - Manual steps for promotion (unless automated)# - Potential for environments to divergeRecommended: Directory Promotion with Automated PRs
For most teams, directory-based promotion with automated pull requests offers the best balance:
This pattern provides:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
# .github/workflows/promote-to-staging.ymlname: Promote to Staging on: # Trigger when dev deployment succeeds workflow_run: workflows: ["Deploy to Development"] types: [completed] branches: [main] jobs: promote: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - uses: actions/checkout@v4 with: repository: company/infrastructure token: ${{ secrets.GH_PAT }} - name: Get current dev image id: dev-image run: | IMAGE=$(yq '.images[0].newTag' \ apps/my-app/overlays/development/kustomization.yaml) echo "tag=$IMAGE" >> $GITHUB_OUTPUT - name: Update staging image run: | cd apps/my-app/overlays/staging kustomize edit set image my-app=registry.example.com/my-app:${{ steps.dev-image.outputs.tag }} - name: Create promotion PR uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GH_PAT }} commit-message: "promote: my-app ${{ steps.dev-image.outputs.tag }} to staging" title: "Promote my-app to staging" body: | ## Promotion Request Promoting my-app to staging environment. **Image Tag**: ${{ steps.dev-image.outputs.tag }} **Source**: Development (verified healthy) ### Checklist - [ ] Development deployment verified - [ ] No breaking changes in release notes - [ ] Ready for staging validation branch: promote/my-app-staging labels: promotion,stagingGitOps success depends heavily on developer experience. If the workflow is friction-heavy, developers will find workarounds that undermine GitOps benefits. Designing for developer ergonomics is essential.
Preview Environments:
One of GitOps' most powerful developer experience features is preview environments—ephemeral environments for each pull request.
How it works:
preview-pr-123pr-123.preview.example.com)This enables:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
# ApplicationSet for preview environmentsapiVersion: argoproj.io/v1alpha1kind: ApplicationSetmetadata: name: preview-environments namespace: argocdspec: generators: # Generate from pull requests - pullRequest: github: owner: company repo: my-app tokenRef: secretName: github-token key: token requeueAfterSeconds: 60 template: metadata: name: 'preview-{{branch}}-{{number}}' labels: app.kubernetes.io/instance: preview preview-pr: '{{number}}' spec: project: previews source: repoURL: https://github.com/company/infrastructure.git targetRevision: HEAD path: apps/my-app/overlays/preview kustomize: images: - my-app=registry.example.com/my-app:pr-{{head_sha}} nameSuffix: '-{{number}}' destination: server: https://kubernetes.default.svc namespace: 'preview-{{number}}' syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true # Auto-delete after PR closure syncPolicy: automated: prune: truePreview environments consume cluster resources. Implement TTLs (auto-delete after 24 hours of inactivity), resource quotas (limit CPU/memory per preview), and max preview limits (e.g., maximum 20 concurrent previews per repository) to prevent resource exhaustion.
Progressive delivery extends GitOps with advanced deployment strategies: canary releases, blue-green deployments, and automated rollback based on metrics. Rather than deploying 100% at once, you deploy gradually and roll back automatically if problems are detected.
Key Progressive Delivery Patterns:
| Strategy | How It Works | Best For |
|---|---|---|
| Canary | Route small percentage to new version; increase if healthy | High-traffic services, gradual validation |
| Blue-Green | Run old and new in parallel; switch all traffic at once | Stateful apps, instant rollback need |
| A/B Testing | Route based on user properties (headers, cookies) | Feature experiments, user segmentation |
| Rolling | Replace instances one at a time | Simple apps, Kubernetes default |
Argo Rollouts for Progressive Delivery:
Argo Rollouts is a Kubernetes controller that provides advanced deployment capabilities beyond native Deployments. It integrates seamlessly with ArgoCD for GitOps-driven progressive delivery.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
# Argo Rollout with Canary StrategyapiVersion: argoproj.io/v1alpha1kind: Rolloutmetadata: name: my-appspec: replicas: 10 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:v1.5.3 ports: - containerPort: 8080 strategy: canary: # Traffic routing via service mesh or ingress trafficRouting: nginx: stableIngress: my-app-stable # Canary analysis with Prometheus metrics analysis: templates: - templateName: success-rate startingStep: 2 args: - name: service-name value: my-app # Progressive rollout steps steps: # Step 1: 5% traffic to canary - setWeight: 5 - pause: {duration: 5m} # Step 2: 10% traffic, start analysis - setWeight: 10 - pause: {duration: 10m} # Step 3: 25% traffic - setWeight: 25 - pause: {duration: 10m} # Step 4: 50% traffic - setWeight: 50 - pause: {duration: 15m} # Step 5: 100% - full promotion - setWeight: 100 ---# Analysis Template using PrometheusapiVersion: argoproj.io/v1alpha1kind: AnalysisTemplatemetadata: name: success-ratespec: args: - name: service-name metrics: - name: success-rate interval: 1m # Must stay above 95% success rate successCondition: result[0] >= 0.95 failureLimit: 3 provider: prometheus: address: http://prometheus.monitoring:9090 query: | sum(rate( http_requests_total{ service="{{args.service-name}}", status=~"2.." }[5m] )) / sum(rate( http_requests_total{ service="{{args.service-name}}" }[5m] ))Flagger for Flux-Based Progressive Delivery:
Flagger is the Flux ecosystem's equivalent to Argo Rollouts. It works with Flux and provides similar capabilities including canary deployments, A/B testing, and automated analysis with Prometheus, Datadog, or other metrics providers.
How Progressive Delivery Integrates with GitOps:
Progressive delivery doesn't conflict with GitOps—it enhances it. Git declares 'deploy version 1.5.3', and the Rollout controller handles how that deployment happens safely. If the canary fails, the Rollout stays on the previous version even though Git says 1.5.3, until you either fix the issue or update Git to roll back.
GitOps provides strong security foundations, but realizing these benefits requires deliberate security architecture. Kubernetes-specific concerns add additional layers to consider.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
# Require images from approved registriesapiVersion: kyverno.io/v1kind: ClusterPolicymetadata: name: require-approved-registryspec: validationFailureAction: Enforce background: false rules: - name: check-registry match: any: - resources: kinds: - Pod validate: message: "Images must come from approved registries" pattern: spec: containers: - image: "registry.example.com/* | gcr.io/my-project/*" ---# Require resource limitsapiVersion: kyverno.io/v1kind: ClusterPolicymetadata: name: require-resource-limitsspec: validationFailureAction: Enforce rules: - name: check-limits match: any: - resources: kinds: - Deployment - StatefulSet validate: message: "All containers must have resource limits" pattern: spec: template: spec: containers: - resources: limits: memory: "?*" cpu: "?*" ---# Require labels for GitOps traceabilityapiVersion: kyverno.io/v1kind: ClusterPolicymetadata: name: require-labelsspec: validationFailureAction: Enforce rules: - name: check-labels match: any: - resources: kinds: - Deployment - Service validate: message: "Resources must have required labels" pattern: metadata: labels: app.kubernetes.io/name: "?*" app.kubernetes.io/managed-by: "?*"Compliance Benefits of GitOps:
GitOps significantly simplifies compliance with frameworks like SOC 2, PCI-DSS, HIPAA, and ISO 27001:
| Compliance Requirement | GitOps Evidence |
|---|---|
| Change management | Pull requests with reviews and approvals |
| Audit trail | Complete Git history with author, timestamp, diff |
| Access control | Branch protection and repository permissions |
| Segregation of duties | Separate roles for code authors and approvers |
| Change documentation | Commit messages and PR descriptions |
| Rollback capability | git revert with full traceability |
| Reproducibility | Declarative state enables exact recreation |
For auditors, you can demonstrate control effectiveness by showing:
Adopting GitOps for existing Kubernetes deployments requires careful migration. Attempting a "big bang" migration—converting everything at once—is high-risk. A phased approach ensures continuity while progressively adopting GitOps benefits.
Extracting Current State:
For clusters with existing resources managed imperatively (kubectl apply, Helm install), you need to extract current state into Git:
# Export all resources from a namespace
kubectl get all,configmap,secret,ingress,pvc \
-n my-app -o yaml > current-state.yaml
# Alternatively, use kreate to extract
kreate export -n my-app --output ./apps/my-app/base/
# For Helm releases
helm get values my-release -n my-app > values.yaml
helm get manifest my-release -n my-app > manifests.yaml
Important: Remove server-generated fields (resourceVersion, uid, creationTimestamp, status) before committing. These fields cause conflicts during reconciliation.
Before enabling auto-heal, run in 'drift detection only' mode. You'll discover resources that were manually modified, undocumented workarounds, and state that exists in the cluster but isn't documented anywhere. This discovery is valuable—it surfaces technical debt that would otherwise break deployments.
Handling Resistance:
GitOps adoption often faces resistance from teams comfortable with existing workflows. Common objections and responses:
Success requires executive sponsorship, clear communication of benefits, and willingness to iterate on workflows based on team feedback.
This module has provided a comprehensive exploration of GitOps—from foundational principles through advanced Kubernetes implementation. Let's consolidate the critical insights:
The GitOps Promise Fulfilled:
With GitOps properly implemented, your infrastructure becomes:
GitOps is not just a deployment technique—it's a philosophical shift toward treating infrastructure with the same rigor we apply to application code. Embrace it fully, and you'll wonder how you ever operated any other way.
Congratulations! You've completed the GitOps module. You now possess the knowledge to implement GitOps for Kubernetes, from foundational principles through production-grade deployments. Go forth and make Git your infrastructure's single source of truth.