What GitOps actually means
GitOps is a deployment methodology where Git is the single source of truth for your infrastructure and application state. Instead of running kubectl apply or helm upgrade in a CI pipeline, you commit the desired state to a Git repository, and a controller (ArgoCD or Flux) continuously reconciles the live cluster state to match it.
The key benefit: every production change is a Git commit. With a full audit trail, instant rollback (just revert the commit), and no direct cluster access required from CI systems.
The architecture
Developer pushes → CI builds image → updates GitOps repo → ArgoCD syncs to cluster
Two repositories:
- App repo: application code + Dockerfile + CI workflow
- GitOps repo: Kubernetes manifests + Helm values for all environments
ArgoCD installation
Install ArgoCD with Helm into your EKS cluster:
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd \
--namespace argocd \
--create-namespace \
--values argocd-values.yaml
# argocd-values.yaml
global:
domain: argocd.example.com
server:
ingress:
enabled: true
ingressClassName: nginx
tls: true
configs:
params:
server.insecure: false
rbac:
policy.default: role:readonly
App-of-Apps pattern
For managing multiple applications, use the app-of-apps pattern. One root ArgoCD Application manages all your other Applications:
# apps/root.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
source:
repoURL: https://github.com/your-org/k8s-configs
path: apps/
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Automated image tag updates
After a successful CI build, update the image tag in the GitOps repo:
# .github/workflows/deploy.yml
- name: Checkout GitOps repo
uses: actions/checkout@v4
with:
repository: your-org/k8s-configs
token: ${{ secrets.GITOPS_PAT }}
path: k8s-configs
- name: Update image tag
run: |
cd k8s-configs
yq e -i '.image.tag = "${{ github.sha }}"' \
apps/my-app/overlays/staging/values.yaml
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "chore: promote my-app to ${{ github.sha }}"
git push
ArgoCD detects the push within 3 minutes (or immediately with a webhook) and syncs the new image to the cluster.
Canary rollouts with Argo Rollouts
For production deployments, replace the standard Kubernetes Deployment with an Argo Rollout:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 10
strategy:
canary:
canaryService: my-app-canary
stableService: my-app-stable
trafficRouting:
nginx:
stableIngress: my-app-ingress
steps:
- setWeight: 10 # 10% traffic to canary
- pause: { duration: 5m }
- analysis: # Auto-check error rates
templates:
- templateName: success-rate
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100
autoPromotionEnabled: false # Require explicit promotion
The AnalysisTemplate queries Prometheus to verify the canary is healthy:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{status!~"5..", app="my-app", canary="true"}[5m]))
/
sum(rate(http_requests_total{app="my-app", canary="true"}[5m]))
If the error rate drops below 95%, Argo Rollouts automatically aborts the canary and routes all traffic back to stable.
Summary
- ArgoCD watches your GitOps repo, not your CI system
- CI only pushes images and commits tag updates — never touches the cluster
- App-of-apps simplifies managing many services
- Argo Rollouts adds progressive delivery with automatic rollback
- Full audit trail of every deployment in Git history