GitOps with ArgoCD for Kubernetes
A hands-on guide to implementing GitOps with ArgoCD: declarative deployments, sync policies, and multi-environment management.
What you'll learn
- ✓What GitOps is and why it improves Kubernetes deployments
- ✓Installing and configuring ArgoCD
- ✓Creating Applications that sync from Git
- ✓Handling multi-environment setups with Kustomize overlays
Prerequisites
- •Basic Kubernetes knowledge (deployments, services, namespaces)
- •Familiarity with Git and YAML
What GitOps Is
GitOps is a deployment model where Git is the single source of truth for your infrastructure and application state. You describe the desired state of your cluster in YAML files stored in a Git repository. A controller running inside the cluster continuously compares the desired state (Git) to the actual state (cluster) and reconciles any drift.
The key shift is that you never run kubectl apply manually. You push a commit, and the controller applies it. You roll back by reverting a commit. Every change is auditable, reviewable, and reversible through Git history.
ArgoCD is the most widely adopted GitOps controller for Kubernetes. It provides a web UI, CLI, RBAC, health checks, and multi-cluster support.
Installing ArgoCD
# Create the namespace
kubectl create namespace argocd
# Install ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods to be ready
kubectl wait --for=condition=ready pod -l app.kubernetes.io/part-of=argocd -n argocd --timeout=120s
# Get the initial admin password
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 -d
# Port-forward the UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
Access the UI at https://localhost:8080. Log in with username admin and the password you extracted. Change the password immediately.
Install the CLI:
# macOS
brew install argocd
# Login
argocd login localhost:8080 --username admin --password <initial-password> --insecure
Repository Structure
A GitOps repo separates application manifests from application code. A common pattern:
gitops-repo/
apps/
api-service/
base/
deployment.yaml
service.yaml
hpa.yaml
kustomization.yaml
overlays/
staging/
kustomization.yaml
replicas-patch.yaml
production/
kustomization.yaml
replicas-patch.yaml
resources-patch.yaml
worker/
base/
deployment.yaml
kustomization.yaml
overlays/
staging/
kustomization.yaml
production/
kustomization.yaml
Base Manifests
# apps/api-service/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 2
selector:
matchLabels:
app: api-service
template:
metadata:
labels:
app: api-service
spec:
containers:
- name: api
image: registry.example.com/api-service:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# apps/api-service/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
Production Overlay
# apps/api-service/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
patches:
- path: replicas-patch.yaml
- path: resources-patch.yaml
# apps/api-service/overlays/production/replicas-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 5
# apps/api-service/overlays/production/resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
template:
spec:
containers:
- name: api
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
Creating an ArgoCD Application
You can create applications through the UI, CLI, or declaratively with YAML. Declarative is the GitOps way:
# argocd/applications/api-service-production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service-production
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/your-org/gitops-repo.git
targetRevision: main
path: apps/api-service/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 1m
The important fields:
source.pathpoints to the Kustomize overlay directory.syncPolicy.automatedenables automatic syncing when Git changes.prune: truedeletes resources from the cluster that no longer exist in Git.selfHeal: truereverts manualkubectlchanges to match Git.
Sync Waves and Hooks
When deploying, some resources must be created before others. A database migration job should run before the new deployment rolls out. Sync waves control ordering:
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
argocd.argoproj.io/sync-wave: "-1"
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: migrate
image: registry.example.com/api-service:v1.1.0
command: ["./migrate", "up"]
restartPolicy: Never
backoffLimit: 3
Lower sync wave numbers execute first. The PreSync hook runs the job before the main sync starts. HookSucceeded cleans up the job after it completes successfully.
The Deployment Workflow
With GitOps and ArgoCD in place, the deployment flow becomes:
- CI pipeline builds and pushes a new container image (
api-service:v1.1.0). - CI pipeline opens a pull request on the GitOps repo, updating the image tag in the Kustomize overlay.
- A teammate reviews the PR. The diff shows exactly what will change in the cluster.
- PR is merged. ArgoCD detects the change within its polling interval (default 3 minutes) or via a webhook.
- ArgoCD applies the manifests. The deployment rolls out with Kubernetes’ rolling update strategy.
- ArgoCD reports the sync status. If health checks fail, the deployment is marked as degraded.
To roll back, revert the merge commit. ArgoCD syncs the previous state.
Image Updater
Manually updating image tags in Git is tedious. ArgoCD Image Updater automates this:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service-production
annotations:
argocd-image-updater.argoproj.io/image-list: api=registry.example.com/api-service
argocd-image-updater.argoproj.io/api.update-strategy: semver
argocd-image-updater.argoproj.io/api.allow-tags: "regexp:^v[0-9]+\\.[0-9]+\\.[0-9]+$"
argocd-image-updater.argoproj.io/write-back-method: git
Image Updater watches the container registry, and when a new semver tag appears, it commits the updated tag to the GitOps repo. The entire flow stays in Git.
App of Apps Pattern
Managing dozens of Application resources by hand does not scale. The App of Apps pattern creates a single root Application that manages all other Applications:
# argocd/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/gitops-repo.git
targetRevision: main
path: argocd/applications
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Every YAML file in argocd/applications/ defines an Application. ArgoCD syncs them all. Adding a new service means adding one YAML file and pushing a commit.
Common Pitfalls
Storing secrets in Git. Even in a private repo, plaintext secrets in Git are a security risk. Use Sealed Secrets, External Secrets Operator, or Vault to inject secrets at runtime.
Disabling self-heal. Without self-heal, someone can kubectl edit a deployment and the change persists until the next Git push. This breaks the GitOps contract.
Monolithic GitOps repos. If every team commits to the same repo, merge conflicts and slow CI become problems. Consider one repo per team or per domain.
Start with one non-critical service. Set up the GitOps repo, create the ArgoCD Application, and deploy through a Git push. Once the workflow is comfortable, expand to more services and enable automated sync.
Related articles
- DevOps GitOps with ArgoCD: Deploy Kubernetes Apps Automatically
Learn GitOps principles and deploy Kubernetes applications with ArgoCD. Covers installation, app configuration, sync policies, multi-environment setups, and rollbacks.
- DevOps GitOps Branching Strategies for Kubernetes
Design effective Git repository structures and branching strategies for GitOps-driven Kubernetes deployments with Flux and Argo CD.
- DevOps Container Orchestration: Docker Swarm vs Kubernetes
Compare Docker Swarm and Kubernetes for container orchestration. Learn the architecture, setup, scaling, and networking of both platforms with practical examples.
- DevOps Zero-Downtime Deployments: Strategies and Patterns
Master zero-downtime deployment strategies including rolling updates, blue-green, canary, and feature flags. Learn practical patterns for Kubernetes and cloud platforms.