What Is GitOps
GitOps is an operations paradigm that treats a Git repository as the single source of truth for infrastructure and applications, and automatically reconciles cluster state with repository state.
Developer → git push → GitHub ──► Argo CD (detects) ──► Kubernetes cluster
↑ continuously syncs state1. Installing Argo CD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Install the Argo CD CLI
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && sudo mv argocd /usr/local/bin
# Retrieve the initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Access the UI via port-forward
kubectl port-forward svc/argocd-server -n argocd 8080:443
# CLI login
argocd login localhost:8080 --username admin --password <password> --insecureExposing Argo CD Externally with Ingress (Production)
# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 4432. Designing the Repository Structure
In GitOps, the standard practice is to separate the application source repository from the infrastructure/deployment repository.
infra-repo/
├── apps/
│ ├── base/ # shared base configuration
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ ├── overlays/
│ │ ├── dev/ # development overlay
│ │ │ ├── kustomization.yaml
│ │ │ └── patch-replicas.yaml
│ │ ├── staging/
│ │ └── production/
└── argocd/
├── projects/
└── applications/Multi-Environment Overlays with Kustomize
# apps/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: my-registry.com/api:latest
resources:
requests:
cpu: 100m
memory: 128Mi
---
# apps/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
images:
- name: my-registry.com/api
newTag: v1.5.0 # pin production to a fixed tag
---
# apps/overlays/production/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 53. ApplicationSet — Automatic Multi-Environment Deployment
ApplicationSet automatically generates Applications for multiple environments from a single template.
# argocd/applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: api-appset
namespace: argocd
spec:
generators:
- list:
elements:
- env: dev
cluster: https://kubernetes.default.svc
namespace: dev
revision: main
- env: staging
cluster: https://staging-cluster.example.com
namespace: staging
revision: main
- env: production
cluster: https://prod-cluster.example.com
namespace: production
revision: v1.5.0 # pin production to a tag
template:
metadata:
name: api-{{env}}
spec:
project: default
source:
repoURL: https://github.com/myorg/infra-repo
targetRevision: "{{revision}}"
path: apps/overlays/{{env}}
destination:
server: "{{cluster}}"
namespace: "{{namespace}}"
syncPolicy:
automated:
prune: true # automatically prune resources deleted from Git
selfHeal: true # automatically revert manual cluster changes
syncOptions:
- CreateNamespace=true
- ServerSideApply=truekubectl apply -f argocd/applicationset.yaml
# List generated Applications
argocd app list
# Check sync status
argocd app get api-production4. Sync Waves — Controlling Deployment Order
Sync Waves let you run deployments in sequence: ConfigMap → DB migration → application.
# Step 1: create the ConfigMap first
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
annotations:
argocd.argoproj.io/sync-wave: "-2" # negative: runs first
---
# Step 2: DB migration Job
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
argocd.argoproj.io/sync-wave: "-1"
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: my-registry.com/api:v1.5.0
command: ["python", "manage.py", "migrate"]
---
# Step 3: the actual application (default: wave 0)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
annotations:
argocd.argoproj.io/sync-wave: "0"5. Argo Rollouts — Canary and Blue-Green Deployments
# Install Argo Rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# kubectl plugin
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64 && sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts# rollout.yaml — canary strategy
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 10
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: my-registry.com/api:v1.5.0
strategy:
canary:
steps:
- setWeight: 10 # send 10% of traffic to the new version
- pause: {duration: 5m}
- analysis:
templates:
- templateName: success-rate # error-rate analysis
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
canaryService: api-canary
stableService: api-stable
trafficRouting:
nginx:
stableIngress: api-ingress
---
# analysis template
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.monitoring:9090
query: |
sum(rate(http_requests_total{status!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))# Start the rollout
kubectl apply -f rollout.yaml
# Watch progress in real time
kubectl argo rollouts get rollout api --watch
# Manually promote to the next step
kubectl argo rollouts promote api
# Abort and roll back immediately if something goes wrong
kubectl argo rollouts abort api
kubectl argo rollouts undo api6. Connecting CI to GitOps — Automatic Image Tag Updates
# .github/workflows/deploy.yml
name: Build and Update Manifest
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push image
run: |
docker build -t my-registry.com/api:${{ github.sha }} .
docker push my-registry.com/api:${{ github.sha }}
- name: Update infra-repo image tag
run: |
git clone https://x-access-token:${{ secrets.INFRA_TOKEN }}@github.com/myorg/infra-repo.git
cd infra-repo
# update the image tag with kustomize
cd apps/overlays/staging
kustomize edit set image my-registry.com/api=my-registry.com/api:${{ github.sha }}
git config user.email "ci@myorg.com"
git config user.name "CI Bot"
git add -A
git commit -m "chore: update api to ${{ github.sha }}"
git push
# Argo CD detects the infra-repo change and deploys automaticallyAI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.