Steven's Knowledge

Getting Started

Install ArgoCD on kind, bootstrap from a Git repo, deploy an app, watch reconciliation, rollback via git revert

Getting Started

This page sets up ArgoCD on a local cluster, points it at a Git repo, and walks the full loop: push a change, watch ArgoCD sync it, break the cluster, watch ArgoCD repair it, rollback via Git.

Prerequisites

  • A Kubernetes cluster (kind is fine for learning):

    kind create cluster --name gitops
    kubectl cluster-info --context kind-gitops
  • kubectl, argocd CLI:

    brew install argocd  # or: curl -sSL https://argo-cd.s3.amazonaws.com/release/argocd-linux-amd64 -o /usr/local/bin/argocd && chmod +x /usr/local/bin/argocd
  • A Git repo you can push to (GitHub, GitLab, your own Gitea — anywhere ArgoCD can reach).

Step 1: Install ArgoCD

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Wait for it
kubectl wait --for=condition=available --timeout=300s deployment/argocd-server -n argocd

Port-forward the UI:

kubectl port-forward svc/argocd-server -n argocd 8080:443 &

Get the initial admin password:

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

Login at https://localhost:8080 (user: admin) or via CLI:

argocd login localhost:8080 --insecure
# Change the password
argocd account update-password

Step 2: Prepare a Git Repo

Make a repo (or use an existing one):

my-gitops-config/
└── apps/
    └── hello/
        ├── deployment.yaml
        └── service.yaml

apps/hello/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  namespace: hello
spec:
  replicas: 2
  selector: { matchLabels: { app: hello } }
  template:
    metadata: { labels: { app: hello } }
    spec:
      containers:
        - name: hello
          image: nginxdemos/hello:plain-text
          ports: [{ containerPort: 80 }]

apps/hello/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: hello
  namespace: hello
spec:
  selector: { app: hello }
  ports: [{ port: 80 }]

Commit + push.

Step 3: Create the ArgoCD Application

kubectl create namespace hello

argocd app create hello \
  --repo https://github.com/youruser/my-gitops-config.git \
  --path apps/hello \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace hello \
  --sync-policy automated \
  --auto-prune \
  --self-heal

Or, store the Application itself in Git as argocd/applications/hello.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: hello
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/youruser/my-gitops-config.git
    path: apps/hello
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: hello
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

And apply: kubectl apply -f argocd/applications/hello.yaml. The "app-of-apps" pattern bootstraps from this.

Step 4: Watch the Sync

In the UI: refresh, you should see hello as Synced and Healthy. The tree view shows the Deployment, ReplicaSet, Pods, Service.

CLI:

argocd app get hello
argocd app sync hello   # manual trigger if needed

Test the app:

kubectl port-forward svc/hello -n hello 8000:80 &
curl http://localhost:8000

Step 5: Change Something via Git

In apps/hello/deployment.yaml, bump replicas:

spec:
  replicas: 5

Commit + push. Within 3 minutes (default polling) — or instantly if your repo has a webhook to ArgoCD — you'll see ArgoCD sync. The cluster scales to 5.

kubectl get pods -n hello

This is GitOps. Git changed; cluster followed.

Step 6: Watch Self-Healing

Pretend to be a rogue operator:

kubectl scale deployment hello -n hello --replicas=1

For about 60 seconds you have 1 pod. Then ArgoCD detects drift (cluster ≠ Git) and reconciles back to 5. The Git state wins.

Try deleting the service:

kubectl delete svc hello -n hello

It gets recreated. Self-heal is on.

Step 7: Rollback

Push a deliberate bad change (image doesn't exist):

image: nginxdemos/hello:does-not-exist

Commit + push. ArgoCD syncs; pods crash-loop. Now revert in Git:

git revert HEAD
git push

ArgoCD re-syncs to the previous good state. Rollback is just Git history.

CLI alternative if you can't wait for repo polling:

argocd app rollback hello   # picks last healthy revision

Step 8: App of Apps

A common pattern is bootstrapping multiple apps from one root Application.

argocd/root.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/youruser/my-gitops-config.git
    path: argocd/applications
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Anything you put in argocd/applications/*.yaml (more Applications) becomes a managed app. One kubectl apply -f root.yaml and the entire platform follows.

Equivalent in Flux

Same idea, different style:

# Install
brew install fluxcd/tap/flux

# Bootstrap (writes Flux manifests to your repo)
flux bootstrap github \
  --owner=youruser \
  --repository=my-gitops-config \
  --branch=main \
  --path=clusters/gitops-cluster

# Add a Kustomization pointing at apps/hello
flux create kustomization hello \
  --source=GitRepository/flux-system \
  --path=./apps/hello \
  --prune=true \
  --interval=1m

Flux uses GitRepository, Kustomization, HelmRelease CRDs instead of ArgoCD's Application. The discipline is identical.

Cleanup

kind delete cluster --name gitops

What's Next

You have a working GitOps loop. Next:

  • Patterns — app-of-apps, ApplicationSets, secrets, multi-cluster
  • Best Practices — repo structure, RBAC, DR, scaling

On this page