What changed, and why
Glassbox leaves the zo-k8s cluster for Vercel. After this PR, Terraform's whole footprint for the site is the one thing it already owned: a DNS record. The glassbox.mseeks.me A record, which pointed at the ingress load balancer, becomes a CNAME at Vercel's edge. And the infra/k8s/glassbox stack goes away entirely, 219 lines of namespace, Deployment and Service, Ingress, install script, and README.
The deliberate non-change deserves a sentence. There is no vercel Terraform provider here. Managing the project declaratively would put a long-lived API token in infra/.env and add provider state to reconcile, and the payoff is thin for one static site whose GitHub-app authorization happens in a browser regardless. The dashboard owns the project. Terraform keeps the zone.
The app-repo half is mseeks/glassbox#20: Dockerfile and nginx out, vercel.json in.
dns.tf: the A record becomes a CNAME
The old record had a moving part. Its value came off data.digitalocean_loadbalancer.ingress, the live lookup that keeps every cluster host pointed at whatever IP the load balancer currently holds. The new record is plain data. Vercel's CNAME target is a fixed name, so nothing needs tracking. That is also why the TTL rises from 300 to 3600: the short TTL existed to ride out LB recreation, and no LB sits in this path anymore. TLS changes hands too. Vercel terminates at its own edge, which takes cert-manager out of the loop for this host.
Comment lines move too. The load-bearing ynab record cites hosts that lean on it for cert-manager's HTTP-01 self-check, glassbox among them until now, and the static record's two "same as ynab/glassbox" asides slim down the same way.
The hairpin host list (glassbox removed) and the new CNAME record with its cutover-ordering comment.
infra/dns.tf · 398 lines
⋯ 73 lines hidden (lines 1–73)
⋯ 12 lines hidden (lines 81–92)
⋯ 287 lines hidden (lines 112–398)
The k8s stack, retired
Five files under infra/k8s/glassbox/ disappear, 219 lines in all. There is no one-for-one replacement. Vercel absorbs most of the work, while a couple of jobs (probes, the namespace itself) end with the pod they served:
| file | what it did | replaced by |
|---|---|---|
manifests/00-namespace.yaml | the glassbox namespace | nothing to isolate |
manifests/10-deployment.yaml | one nginx pod serving ghcr.io/mseeks/glassbox:latest (Recreate, /healthz probes, 16Mi request) and its Service | Vercel's edge serves the build output |
manifests/20-ingress.yaml | routed glassbox.mseeks.me through the DO LB; cert-manager filled glassbox-mseeks-me-tls via letsencrypt-prod | Vercel terminates TLS itself |
install.sh | context preflight, kubectl apply, rollout restart + status | git push — the integration deploys |
README.md | how the stack was wired | the app repo's new Deploy section |
Doc sweep: glassbox was everyone's example
Glassbox was the example everything else pointed at. The cert-manager README used its Ingress as the live sample, the sibling stacks compared themselves to it, and dns.tf counted it among the hairpin hosts. Eight spots get small swaps, five READMEs and three dns.tf comments, so no doc references a deleted directory:
| file | swap |
|---|---|
cert-manager/README.md | live example is now static's Ingress (static.mseeks.me → static-mseeks-me-tls) |
static/README.md | drops the "Like glassbox" comparison; describes itself standalone |
2ls/README.md | "same shape as static/glassbox" → "same shape as static" |
everwhen/README.md | "Unlike glassbox/static" → "Unlike static/2ls" |
ingress-nginx/README.md | example Ingress host is now static.mseeks.me |
dns.tf (comments) | out of the hairpin host list, and out of the static record's two "same as ynab/glassbox" asides |
The requirements stay generic; only the live example moves to a host that still terminates TLS in-cluster.
infra/k8s/cert-manager/README.md · 52 lines
⋯ 26 lines hidden (lines 1–26)
⋯ 16 lines hidden (lines 37–52)
Verification, and the cutover runbook
terraform fmt -check, init, and validate pass on the branch, and terraform plan against live state shows the intended replacement, with one unrelated change beside it:
digitalocean_record.mseeks_me_glassboxmust be replaced —type: "A" -> "CNAME"(forces replacement),value: "174.138.116.75" -> "e24be5a7fd9d016f.vercel-dns-017.com.",ttl: 300 -> 3600.
That second change is drift: the DOKS version data source now returns 1.36.0-do.2, so a plain apply would also patch-bump the cluster. That upgrade is not this PR's business — the cutover apply targets the record (terraform apply -target=digitalocean_record.mseeks_me_glassbox), leaving the cluster bump as its own deliberate decision.
The cutover, in order:
1. Create the Vercel project (import mseeks/glassbox; the Vite preset covers build and output), let it deploy, and attach the glassbox.mseeks.me domain. The dashboard mints the project's CNAME target at that moment; the committed value is the one it issued. 2. Merge both PRs; apply the targeted record flip. 3. Verify: a hashed asset serves immutable, a nonsense path serves the app shell, and the response says Vercel, not nginx. 4. kubectl delete namespace glassbox — the pod, Service, Ingress, and cert secret go with it, and the cluster frees the pod's small reservation.