Files
bztmon-site/src/content/blog/shipping-this-site.md
T
jwright 22f482d89a M2: content collections — case studies, blog, RSS, tags, sitemap
Projects + blog as schema-validated content collections; structured case
studies (problem/design/outcome), blog with tag pages, reading time, RSS
feed (drafts excluded), sitemap, and Shiki dual-theme code highlighting.
2026-06-17 16:56:46 +10:00

1.8 KiB

title, date, summary, tags
title date summary tags
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.
gitops
astro
homelab
security

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

  1. The site is built and baked into a hardened nginx-unprivileged image.
  2. The image is pushed to a self-hosted public Gitea registry — deliberately separate from the private instance that holds my infrastructure code.
  3. The image digest is pinned in a private home-ops repo.
  4. ArgoCD reconciles that repo onto the cluster.
  5. 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.