intermediate ~90 min updated 2026-06-01
Argo CD GitOps Deployment
Install Argo CD on a kind cluster, connect it to a Git repository, and deploy an app declaratively with an Application manifest. Watch automated sync repair drift in real time.
Objective
Deploy an application via Argo CD from a Git repository with automated sync and self-healing enabled, then observe drift correction. The Git repo becomes the single source of truth: any manual change in the cluster is reverted, and any commit is rolled out automatically.
Prerequisites
- Docker installed and running
- kind and kubectl installed
- A free GitHub account
- Git installed and configured with push access
- Comfort with basic kubectl commands
Architecture
Argo CD runs inside the cluster and continuously compares the live state against manifests stored in your GitHub repo. An Application custom resource tells Argo CD which repo path to watch and which namespace to deploy into. With automated sync plus self-heal, the reconciliation loop pushes the cluster back to the Git-declared state.
GitHub repo (manifests/) kind cluster
+------------------------+ poll +---------------------------+
| deployment.yaml | <-------- | argocd namespace |
| service.yaml | | repo-server / controller |
+------------------------+ | api-server / redis |
^ +------------+--------------+
| git push | apply & heal
you commit changes +------------v--------------+
| guestbook namespace |
| Deployment + Service |
+---------------------------+
Steps
1. Create the cluster and install Argo CD
kind create cluster --name argocd-lab
kubectl create namespace argocd
kubectl apply -n argocd -f \
https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl wait --for=condition=available deployment/argocd-server \
-n argocd --timeout=300s
2. Get the admin password and open the UI
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d; echo
kubectl port-forward svc/argocd-server -n argocd 8080:443 &
Browse to https://localhost:8080 (accept the self-signed cert) and log in as admin.
3. Create the Git repository with manifests
mkdir gitops-demo && cd gitops-demo && git init -b main
mkdir manifests
# manifests/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
selector:
matchLabels:
app: guestbook
template:
metadata:
labels:
app: guestbook
spec:
containers:
- name: guestbook
image: ghcr.io/argoproj/argocd-example-apps/guestbook-ui:latest
ports:
- containerPort: 80
# manifests/service.yaml
apiVersion: v1
kind: Service
metadata:
name: guestbook
spec:
selector:
app: guestbook
ports:
- port: 80
targetPort: 80
git add -A && git commit -m "Initial guestbook manifests"
gh repo create gitops-demo --public --source=. --push
4. Create the Argo CD Application
# app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/YOUR_USER/gitops-demo.git
targetRevision: main
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
kubectl apply -f app.yaml
kubectl get applications -n argocd -w
5. Test self-healing (drift correction)
Manually scale the deployment and watch Argo CD revert it:
kubectl scale deployment guestbook -n guestbook --replicas=5
sleep 20
kubectl get deployment guestbook -n guestbook
6. Deploy a change through Git
sed -i 's/replicas: 2/replicas: 3/' manifests/deployment.yaml
git commit -am "Scale guestbook to 3 replicas"
git push
# Within ~3 minutes (default poll interval) the cluster converges:
kubectl get deployment guestbook -n guestbook -w
Expected output
$ kubectl get applications -n argocd
NAME SYNC STATUS HEALTH STATUS
guestbook Synced Healthy
$ kubectl scale deployment guestbook -n guestbook --replicas=5
deployment.apps/guestbook scaled
$ sleep 20 && kubectl get deployment guestbook -n guestbook
NAME READY UP-TO-DATE AVAILABLE AGE
guestbook 2/2 2 2 6m <- self-heal reverted drift
$ kubectl get deployment guestbook -n guestbook # after the git push
NAME READY UP-TO-DATE AVAILABLE AGE
guestbook 3/3 3 3 9m
Troubleshooting
- Application stuck in
Unknown/ComparisonError: Argo CD cannot reach the repo. For private repos add credentials withargocd repo addor a repository Secret; for public repos verify therepoURLhas no typo. OutOfSyncbut auto-sync never runs: thesyncPolicy.automatedblock is missing or misindented inapp.yaml. Re-apply and checkkubectl describe application guestbook -n argocd.- Pods
ImagePullBackOffin guestbook namespace: the kind node cannot pull from ghcr.io. Test withdocker pull ghcr.io/argoproj/argocd-example-apps/guestbook-ui:latestand check your network/proxy. - Port-forward to the UI drops immediately: argocd-server is not Ready yet. Run
kubectl get pods -n argocdand wait for all pods Running before forwarding. - Self-heal does not revert your manual change: self-heal triggers on the next reconciliation (up to a few seconds for live resources, but app refresh is every 3 minutes). Force it with a UI Refresh or
kubectl annotate application guestbook -n argocd argocd.argoproj.io/refresh=normal.
Cleanup
kubectl delete -f app.yaml
kubectl delete namespace guestbook --ignore-not-found
kubectl delete namespace argocd
kill %1 2>/dev/null || true
kind delete cluster --name argocd-lab
gh repo delete YOUR_USER/gitops-demo --yes
cd .. && rm -rf gitops-demo