Files
k8s-starter-pack/docs/05-metallb.md
T
Maxwell MorrisandClaude Sonnet 5 92c3ad3191 Initial commit: GitOps Starter Pack
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>
2026-08-22 17:30:45 +01:00

6.9 KiB
Raw Blame History

05 — MetalLB: getting real IP addresses on bare metal

The app-of-apps you just applied (docs/04-app-of-apps.md) already brought up metallb alongside demo. This doc explains what it does, walks you through the one piece only you can decide (which IP addresses to actually use), and finishes by giving Argo CD's own UI a real address too.

Why this is needed at all

On a cloud provider, a Kubernetes Service of type: LoadBalancer triggers the cloud to provision an actual load balancer and hand your Service a real, routable IP automatically. Bare metal — your Talos node, sitting on your own network — has no such thing behind it. Without help, a LoadBalancer Service on bare metal just sits with its address stuck at <pending> forever.

MetalLB is that help: software that runs inside your cluster, watches for LoadBalancer Services, and hands them a real address from a range you configure — then makes that address actually reachable on your network. It's the standard, widely-used answer to this exact gap, which is why it's installed as a pre-installed app in this repo rather than left as a "next step."

apps/demo/service.yaml is already type: LoadBalancer — that's what you saw sitting at <pending> if you looked closely in the last doc. This doc is what turns that into a real, reachable address.

What's already running

apps/metallb.yaml → one Application, pointing at apps/metallb, a Helm chart that does two things at once (see apps/metallb/Chart.yaml):

  • Pulls in the official upstream MetalLB chart as a dependency — this is also the real, working example of a Helm-based app mentioned in docs/07-adding-new-apps.md.
  • Carries two templates of its own (apps/metallb/templates/): an IPAddressPool and an L2Advertisement — MetalLB's own custom resources, which tell it which addresses it's allowed to use and how to advertise them. Right now these still contain placeholder IP addresses that won't work on your network — that's what you're about to fix.

Both the upstream chart and our two extra resources sync together as one Application. Look at the sync-wave annotation on apps/metallb/templates/ipaddresspool.yaml — it's what keeps this safe despite being one sync: MetalLB's own resources (including its CRDs) apply first, and Argo CD waits for them to be healthy before applying our two resources, which depend on those CRDs existing.

Answer three questions before editing anything

Ask Claude: literally ask — "help me pick MetalLB's IP range and the addresses for Argo CD and the demo app." This is the one part of this whole repo that depends entirely on your network, which nothing in this repo can know in advance. Have your router's admin page or DHCP settings handy; Claude will ask what your LAN's subnet is and what range your DHCP server already hands out, so the pool it suggests doesn't collide with anything.

You need three decisions:

  1. The pool range — a handful of addresses (5–10 is plenty for this repo) on your LAN's subnet that your DHCP server will never hand to anything else. Commonly a slice just above or below your DHCP range, e.g. if your router hands out .100–.199, something like .240–.250 is a safe, unused slice.
  2. Argo CD's UI address — one address from that pool.
  3. The demo app's address — a different address from that pool.

Edit the placeholders

  1. apps/metallb/templates/ipaddresspool.yaml — replace the addresses range with your chosen pool.
  2. apps/demo/service.yaml — replace the metallb.universe.tf/loadBalancerIPs annotation with the demo app's chosen address (must be inside the pool from step 1).
  3. docs/network.md — record the pool range and the demo app's address in the tables there (Argo CD's address goes in once you've picked it below).
  4. Commit and push all three:
    git add apps/metallb/templates/ipaddresspool.yaml apps/demo/service.yaml docs/network.md
    git commit -m "Configure MetalLB's address pool for my network"
    git push
    
  5. Force a sync rather than waiting for the next poll:
    argocd app sync metallb demo
    

Verify

kubectl -n demo get svc demo

EXTERNAL-IP should now show the address you chose instead of <pending>. Visit http://<that-address> in a browser — same "It works!" page as before, now on its own real IP instead of a NodePort.

kubectl -n metallb-system get ipaddresspools,l2advertisements

Confirms your pool and advertisement were applied as expected.

Give Argo CD's UI a real address too

So far you've reached the Argo CD UI through kubectl port-forward (see docs/03-installing-argocd.md). Now that MetalLB is running, you can give it a permanent address instead. Argo CD's own install isn't managed through this repo's GitOps loop (it's the imperative bootstrap step — see docs/03), so this one change is applied directly rather than through Git:

kubectl -n argocd patch svc argocd-server --type merge -p \
  '{"spec":{"type":"LoadBalancer"},"metadata":{"annotations":{"metallb.universe.tf/loadBalancerIPs":"<ARGOCD_IP>"}}}'

Replace <ARGOCD_IP> with the address you chose in step 2 above. Then:

kubectl -n argocd get svc argocd-server

Once EXTERNAL-IP shows your chosen address, visit https://<that-address> directly — no port-forward needed from now on. (You'll still hit the same self-signed certificate warning as before; see docs/03-installing-argocd.md for why that's expected here.)

Update docs/network.md one more time with this address — commit and push it. This one's a genuine exception to "everything's driven by Git": the patch command above isn't a file in this repo, so this file is the only place this address gets recorded at all.

Ask Claude: "why did the demo app get its LoadBalancer IP through Git, but Argo CD's didn't?" — a good check that the boundary between "managed by this repo" and "the imperative bootstrap that starts it" has actually landed.

Checklist

  • Chosen a pool range, an Argo CD UI address, and a demo app address, with Claude's help, that fit your actual network
  • apps/metallb/templates/ipaddresspool.yaml edited to your real range
  • apps/demo/service.yaml edited to your real demo address
  • docs/network.md updated with the pool range and both addresses
  • Committed, pushed, synced
  • kubectl -n demo get svc demo shows a real EXTERNAL-IP, page loads in a browser
  • argocd-server patched to LoadBalancer, reachable at its own address without port-forwarding

Continue to docs/06-your-first-change.md.