// writeups
Self-hosting a portfolio on bare-metal k3s
This site is served from a single-node k3s cluster on repurposed hardware. There are zero inbound ports: a Cloudflare Tunnel dials out from the box, so nothing is exposed to the internet directly — the origin has no public IP surface at all.
GitOps, not SSH
Deploys are a merge, not a manual step. A push to main triggers a GitHub Actions build that pushes an image to GHCR and pins the tag into a kustomization; ArgoCD on the box reconciles the cluster to match git:
push main → CI build → GHCR image → pin kustomization [skip ci] → ArgoCD sync → rolling updateThe frontend web app syncs automatically via ArgoCD; the Go core, being deploy-sensitive, is applied by the operator after CI pins it. Caddy sits in front as the in-cluster reverse proxy with flush_interval -1 so Server-Sent Events stream through unbuffered.
Observability on display
Prometheus, Grafana and Loki run in an observability namespace and feed the public /system page — real request rate, p99 latency, resource usage, a deploy feed wired to the CI pipeline, and a redacted tail of the platform's own logs. The metrics you see are the box serving you the page.
Everything is in the repo
infra/k8s/— namespaces, deployments, services, NetworkPolicies, RBAC.infra/argocd/— the GitOps applications.infra/cloudflared/— the tunnel config..github/workflows/— the build + deploy pipelines.
The /infra page renders that same IaC on display — the manifests, the workflow, the tunnel — annotated. It's a portfolio that documents its own provisioning.