Self-hosted Kubernetes platform

A highly available k3s platform for internal applications, with load-balanced ingress, TLS, and push-to-deploy CI/CD.

Traffic path through the k3s platform Clients connect over HTTPS to a virtual IP. Two Nginx load balancers share the virtual IP through keepalived, one active and one on standby. The active balancer forwards traffic to the Traefik ingress inside the k3s cluster, which routes it to application pods on the worker nodes. A dedicated control plane node manages the workers. Clients HTTPS VIP Load balancer 1 Nginx · active Load balancer 2 Nginx · standby keepalived · VRRP k3s cluster Ingress · Traefik Worker 1 app pods Worker 2 app pods Control plane dedicated node
Solid lines carry live traffic. Dashed lines show the standby path that keepalived fails over to.

Problem

Internal applications, such as the production floor dashboard, needed a place to run. That platform had to keep serving if a load balancer went down, handle HTTPS cleanly, and ship new versions from a git push instead of by hand.

Approach

  • Separate roles. A k3s cluster with a dedicated control plane and separate worker nodes, so cluster management and application workloads don't compete for the same machine.
  • No single front door. Nginx load balancers sit in front of the cluster, with keepalived managing a shared virtual IP. Clients only ever see the VIP. If the active balancer fails, the standby takes the address over.
  • One ingress. Traefik is the ingress controller, routing requests to the right service, with TLS termination for HTTPS.
  • Push to deploy. GitHub Actions workflows run on self-hosted runners. A push builds the container image, publishes it to GitHub Container Registry, and rolls it out to the cluster.
  1. git pushapp repo
  2. GitHub Actionsworkflow triggers
  3. Self-hosted runnerbuilds the image
  4. GHCRstores the image
  5. k3srolls it out

Stack

  • k3s (Kubernetes)
  • Nginx
  • keepalived
  • Traefik
  • TLS
  • GitHub Actions
  • Self-hosted runners
  • GitHub Container Registry

Outcome

Internal applications, including the production floor dashboard, run on a platform with no single load balancer as a point of failure. Shipping a change is a git push: the pipeline handles the build, the registry, and the rollout.