Argo CD GitOps Tutorial 2026: Deploy Kubernetes Apps from Git with Self-Healing

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-admin access.
  • 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:

FeatureArgo CDFlux v2
UIBuilt-in web UINone (use Weave GitOps)
CRD modelApplication, AppProject, ApplicationSetGitRepository, Kustomization, HelmRelease
Helm supportNativeNative
Kustomize supportNativeNative
Multi-clusterApplicationSet + cluster secretsMulti-cluster with GitRepository per cluster
RBACProject-level RBACRelies on Kubernetes RBAC
SSOOIDC, SAML, LDAP built-inVia Dex (separate deployment)
Notificationsargocd-notificationsnotification-controller
CommunityCNCF graduated, largeCNCF graduated, large
Best forTeams wanting a UI, app-centric modelTeams 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

Leonardo Lazzaro

Software engineer and technical writer. 10+ years experience in DevOps, Python, and Linux systems.

More articles by Leonardo Lazzaro