354475a453
build-and-deploy / build (push) Failing after 11m14s
Add an optional hero field to the blog schema; render it on the post page and as the index card thumbnail. Ship 4 verified Nano Banana hero images (shipping/ init-gating/authentik/workload) as optimized webp (64-134KB). Publish the secret-zero post. (outbound hero pending — its file was a dup of shipping; secret-zero hero pending generation.)
2.0 KiB
2.0 KiB
title, date, summary, tags, hero, heroAlt
| title | date | summary | tags | hero | heroAlt | ||||
|---|---|---|---|---|---|---|---|---|---|
| Shipping this site: GitOps from a homelab to the public internet | 2026-06-15 | How this portfolio is built and served — Astro to a container image, a self-hosted Gitea registry, ArgoCD, and a Cloudflare Tunnel — with security as acceptance criteria, not polish. |
|
/blog/shipping-this-site.webp | A glowing data container travels a luminous rail from a small server rack toward a softly glowing globe of Earth, wrapped by a reconcile loop. |
This site is a static Astro build, but how it gets to you is the interesting part. It's served from my homelab Kubernetes cluster over a Cloudflare Tunnel, deployed the same way I'd ship anything else: as an immutable image, pinned by digest, reconciled by GitOps.
The pipeline
- The site is built and baked into a hardened
nginx-unprivilegedimage. - The image is pushed to a self-hosted public Gitea registry — deliberately separate from the private instance that holds my infrastructure code.
- The image digest is pinned in a private
home-opsrepo. - ArgoCD reconciles that repo onto the cluster.
- A Cloudflare Tunnel exposes exactly one service — this site — outbound-only.
No open ports. No server runtime. No registry credential on the cluster, because the public package is anonymous-pull and the image holds nothing secret.
Security as acceptance criteria
The interesting constraint was treating security as a checklist to pass, not a vibe:
[x] Static output — no server runtime to attack
[x] Strict CSP, no unsafe-inline / unsafe-eval
[x] Self-hosted fonts — zero third-party requests
[x] No secrets in the client bundle (verified by build-time grep)
[x] Outbound-only tunnel, single hostname, no catch-all
Why bother
Because the site is the argument. A platform engineer's portfolio should demonstrate the discipline it's advertising — and "it's a static page" is no excuse to skip the rigour. The deployment story is part of the work.