copy pass: straighten quotes and arrows, thin the dash asides, drop the app-count caption from the constellation
build-and-deploy / build (push) Failing after 13m18s
build-and-deploy / build (push) Failing after 13m18s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
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."
|
||||
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"]
|
||||
hero: "/blog/shipping-this-site.webp"
|
||||
heroAlt: "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."
|
||||
@@ -10,16 +10,16 @@ heroAlt: "A glowing data container travels a luminous rail from a small server r
|
||||
|
||||
This site is a static Astro build, and that's the least interesting thing about it. What matters
|
||||
is how it reaches you: served from my homelab Kubernetes cluster over a Cloudflare Tunnel, shipped
|
||||
the way I'd ship anything I actually cared about — an immutable image, pinned by digest, reconciled
|
||||
the way I'd ship anything I actually cared about - an immutable image, pinned by digest, reconciled
|
||||
by GitOps. No special case for "it's just a website."
|
||||
|
||||
## The pipeline
|
||||
|
||||
The build is baked into a hardened `nginx-unprivileged` image and pushed to a **self-hosted public
|
||||
Gitea registry** — deliberately a separate instance from the private one holding my infrastructure
|
||||
Gitea registry**, deliberately a separate instance from the private one holding my infrastructure
|
||||
code, so the public artifact and the private estate never share a trust boundary. The image digest
|
||||
is then pinned in a private `home-ops` repo, **ArgoCD** reconciles that repo onto the cluster, and
|
||||
a **Cloudflare Tunnel** exposes exactly one service — this site — outbound-only.
|
||||
a **Cloudflare Tunnel** exposes exactly one service, this site, outbound-only.
|
||||
|
||||
Follow the chain and notice what's missing. No open ports: the tunnel dials out. No server runtime:
|
||||
the output is static files behind nginx. No registry credential on the cluster: the public package
|
||||
@@ -32,9 +32,9 @@ The discipline that made it work was treating security as a checklist to *pass*,
|
||||
gesture at. The site didn't ship until every box was ticked:
|
||||
|
||||
```text
|
||||
[x] Static output — no server runtime to attack
|
||||
[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] 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
|
||||
```
|
||||
@@ -46,7 +46,7 @@ verify, and a failing item blocks the ship. "Pretty secure" isn't a state you ca
|
||||
## The principle
|
||||
|
||||
The site *is* the argument. A platform engineer's portfolio should demonstrate the discipline it
|
||||
advertises, and "it's a static page" is no excuse to skip the rigour — it's the cheapest possible
|
||||
advertises, and "it's a static page" is no excuse to skip the rigour - it's the cheapest possible
|
||||
place to practise it. If the pipeline behind a brochure site is immutable, verified, and
|
||||
zero-trust, that's not overkill. That's the standard, rehearsed where the stakes are low so it
|
||||
holds where they aren't.
|
||||
|
||||
Reference in New Issue
Block a user