Adapting This for Your Cluster¶
This repository documents one specific homelab. Node names, addresses, the
wlkr.ch domain, and the Git repository URL are hardcoded throughout
payload/ and ansible/. Run the Quickstart unchanged and you
get a cluster that syncs from this repository and requests certificates for a
domain you don't control.
Everything below is a change you make in your own fork, before step 1 of the Quickstart.
1. Fork and repoint ArgoCD¶
Every ArgoCD Application points at this repository by URL. Until you change
them, your cluster pulls its desired state from here and your own commits have
no effect.
Rewrite them to your fork:
git grep -lz 'github.com/JanWelker/homelab' -- payload/ | \
xargs -0 sed -i '' 's|github.com/JanWelker/homelab|github.com/YOUR_USER/YOUR_REPO|g'
Note
sed -i '' is the BSD/macOS form. On Linux use sed -i with no argument.
If your fork is private, ArgoCD also needs repository credentials — see the ArgoCD private repository docs.
2. Choose your domain¶
Two DNS zones carry all traffic:
| Pattern | Purpose |
|---|---|
*.k8s.<your-domain> |
user-facing workloads, via apps-gateway |
*.infra.k8s.<your-domain> |
platform UIs (ArgoCD, Grafana, Hubble, OpenBao, Rook), via infra-gateway |
Replace the domain everywhere:
That covers the Gateway hostnames, the wildcard Certificate resources, every
HTTPRoute, ArgoCD's global.domain, and the
link.argocd.argoproj.io/external-link annotations.
You must own this domain: the certificates are issued by Let's Encrypt through
a DNS-01 challenge, which requires write access to the zone. .local, .lan
and made-up TLDs will not work.
3. Point DNS at the gateway IPs¶
The two Gateways take fixed addresses from the Cilium L2 pools in
payload/platform/cilium/lb-pools.yaml:
| Gateway | DNS record |
|---|---|
apps-gateway |
*.k8s.<your-domain> |
infra-gateway |
*.infra.k8s.<your-domain> |
Change both CIDRs to free addresses on your LAN — in the nodes' subnet, since
Cilium announces them over L2 ARP, and outside the DHCP range, or the router
will eventually hand one to a laptop — then create the two wildcard A
records.
4. Set up the DNS-01 solver¶
payload/platform/certificates/cluster-issuers.yaml is written for AWS
Route53. Update:
email:— your address, on both issuers. Let's Encrypt sends expiry notices here.region:— the Route53 region.
If your DNS is hosted elsewhere, replace the dns01.route53 solver with the
matching cert-manager DNS-01 provider
and adjust the credential path in OpenBao accordingly.
Tip
Switch issuerRef in certificates.yaml to letsencrypt-staging while you are still iterating. Production allows 5 duplicate certificates per week, and a misconfigured solver burns through that in minutes.
5. Describe your hardware¶
Edit ansible/inventory.yaml; the variables are listed under
Quickstart → step 2. Beyond those:
| Setting | Notes |
|---|---|
control_plane_vip |
Free address on the nodes' subnet, outside DHCP and distinct from the LoadBalancer pools. Becomes the API endpoint in the cluster certificates. |
control_plane_vip_interface |
The NIC kube-vip advertises on; check ip link on a provisioned node. |
| host entries | Replace the six hosts with yours. Each needs ansible_host (static IP) and mac_address (the NIC that PXE boots). |
control_plane / workers |
Group membership decides the node role. |
pod_subnet, service_subnet |
Only change if they collide with your LAN. |
Then update payload/platform/cilium/values.yaml:
k8sServiceHost— a literal, reachable API server address, because Cilium replaceskube-proxyand cannot reach the API through a Service — see Design Decisions → Cilium.devices— the interface prefix Cilium binds to; checkip linkon a provisioned node.
And payload/platform/kube-vip/daemonset.yaml, which takes over from the
bootstrap static pod once ArgoCD syncs and cannot read the inventory:
address— the same value ascontrol_plane_vip.vip_interface— the same value ascontrol_plane_vip_interface.
A mismatch here does not fail at provisioning. It fails on the first sync, when the DaemonSet removes the static pods and starts advertising somewhere else.
6. Provisioning access¶
ansible/templates/butane_node_config.yaml.j2 injects
~/.ssh/id_ed25519.pub as the authorized key for the core user. Point it at
your own key if you use a different path or algorithm. There is no password,
no console login, and no rescue path short of reprovisioning, so this is the
only way into the nodes afterwards.
7. The documentation site¶
If you want your fork to publish its own copy of these docs, update
zensical.toml: site_url, repo_url, repo_name, and copyright. The
docs.yaml workflow then publishes to your own GitHub Pages. Otherwise, delete
.github/workflows/docs.yaml to stop the build from running.
Checklist¶
Before make config:
- [ ]
repoURLpoints at your fork in all ofpayload/ - [ ] Domain replaced throughout
payload/ - [ ] LoadBalancer IPs are free addresses on your subnet
- [ ] Wildcard DNS records created for both gateways
- [ ] ACME email and DNS-01 provider match your setup
- [ ]
inventory.yamldescribes your nodes, with the rightboot_server_ip - [ ]
control_plane_vipis free, andcontrol_plane_vip_interfacematches the NIC - [ ]
addressandvip_interfacein the kube-vip DaemonSet match those two - [ ]
k8sServiceHostanddevicesmatch your control plane and NICs. On a new build, pointk8sServiceHostatcontrol_plane_viponce the VIP answers — see Control Plane VIP - [ ] SSH public key path is correct
- [ ] Changes committed and pushed — ArgoCD reads from Git, not your working tree