Lesson 09 of 9 · Hands-on lab
Hands-on lab: Argo CD deploying to a second cluster
A free, code-first lab that puts this whole course into practice: Argo CD on a tools cluster deploys to a second (target) cluster from a Git repo, through 13 short steps from registering the cluster to ApplicationSets, RBAC, notifications and webhooks.
Open the lab on GitHub13 steps · each folder has a short README (goal → run → expect → break it) and the code
What you'll build
The argocd-gitops-lab is a free, hands-on companion to this course. Argo CD runs on one cluster (the "tools" cluster) and deploys to another (the "target" cluster), from a Git repo and a registry on a third machine:
git server + registry Argo CD (tools cluster) target cluster
(repo: deploy-demo, ◄─ polls ──┤ Application hello-web ── apply ──► API :6443
image: hello-web) │ │ nodes
▲ │
└────────────────────── image pull (containerd) ────────────────┘
Two repos, on purpose: the app repo holds code and CI builds the image (that's the companion gitea-action-lab, step 05); the deploy repo holds only the desired state that Argo CD applies, and needs no pipeline at all.
A model railway with a control room. The control room (Argo CD) has the timetable (Git) and remote control of a layout in the next room (the target cluster). Change the timetable, and the trains follow it; push a train off the track by hand, and the control room puts it back.
What you need
- A tools cluster with Argo CD installed (plain manifests or the Helm chart), and its UI reachable.
- A target cluster you have a kubeconfig for, reachable from the Argo CD pods.
- A Git server and registry (the lab uses Gitea for both).
- The
hello-webimage, built by the CI lab or any small web image of your own.
Single-node k3s clusters on small VMs are enough for every step.
The 13 steps, mapped to this course
| Step | Folder | You practise | Course lesson |
|---|---|---|---|
| 00 | 00-register-cluster |
CLI login, argocd cluster add, everyday CLI |
02 · Installing Argo CD & the first app |
| 01 | 01-registry-trust |
Target nodes pulling from your registry (containerd hosts.toml) |
02, and Docker & Containers, lesson 09 |
| 02 | 02-first-app |
Deploy by git push; Synced · Healthy in the UI tree |
02 |
| 03 | 03-drift-selfheal-prune-rollback |
Self-heal, drift detection, prune, rollback with git revert |
06 · Sync policies, hooks & waves |
| 04 | 04-health |
Synced but Degraded; Synced but Progressing | 08 · Troubleshooting |
| 05 | 05-helm-kustomize |
Dev/prod overlays and a Helm chart from one repo | 04 · Helm & Kustomize |
| 06 | 06-sync-hooks |
PreSync migration, waves, PostSync smoke test | 06 |
| 07 | 07-appset-folders |
One folder = one app (Git directory generator) | 05 · Multi-cluster management |
| 08 | 08-appset-repos |
One repo = one app (SCM-provider generator) | 05 |
| 09 | 09-multi-cluster |
One app on every cluster; then a full four-cluster platform: least-privilege access, projects per team, sync windows, RBAC with a tested permission matrix | 05 |
| 10 | 10-rbac-projects |
AppProject + RBAC: sync only your team's apps | 07 · Production patterns |
| 11 | 11-notifications |
Messages on sync and on Degraded | 07 |
| 12 | 12-webhook-instant-sync |
Sync on push instead of polling | 07 |
Every folder follows the same pattern: goal → run → expect → break it. The "break it" part is where most of the learning happens.
Highlights worth knowing before you start
- Register clusters with the CLI. The UI can't add a cluster;
argocd cluster add <context>stores a credential for it. The context is the NAME column ofkubectl config get-contexts, not the cluster name. The cluster showsUnknownuntil the first app targets it; that's normal. - Nodes pull images themselves. If the registry is plain HTTP, every node's containerd must be told to trust it, or pods sit in
ImagePullBackOff. The lasting fix is TLS on the registry. - Alert on Health, not Sync. A wrong image tag is perfectly "Synced"; it's Health that turns Degraded.
- Argo CD renders Helm itself.
helm liston the target shows nothing, because Argo CD applies rendered manifests, not Helm releases. - Rollback is
git revert. A UI rollback needs auto-sync off, and the next sync undoes it. - SCM-provider scans are slow by default. New repos are discovered every 30 minutes unless you tune
requeueAfterSecondsor trigger a refresh. Changes inside existing apps still sync on the normal poll or webhook.
When something breaks
The lab ships a TROUBLESHOOTING.md built from errors actually hit while writing it: CLI argument mistakes, empty hosts.toml files, ApplicationSets applied before the repo had a commit, SSH clone URLs from an unconfigured Git server, and scanners pointed at a user instead of an organisation. Combine it with lesson 08 of this course.
Run the series end to end
- Do the gitea-action-lab first: it builds and pushes the
hello-webimage tagged with a commit SHA. - Then work through steps 00–12 of argocd-gitops-lab in order.
- Finish by changing the image tag in the deploy repo and watching the new version roll out to the target cluster, with a webhook making it near-instant.
Recap
- One Argo CD on a tools cluster deploys to a target cluster from Git: hub and spoke.
- 13 short steps cover registration, registry trust, first app, drift and rollback, health, Helm/Kustomize, hooks, ApplicationSets, multi-cluster, RBAC, notifications and webhooks.
- Each step is goal → run → expect → break it; pair it with the course lesson in the table.
- Do the CI lab first, then this CD lab, for the full commit-to-cluster loop.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.