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.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "Shipping this site: GitOps from a homelab to the public internet"
|
||||
date: 2026-06-15
|
||||
summary: "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."
|
||||
tags: ["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:
|
||||
|
||||
```text
|
||||
[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.
|
||||
Reference in New Issue
Block a user