From git push to running service — the portal's own delivery pipeline
Context
The homelab's public portal didn't start as a full website — it started as proof that a change to code could travel, unassisted, from a commit to a running service on the cluster. Before investing in real content, I wanted the full delivery chain working end to end, on the simplest possible payload.
The problem
Two things mattered from the start, on top of "does it work":
- A repo layout that could absorb later work (health checks, authentication, a self-service catalog) without a rewrite each time a new feature showed up.
- Proof that the whole path — build, publish, deploy — could run without a human doing any of the steps by hand.
What I built
A three-stage pipeline: build a container image on every push, publish it to a private registry, then apply it to the cluster. A dedicated namespace, a single replica to start. Once the skeleton proved itself, the project's folder structure (routes, services, configuration) sat ready and empty — deliberately — for what came next. That anticipation paid off: when I later split the portal into a public showcase and an authenticated admin area, that work filled the existing structure instead of reorganizing it.
Authentication came next, delegated to an identity provider using a standard protocol (OpenID Connect) rather than hand-rolled. At first it protected the entire site; later I inverted that, so only the admin area requires a login and the public pages are open by default — a public showcase and a gated control room, not one undifferentiated site behind a wall.
Pitfalls worth remembering
- A build runner that couldn't actually build. The automation runner had none of git, container access, or working name resolution to the registry — a reminder that "the pipeline is written" and "the pipeline works" are two different claims.
- A token that looked right but wasn't enough. The automatically generated credential the pipeline uses by default doesn't have registry-push rights; a dedicated, scoped token was needed.
- A typo that failed silently. A misspelled secret name meant the login step ran with empty credentials — no crash, just a quiet failure downstream.
- Cluster nodes that couldn't resolve the registry's name. Fixed with a static resolution entry versioned in code, with an explicit note that the real fix is a proper internal DNS server — a piece of homelab plumbing still being built at the time.
- A container that refused to start under a strict security profile. Requiring a named non-root user isn't enough for the cluster to enforce it — it needs a numeric identity, verified empirically rather than assumed.
Where it stands
The pipeline is unremarkable by design — that's the point. Every later feature (login, a self-service catalog, a graphical console into a provisioned VM) rode on this same three-stage skeleton without needing to touch it again.