v1.7.0 — Headscale control plane + resume.sh
Highlights
Free, self-hosted VPN mesh for the landing zone via Headscale (BSD-3) — point the official Tailscale mobile apps at http://headscale.local, mesh your phone with the cluster, demo mobile apps against backends running on your laptop. No Tailscale account, no paid tier, no phoning home.
Also ships scripts/resume.sh — fills the gap left by v1.5.1 where the Colima daemon-DNS pin only ran during fresh bootstrap, never on resume.
What's new
Added — Headscale app (app-of-apps pattern)
New Gitea repo platform/headscale carries everything the app needs; the only reference from gitops-infra is gitops-repo/apps/headscale.yaml:
- ConfigMap'd
config.yaml(embedded DERP STUN on UDP 3478, Magic DNS*.landingzone.local, SQLite persistence) - PVC for the DB + private keys
- Deployment (
headscale/headscale:0.26.1, ARM64-native) + Services (ClusterIP for API, LoadBalancer for DERP UDP) + Ingress athttp://headscale.local - Helper scripts:
scripts/create-user.sh,scripts/create-preauth.sh - README covering the registration flow for iOS / Android / laptop Tailscale clients, the subnet-router trick for reaching
172.20.0.0/24from mobile, and the Cloudflare Tunnel path for cross-internet demos
Plus wiring in this repo:
gitops-repo/apps/headscale.yaml— the Application manifestgitops-repo/manifests/argocd/values.yaml— newgitea-headscalerepo credentialsscripts/setup-hosts.sh+scripts/04-create-k3d-cluster.sh—headscale.localin the hosts + CoreDNS lists- Portal card under Your Applications
Added — scripts/resume.sh
Idempotent resume flow after a colima stop / ./teardown.sh cycle:
- Start Colima if not running.
- Merge
dns: ["8.8.8.8", "1.1.1.1"]into/etc/docker/daemon.json(fills the v1.5.1 gap where already-bootstrapped clusters never got the pin). docker compose ... startthe compose stack.k3d cluster start gitops-local, wait for nodes Ready.- Re-patch each k3d node's
/etc/resolv.conf(belt-and-braces — daemon.json catches new containers, node-level patch catches existing ones). - Re-patch CoreDNS NodeHosts for
gitea.local. - Clear ghost Pending pods from the pre-stop state.
Using Headscale
After the app syncs:
# Create a user + pre-auth key (free unlimited)
bash scripts/create-user.sh alice
bash scripts/create-preauth.sh alice --reusable --expiry 168h
# On your phone: install the Tailscale app,
# → Settings → Change server → http://headscale.local
# → Sign in with auth key → paste the preauth key
# → Phone now has a 100.x.x.x Tailscale IPTo reach portal.local / gitea.local:3000 / etc. from the phone, register the laptop as a subnet router (see the app repo's README for the tailscale up --advertise-routes recipe).
For phones on cellular (outside your LAN), Headscale needs a public URL. Deploy Cloudflare Tunnel as a sibling Application — still 100% free for dev.
Upgrading an existing cluster
# If the cluster is stopped, use the new resume script:
./scripts/resume.sh
# Then pull + re-push gitops-infra to get Headscale wired in:
git pull
bash scripts/07-push-gitops-repo.sh
# Flush argocd-repo-server cache and let ArgoCD sync:
kubectl -n argocd rollout restart deploy argocd-repo-serverFull changelog
See CHANGELOG.md.