Argo CD GitOps Tutorial 2026: Deploy Kubernetes Apps from Git with Self-Healing
GitOps has become the standard model for continuous delivery on Kubernetes. Instead of running kubectl apply from a CI pipeline, you commit your desired state to Git and a controller inside the cluster reconciles reality to match it. This tutorial walks you through a complete argocd kubernetes tutorial: installing Argo CD, deploying your first app, enabling self-healing, managing multiple environments with Kustomize, and scaling to many apps with ApplicationSets.
Prerequisites: a running Kubernetes cluster (kind, k3s, EKS, GKE, or AKS all work), kubectl configured, and git available locally.
1. What GitOps Is — and Why Pull-Based CD Wins
Traditional pipelines push changes: a CI job authenticates to the cluster and runs kubectl apply. This approach has two weaknesses. First, your pipeline needs cluster credentials — a security liability. Second, nobody continuously checks that the cluster still matches what was deployed; manual changes or node replacements silently create drift.
GitOps flips the model. A controller pulls from Git on a schedule and reconciles the cluster toward the committed state. Key properties:
- Git is the single source of truth. Every change is a commit — auditable, reversible, and reviewed via pull request.
- Credentials stay inside the cluster. No external system needs
cluster-adminaccess. - Self-healing is built-in. The controller detects and corrects drift automatically.
Argo CD implements this model and is the most widely adopted GitOps tool in the Kubernetes ecosystem as of 2026.
2. Install Argo CD on Kubernetes
Create a dedicated namespace and apply the upstream manifests:
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Wait for all pods to reach Running:
kubectl -n argocd rollout status deploy/argocd-server
Access the UI via port-forward
kubectl port-forward svc/argocd-server -n argocd 8080:443
Open https://localhost:8080 in your browser (accept the self-signed cert). The default username is admin. Retrieve the initial password with:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d && echo
Log in, then immediately change the password under User Info → Update Password or via the CLI:
argocd login localhost:8080 --username admin --insecure
argocd account update-password
3. Connect a Git Repository
Argo CD uses an Application custom resource to track a Git repo path. Create the file application.yaml with your repository details:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-k8s-manifests.git
targetRevision: main
path: manifests/base
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
syncOptions:
- CreateNamespace=true
Apply it:
kubectl apply -f application.yaml
Your repository at manifests/base should contain plain Kubernetes YAML files — a Deployment, a Service, and whatever else your app needs. Argo CD will detect them automatically and show the app in the UI with status OutOfSync until a sync is performed.
4. Deploy Your First App with Auto-Sync
Trigger a manual sync once to verify everything is wired up correctly:
argocd app sync my-app
Watch the rollout:
kubectl -n my-app rollout status deploy/my-app
Once healthy, enable automated sync so future commits deploy themselves. Update application.yaml to add the automated block:
syncPolicy:
automated:
prune: true # delete resources removed from Git
selfHeal: true # revert manual changes to the cluster
syncOptions:
- CreateNamespace=true
Apply the change:
kubectl apply -f application.yaml
From this point forward, every commit merged to main in the manifests/base path will trigger a reconciliation within three minutes (the default polling interval).
5. Self-Healing Demo
selfHeal: true means Argo CD will detect and undo manual changes. Prove it:
# Confirm the deployment is running
kubectl -n my-app get deploy my-app
# Manually delete it (simulating accidental deletion or drift)
kubectl -n my-app delete deploy my-app
# Watch Argo CD restore it — typically within 60–180 seconds
kubectl -n my-app get deploy -w
You will see the deployment disappear and then reappear as Argo CD's reconciliation loop fires. The UI will briefly show the app as OutOfSync / Degraded before returning to Synced / Healthy.
This is the core value proposition of GitOps: the cluster always converges back to what Git says, regardless of what happens in between.
6. Multi-Environment Setup with Kustomize Overlays
A common pattern is to share a base manifest set and layer environment-specific patches on top. Structure your repo like this:
manifests/
base/
deployment.yaml
service.yaml
kustomization.yaml
overlays/
dev/
kustomization.yaml
patch-replicas.yaml
staging/
kustomization.yaml
patch-replicas.yaml
prod/
kustomization.yaml
patch-replicas.yaml
manifests/overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- patch-replicas.yaml
images:
- name: my-app
newTag: "1.4.2"
Create one Argo CD Application per environment, pointing each to its overlay path:
# prod-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-k8s-manifests.git
targetRevision: main
path: manifests/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: my-app-prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Repeat for dev and staging, changing name, path, and namespace accordingly.
7. Argo CD ApplicationSet: Managing Many Apps at Once
When you have dozens of services or environments, creating individual Application resources by hand becomes tedious. ApplicationSet generates Application objects from a template and a generator. The List generator is the simplest:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-apps
namespace: argocd
spec:
generators:
- list:
elements:
- app: frontend
env: prod
- app: backend
env: prod
- app: frontend
env: staging
- app: backend
env: staging
template:
metadata:
name: "{{app}}-{{env}}"
spec:
project: default
source:
repoURL: https://github.com/your-org/your-k8s-manifests.git
targetRevision: main
path: "manifests/{{app}}/overlays/{{env}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{app}}-{{env}}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Apply with kubectl apply -f applicationset.yaml and Argo CD will generate four Application objects automatically. Add a new element to the list and it appears in the cluster; remove one and it is deleted.
For even more power, the Git Directory generator scans a repo path and creates one Application per subdirectory — no list to maintain at all.
8. Argo CD vs Flux: Comparison
Both tools implement GitOps for Kubernetes. Here is a practical comparison as of 2026:
| Feature | Argo CD | Flux v2 |
|---|---|---|
| UI | Built-in web UI | None (use Weave GitOps) |
| CRD model | Application, AppProject, ApplicationSet | GitRepository, Kustomization, HelmRelease |
| Helm support | Native | Native |
| Kustomize support | Native | Native |
| Multi-cluster | ApplicationSet + cluster secrets | Multi-cluster with GitRepository per cluster |
| RBAC | Project-level RBAC | Relies on Kubernetes RBAC |
| SSO | OIDC, SAML, LDAP built-in | Via Dex (separate deployment) |
| Notifications | argocd-notifications | notification-controller |
| Community | CNCF graduated, large | CNCF graduated, large |
| Best for | Teams wanting a UI, app-centric model | Teams preferring pure GitOps primitives, Flux-native tooling |
Choose Argo CD if your team values a graphical dashboard and an app-centric mental model. Choose Flux if you prefer composable, lightweight controllers and don't need a built-in UI.
9. FAQ: Common Issues
App is stuck in OutOfSync even after syncing
This usually means a resource in the cluster has a field that Argo CD cannot reconcile — often a kubectl.kubernetes.io/last-applied-configuration annotation conflict or a mutating webhook adding fields. Check the diff in the UI (App Details → Diff) and add a resource.exclusions rule in argocd-cm if the field is injected by the cluster and should be ignored.
Health check shows Degraded for a Deployment
Argo CD checks Deployment.status.availableReplicas. If a pod is crash-looping or in ImagePullBackOff, the app will show as Degraded. Run kubectl -n <namespace> describe pod <pod-name> to get the root cause.
Sync fails with "ComparisonError: failed to load target state"
This means Argo CD cannot render the manifests from Git — typically a broken Kustomization, a missing Helm values file, or a syntax error in YAML. Run kustomize build manifests/overlays/prod locally to reproduce the error without deploying.
Auto-sync keeps reverting my hotfix
selfHeal: true is working as intended. The correct fix is to commit your hotfix to Git. If you genuinely need to pause reconciliation temporarily, use argocd app set my-app --sync-policy none to disable auto-sync for that application.
Next Steps
You now have a working GitOps pipeline with Argo CD: installs in minutes, syncs from Git automatically, heals itself when the cluster drifts, and scales to any number of environments or applications with ApplicationSets. From here you can explore:
- Argo Rollouts for canary and blue-green deployments with automatic analysis
- argocd-image-updater to automate image tag bumps via commit
- Sealed Secrets or External Secrets Operator to manage sensitive values in Git safely
- Argo CD Notifications for Slack/PagerDuty alerts on sync failures