ArgoCD — GitOps for Kubernetes
ArgoCD continuously monitors a Git repository and automatically applies the desired state to your Kubernetes cluster. Git becomes the single source of truth.
💡 GitOps Concept
Never run kubectl apply manually in production. All changes go through Git → Pull Request → Merge → ArgoCD applies automatically. Git history is your audit log.
📦 Installation
# Install ArgoCD into your cluster
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=Ready pod -l app.kubernetes.io/name=argocd-server \
-n argocd --timeout=120s
# Access the UI via port-forward
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
# Open: https://localhost:8080 (login: admin / )
# Install ArgoCD CLI
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && mv argocd /usr/local/bin/
# Login via CLI
argocd login localhost:8080 --username admin --password <PASSWORD> --insecure
📄 Creating an Application
An ArgoCD Application tells ArgoCD which Git repo/path to sync to which cluster/namespace.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd # ArgoCD lives here
spec:
project: default
source:
repoURL: https://github.com/myorg/infra.git
targetRevision: main # branch, tag, or commit SHA
path: manifests/myapp # folder within the repo
destination:
server: https://kubernetes.default.svc # in-cluster
namespace: myapp
syncPolicy:
automated:
prune: true # delete resources removed from git
selfHeal: true # auto-fix manual kubectl changes
syncOptions:
- CreateNamespace=true # create the namespace if missing
retry:
limit: 3
backoff:
duration: 5s
factor: 2
| Setting | What It Does | Recommendation |
|---|---|---|
automated.prune: true | Deletes resources that were removed from Git | Enable in production — keeps cluster clean |
automated.selfHeal: true | Reverts manual kubectl changes automatically | Enable — enforces Git as source of truth |
prune: false | Never deletes resources, even if removed from Git | Use during migrations to avoid accidental deletes |
CreateNamespace=true | Creates the target namespace if missing | Enable for convenience |
⌨️ CLI Commands
# ── Applications ────────────────────────────────────────
argocd app list # list all apps
argocd app get myapp # detailed status
argocd app sync myapp # trigger manual sync
argocd app sync myapp --prune # sync and prune orphaned resources
argocd app diff myapp # show what would change
argocd app rollback myapp 3 # rollback to history id 3
argocd app history myapp # show sync history
argocd app delete myapp # delete app (not the resources)
argocd app delete myapp --cascade # delete app AND its k8s resources
# ── Repositories ────────────────────────────────────────
argocd repo add https://github.com/org/infra.git \
--username git --password mytoken
argocd repo add git@github.com:org/infra.git \
--ssh-private-key-path ~/.ssh/id_rsa
argocd repo list
# ── Projects ────────────────────────────────────────────
argocd proj list
argocd proj create myproject
# ── Users / RBAC ────────────────────────────────────────
argocd account list
argocd account update-password --current-password old --new-password new
🩺 Sync & Health Status
| Status | Meaning | Action |
|---|---|---|
| Synced | Cluster matches Git exactly | Nothing — all good |
| OutOfSync | Cluster differs from Git | Click Sync or argocd app sync myapp |
| Unknown | Can't determine status (API error) | Check ArgoCD server logs |
| Healthy | All pods running, services responding | Nothing |
| Degraded | Some pods crashing or not ready | kubectl describe pod + check logs |
| Progressing | Rolling update in progress | Wait or watch with kubectl rollout status |
| Suspended | App sync is paused | Resume in UI or argocd app resume myapp |
📁 Git Repo Structure
infra/
├── apps/ # ArgoCD Application manifests
│ ├── app-of-apps.yaml # the root Application
│ ├── myapp.yaml
│ └── monitoring.yaml
│
├── manifests/
│ ├── myapp/
│ │ ├── namespace.yaml
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── configmap.yaml
│ │
│ └── monitoring/
│ ├── prometheus/
│ └── grafana/
│
└── README.md
App of Apps Pattern
One root ArgoCD Application that manages all other Applications. Add a new service = add one YAML to apps/.
# apps/app-of-apps.yaml — the root application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
source:
repoURL: https://github.com/myorg/infra.git
targetRevision: main
path: apps # watches the apps/ directory
destination:
server: https://kubernetes.default.svc
namespace: argocd # child apps are deployed here
syncPolicy:
automated:
prune: true
selfHeal: true
⛵ Helm Charts
ArgoCD can deploy Helm charts directly from a repo — no need to run helm install manually.
spec:
source:
repoURL: https://prometheus-community.github.io/helm-charts
chart: kube-prometheus-stack
targetRevision: "55.5.0"
helm:
releaseName: monitoring
values: |
grafana:
adminPassword: "admin"
service:
type: NodePort
prometheus:
prometheusSpec:
retention: 30d
📋 Day-to-Day Workflow
Make a change
Edit a YAML file in your infra repo. Change image tag, update replicas, add a ConfigMap key, etc.
Create a PR
Push to a feature branch. Open a pull request so teammates can review the infrastructure change.
Merge to main
After approval, merge the PR. ArgoCD detects the change within 3 minutes (or immediately on webhook push).
Watch the sync
In the ArgoCD UI, watch the application sync. Each resource shows green (healthy) or red (error) in real-time.
Rollback if needed
If something is wrong, click History and Rollback in the UI, or run argocd app rollback myapp 3. ArgoCD re-applies the previous Git commit's state instantly.
With selfHeal: true, any manual kubectl change will be reverted by ArgoCD within minutes. This is the point — but be aware of it during debugging. Disable auto-sync temporarily if needed: argocd app set myapp --sync-policy none