morten/nordbye.it

infrastructure

This site is the case study.

No Vercel, no Netlify. The page you are reading was built by CI, pushed to a registry, and reconciled by ArgoCD onto a self-hosted Talos Kubernetes cluster in Oslo. The status below is read from that cluster.

checking the cluster…

Serving build

72088b9

pinned to commit SHA

ArgoCD app

Synced

healthy · auto-sync

Cluster nodes

6 / 6 ready

Talos Linux · Kubernetes

TLS certificate

auto

auto-renews · Let's Encrypt

the request path

How this page reached you.

Every request crosses this chain. Each hop maps to a manifest in the repo.

You

HTTPS request to nordbye.it

TLS 1.3

DNS + edge

Resolves to the cluster's public entry point

proxied

Cilium LB

L2-announced VIP, eBPF load-balancing into the cluster

LoadBalancer IPAM

Traefik

Gateway API HTTPRoute, certificate from cert-manager

gateway.networking.k8s.io

portfolio pod

Hardened nginx serving the static export

read-only rootfs

the deploy path

How a commit becomes this page.

Nothing is applied by hand. A push to main is the only deploy action that exists. ArgoCD reconciles the rest.

git push

Commit to main in the homelab monorepo

GitHub Actions

Builds the image, scans it, updates the manifest tag in the same run

GHCR

Image pushed and pinned to the commit SHA, never :latest

ArgoCD

Detects the manifest change in Git and syncs the Application

app-of-apps

Talos cluster

Rolling update, zero downtime. You're looking at the result.

genesis

how the numbers get here

A static site with a live pulse.

This site is a static export served by nginx. There is no server-side code to query the cluster. Instead, a small CronJob inside the cluster gathers the facts every few minutes, from the Kubernetes API, ArgoCD, and cert-manager, and publishes a single status.json through the same gateway.

The page fetches it client-side. If the fetch fails, the tiles fall back to a build-time snapshot and say so. The page never breaks because the homelab is having a bad day.

GET /status.jsonrefreshed every 5 min
{
  "generatedAt": "2026-07-12T09:14:02Z",
  "build":      "286dd6c",
  "argocd":     { "sync": "Synced", "health": "Healthy" },
  "nodes":      { "ready": 6, "total": 6 },
  "versions":   { "talos": "v1.11.6", "kubernetes": "v1.34.0" },
  "cert":       { "notAfter": "2026-09-04T08:11:39Z" }
}

the platform underneath

What keeps it honest.

The moving parts behind the diagrams above. Every manifest lives in the homelab repo.

Talos Linux

Immutable, API-managed Kubernetes OS. No SSH, no shell, no drift.

Cilium

eBPF CNI, L2 announcements and LB-IPAM for bare-metal VIPs.

Traefik

Gateway API implementation. HTTPRoutes, not Ingress annotations.

ArgoCD

App-of-apps GitOps. Git is the only write path to the cluster.

cert-manager

ACME certificates issued and rotated without human hands.

External Secrets

Secrets synced from an upstream store. None live in Git.

Prometheus + Grafana

kube-prometheus-stack, with dashboards versioned as code.

Loki + OTel

Log aggregation and traces via the OpenTelemetry collector.