Understanding ArgoCD: GitOps for Kubernetes
With Kubernetes, you can apply manifests manually with kubectl apply or trigger deployments from a CI pipeline. Both approaches work — until someone applies a hotfix directly to the cluster and now production no longer matches what’s in git. ArgoCD solves this by making git the single source of truth: the cluster continuously reconciles itself to match whatever is in the repository.
What is GitOps?
GitOps is an operational model where:
- The desired state of your system lives in git
- An agent continuously compares desired state to actual state
- Any drift is automatically corrected (or flagged)
With ArgoCD, you push a change to git and the cluster catches up — you never kubectl apply to production directly.
Installing ArgoCD
$ kubectl create namespace 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=available deployment \
-l app.kubernetes.io/name=argocd-server \
-n argocd --timeout=120s
# port-forward the UI
$ kubectl port-forward svc/argocd-server -n argocd 8080:443
Get the initial admin password:
$ kubectl get secret argocd-initial-admin-secret -n argocd \
-o jsonpath='{.data.password}' | base64 -d
r8Kf9pXqZmN2wQ4T
Visit https://localhost:8080, log in as admin, and you’re in.
Install the CLI:
$ brew install argocd
$ argocd login localhost:8080 \
--username admin \
--password r8Kf9pXqZmN2wQ4T \
--insecure
Creating Your First Application
An ArgoCD Application links a git repository path to a cluster namespace. Here’s the YAML approach:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: main
path: apps/myapp
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # delete resources removed from git
selfHeal: true # revert manual changes to the cluster
syncOptions:
- CreateNamespace=true
$ kubectl apply -f myapp-argocd.yaml
application.argoproj.io/myapp created
$ argocd app get myapp
Name: myapp
Project: default
Server: https://kubernetes.default.svc
Namespace: production
URL: https://localhost:8080/applications/myapp
Repo: https://github.com/myorg/k8s-manifests
Target: main
Path: apps/myapp
Sync Policy: Automated (Prune, Self Heal)
Sync Status: Synced to main (a3f2c9d)
Health Status: Healthy
Sync Policies
ArgoCD can sync manually or automatically.
Manual sync — ArgoCD shows drift but waits for you to approve:
$ argocd app sync myapp
Automated sync — ArgoCD applies changes within seconds of a git push. Use prune: true to also delete resources that were removed from git, and selfHeal: true to revert any manual kubectl edits.
For production, many teams prefer automated sync with manual promotion: automated in staging, manual approval gate for production. You can set up ArgoCD sync windows to restrict when automated syncs are allowed.
Repository Structure Patterns
ArgoCD works with raw manifests, Helm charts, or Kustomize. A common layout for multiple apps and environments:
k8s-manifests/
├── apps/
│ ├── myapp/
│ │ ├── base/ # shared manifests
│ │ └── overlays/
│ │ ├── staging/ # kustomize patches for staging
│ │ └── production/
│ └── api-gateway/
└── argocd/
└── applications/ # ArgoCD Application YAMLs
Kustomize source:
source:
repoURL: https://github.com/myorg/k8s-manifests
path: apps/myapp/overlays/production
targetRevision: main
Helm chart source:
source:
repoURL: https://github.com/myorg/k8s-manifests
path: helm/myapp
targetRevision: main
helm:
valueFiles:
- values/production.yaml
App of Apps Pattern
Managing dozens of ArgoCD Application resources manually doesn’t scale. The App of Apps pattern uses one root Application to manage all others:
# root-app.yaml
spec:
source:
path: argocd/applications # directory of Application YAMLs
Push a new Application YAML to argocd/applications/ and ArgoCD picks it up automatically — no manual kubectl apply needed.
Checking Sync Status
# list all apps
$ argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
myapp in-cluster production default Synced Healthy Auto-Prune
# see what's out of sync (before syncing)
$ argocd app diff myapp
# force a sync
$ argocd app sync myapp --prune
# roll back to a previous git commit
$ argocd app rollback myapp <revision-id>
Conclusion
ArgoCD enforces a discipline that’s hard to achieve with pipelines alone: every change to the cluster goes through git, drift is visible and correctable, and the history of every deployment is a git commit. The combination of automated sync, self-healing, and pull-request-based change management gives you Kubernetes deployments that are auditable, reproducible, and recoverable. Pair it with Helm or Kustomize for per-environment configuration and you have a complete GitOps workflow.