A teaching-first, single-cluster GitOps setup: Talos Linux + Argo CD with an app-of-apps layout, MetalLB pre-installed for real LoadBalancer IPs on bare metal, a demo app, and a progressive docs/ walkthrough with a CLAUDE.md tutor persona and guided bootstrap checklist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.1 KiB
04 — The app-of-apps pattern
This is the last manual step. After this, Git drives everything.
The idea
An Argo CD Application is a resource that says "watch this path in this
Git repo, and keep the cluster matching what's there." The simplest
possible setup is one Application per app you want to run — but then every
new app means one more manual kubectl apply, which quietly puts you back
to making changes by hand.
App-of-apps fixes this by making Applications themselves something Argo
CD manages. One Application — the app-of-apps — watches a folder
(apps/) full of other Application manifests. Add a new app by
adding a new file to that folder and pushing it — the app-of-apps notices
the new file and creates the Application it describes, which in turn
deploys the app. No kubectl apply required past this point, ever, for a
new app.
argocd/app-of-apps/app-of-apps.yaml (you apply this ONE file, by hand, once)
│
│ watches apps/, non-recursively
├──────────────┬───────────────┐
▼ ▼ ▼
apps/demo.yaml apps/metallb.yaml (more, later — just add a file)
│ │
▼ ▼
apps/demo/*.yaml apps/metallb/* (the actual manifests / Helm chart)
Three layers, each watching the next. You only ever touch the bottom two —
the app-of-apps is set-and-forget. Notice apps/metallb.yaml is already
here — see docs/05-metallb.md right after this one.
Why apps/ isn't recursive
Look at argocd/app-of-apps/app-of-apps.yaml
— specifically directory.recurse: false. The apps/ folder contains both
Application manifests (apps/demo.yaml, apps/metallb.yaml) and
subfolders holding the real workload manifests those Applications point to
(apps/demo/*.yaml, apps/metallb/*). Non-recursive means the
app-of-apps only ever looks at files directly inside apps/, never inside
its subfolders — so it only ever sees Application definitions, and leaves
the actual workload manifests to the Application that owns them. Without
this, the app-of-apps would try to apply raw Deployments and Helm chart
files itself, duplicating and conflicting with what those Applications are
already doing.
Every Application needs its own repoURL
Applications don't inherit repoURL from whatever applied them — each one
declares its own source repo independently. That means the placeholder
https://github.com/YOUR_GITHUB_USERNAME/... needs fixing in every file
that has it, not just the one you apply by hand:
grep -rl YOUR_GITHUB_USERNAME .
You should see three files: argocd/app-of-apps/app-of-apps.yaml,
apps/demo.yaml, and apps/metallb.yaml. Edit spec.source.repoURL in
each to point at your fork.
Steps
- Fix
repoURLin all three files (above). - Commit and push — this part matters more than it looks: the
app-of-apps reads
apps/demo.yamlandapps/metallb.yamlfrom Git, not from your local edits, so they have to be pushed before you apply anything.git add argocd/app-of-apps/app-of-apps.yaml apps/demo.yaml apps/metallb.yaml git commit -m "Point Applications at my fork" git push - Apply the app-of-apps — the one manifest you apply by hand:
kubectl apply -f argocd/app-of-apps/app-of-apps.yaml - Watch it sync, then watch
demoandmetallbappear underneath it:You should seeargocd app listapp-of-apps,demo, andmetallb, allSyncedandHealthywithin a minute or two (MetalLB's own install takes a little longer than the demo app). - Confirm the demo app is actually running:
You'll notice the Service's
kubectl -n demo get pods,svcEXTERNAL-IPcolumn says<pending>— that's expected. The demo app is aLoadBalancerService, and nothing hands those out real addresses on bare metal until MetalLB is configured, which is exactly the next doc.
If app-of-apps, demo, and metallb all show Synced/Healthy, the
loop — Git → Argo CD → cluster — is already proven end to end; you just
can't see the demo app in a browser yet.
Ask Claude: "walk me through exactly what happened, in order, from the moment I ran
kubectl apply -f argocd/app-of-apps/app-of-apps.yamlto these three Applications existing." Making sure you can narrate that chain yourself is the real checkpoint for this doc — everything from here on is a variation on it.
Checklist
repoURLfixed in all three files listed above- Fix committed and pushed
app-of-appsapplied and showsSynced/HealthydemoandmetallbApplications appeared automatically, bothSynced/Healthykubectl -n demo get pods,svcshows the demo app's Pod running (ServiceEXTERNAL-IPstill<pending>— expected for now)
Continue to docs/05-metallb.md to give the demo app —
and Argo CD's own UI — a real address.