2026-08-22 17:30:45 +01:00
# Guided bootstrap checklist
A single-page, ordered checklist covering the entire path from bare
2026-08-22 18:25:38 +01:00
machines to a working GitOps loop. It's meant to be worked through **with
2026-08-22 17:30:45 +01:00
Claude**, one item at a time — ask Claude to walk you through this list,
and it will run each step with you, wait for your output, confirm it
worked, and only then move to the next one. See `CLAUDE.md` at the repo
root for how Claude is primed to do this.
Each item names the doc it's drawn from, in case you want the full
2026-08-22 18:25:38 +01:00
explanation before or after running it. Replace `<NODE1_IP>` /`<NODE2_IP>` /`<NODE3_IP>` ,
`<VIP_IP>` , `YOUR_GITHUB_USERNAME` , and the MetalLB IP addresses with your
real values throughout — Claude will remind you if you paste a command
still containing a placeholder, and will ask you for all of these directly
in Phases 2 and 5 rather than guessing them.
2026-08-22 17:30:45 +01:00
## Phase 1 — Prerequisites ([`01`](01-prerequisites.md))
2026-08-22 18:25:38 +01:00
- [ ] Three target machines chosen, each network-reachable, each with a
stable (ideally DHCP-reserved) IP address, each with a second disk
≥900GB set aside for Longhorn
- [ ] Custom Talos schematic built at factory.talos.dev with `iscsi-tools`
and `util-linux-tools` , ISO downloaded
- [ ] All three machines booted from that ISO, sitting at the boot screen
2026-08-22 17:30:45 +01:00
- [ ] `talosctl version --client` runs successfully
- [ ] `kubectl version --client` runs successfully
- [ ] `argocd version --client` runs successfully
- [ ] Repo forked on GitHub and cloned locally as **your own copy**
## Phase 2 — Talos bootstrap ([`02`](02-talos-bootstrap.md), [`talos/README.md`](../talos/README.md))
2026-08-22 18:25:38 +01:00
- [ ] **Ask Claude** to help pick: your three nodes' IP addresses (or
confirm DHCP reservations) and a separate VIP address — all based on
your actual LAN/DHCP setup
- [ ] `talosctl gen config gitops-starter https://<VIP_IP>:6443 --output-dir _out --config-patch-control-plane @talos/patch-controlplane.yaml --config-patch-control-plane @talos/patch-longhorn.yaml` run — `_out/controlplane.yaml` , `worker.yaml` , `talosconfig` exist
2026-08-22 17:30:45 +01:00
- [ ] `TALOSCONFIG` exported (or `talosctl config endpoint` /`node` set) to `_out/talosconfig`
2026-08-22 18:25:38 +01:00
- [ ] For **each** of the three nodes: `talosctl get disks --insecure --nodes <NODE_IP>` confirms two disks, then `talosctl apply-config --insecure --nodes <NODE_IP> --file _out/controlplane.yaml` run, node rebooted
- [ ] `talosctl bootstrap --nodes <NODE1_IP>` run — **exactly once** , against **exactly one** node
- [ ] `talosctl kubeconfig --nodes <VIP_IP> .` run, `KUBECONFIG` pointed at the result
- [ ] `kubectl get nodes` shows **all three** nodes, `Ready`
- [ ] `docs/network.md` updated with all three node addresses and the VIP
2026-08-22 17:30:45 +01:00
## Phase 3 — Argo CD install ([`03`](03-installing-argocd.md), [`argocd/README.md`](../argocd/README.md))
- [ ] `kubectl create namespace argocd`
- [ ] `kubectl apply --server-side -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml`
- [ ] `kubectl -n argocd wait --for=condition=Available --timeout=300s deployment --all` completes
- [ ] Initial admin password retrieved
- [ ] `kubectl -n argocd port-forward svc/argocd-server 8080:443` running in its own terminal
- [ ] `argocd login localhost:8080 --username admin --password <pw> --insecure` succeeds
## Phase 4 — Hand the cluster to Git ([`04`](04-app-of-apps.md))
2026-08-22 18:25:38 +01:00
- [ ] `spec.source.repoURL` fixed in all four placeholder files: run
2026-08-22 17:30:45 +01:00
`grep -rl YOUR_GITHUB_USERNAME .` to find them
2026-08-22 18:25:38 +01:00
(`argocd/app-of-apps/app-of-apps.yaml` , `apps/demo.yaml` ,
`apps/metallb.yaml` , `apps/longhorn.yaml` )
2026-08-22 17:30:45 +01:00
- [ ] Edit committed and pushed to your fork's `main` branch
- [ ] `kubectl apply -f argocd/app-of-apps/app-of-apps.yaml`
- [ ] `argocd app list` shows `app-of-apps` , `Synced` /`Healthy`
2026-08-22 18:25:38 +01:00
- [ ] `argocd app list` shows `demo` , `metallb` , and `longhorn` , appeared
automatically, all `Synced` /`Healthy`
2026-08-22 17:30:45 +01:00
- [ ] `kubectl -n demo get pods,svc` shows the demo app's Pod running
(Service `EXTERNAL-IP` still `<pending>` — expected for now)
## Phase 5 — MetalLB: real IP addresses ([`05`](05-metallb.md))
- [ ] **Ask Claude** to help pick: a MetalLB pool range, an address for
Argo CD's UI, and an address for the demo app — all based on your
actual LAN/DHCP setup
- [ ] `apps/metallb/templates/ipaddresspool.yaml` edited to your real pool range
- [ ] `apps/demo/service.yaml` edited to your real demo app address
- [ ] `docs/network.md` updated with the pool range and both addresses (Argo CD's added once patched, below)
- [ ] Committed, pushed, and synced (`argocd app sync metallb demo` )
- [ ] `kubectl -n demo get svc demo` shows a real `EXTERNAL-IP` ; page loads at `http://<that-address>`
- [ ] `argocd-server` Service patched to `LoadBalancer` with your chosen Argo CD address (`kubectl -n argocd patch svc argocd-server ...` )
- [ ] Argo CD UI reachable at `https://<that-address>` with no port-forward running
2026-08-22 18:25:38 +01:00
## Phase 6 — Longhorn: real storage ([`06`](06-longhorn.md))
- [ ] `kubectl -n longhorn-system get pods` shows manager/CSI Pods
`Running` on all three nodes
- [ ] `kubectl get storageclass` shows a default `longhorn` StorageClass
- [ ] (Optional) Gave the demo app a real PVC and confirmed it's `Bound`
with a healthy, replicated Longhorn volume behind it
## Phase 7 — Prove the loop ([`07`](07-your-first-change.md))
2026-08-22 17:30:45 +01:00
- [ ] Edited the `<h1>` text in `apps/demo/configmap.yaml`
- [ ] Committed and pushed
- [ ] `argocd app sync demo` (or waited for auto-poll) shows the change applied
- [ ] Refreshed the browser — new text visible
- [ ] (Optional) Triggered self-heal: hand-edited the ConfigMap with `kubectl patch` , confirmed Argo CD reverted it
## You're done when
Every box above is checked, you've watched a change you made in a text
editor show up in a browser without ever running `kubectl apply` yourself
for it, and you can explain in your own words what happened between "git
push" and the page updating. If you can't yet, that's the sign to ask
Claude for a walkthrough of whichever phase felt shaky before calling this
finished.
2026-08-22 18:25:38 +01:00
From here: [`08-adding-new-apps.md` ](08-adding-new-apps.md ) to add
something of your own, or [`09-growing-up.md` ](09-growing-up.md ) for the
2026-08-22 17:30:45 +01:00
bigger next steps (more nodes, ingress, secrets management).