← All projects

Project details / September 2026

Kubernetes homelab

Three physical machines, automated Linux configuration, and a GitOps pipeline from source code to the page you’re reading.

The setup

Three Lenovo ThinkCentre mini PCs run Ubuntu Server, each with four logical CPUs, roughly 8 GB of RAM, and a 256 GB SSD. They connect through a Gigabit Ethernet switch.

Ansible configures the hosts, Kubernetes prerequisites, and containerd. I bootstrapped the cluster with kubeadm and use Flannel for pod networking. SSH keys and Tailscale provide remote administration.

The lab now has highly available control-plane services and replicated storage. I’ve tested node failures and backup restores, checking data integrity.

CI/CD and deployment

The portfolio runs in an unprivileged Nginx container. GitHub Actions builds and tests the image, publishes it to GitHub Container Registry, and opens a PR to update the image digest in the homelab repository.

  1. Build and test. CI builds the image, checks that the site and resume are served, and verifies that the container runs as a non-root user.
  2. Publish and promote. The image is pushed to GHCR. A deployment PR pins its digest in the Kubernetes manifest.
  3. Reconcile. After checks pass and the PR merges, Argo CD syncs the application manifests from Git.
  4. Serve. Kubernetes rolls out the site. Cloudflare Tunnel connects the Service to the public HTTPS domain.

The portfolio has two replicas, readiness and liveness probes, resource requests and limits, and a read-only root filesystem. Reverting the deployment digest in Git is the rollback path.

The tunnel uses outbound connections, so public access doesn’t require router port forwarding.

Monitoring

Prometheus, Grafana, and Alertmanager are deployed through Helm. A monitor attached to node01 displays Grafana using a browser kiosk and a systemd-managed port forward.

I use monitoring and alerts to track service health and resource usage, then test failures and restores to check how the system recovers.

Testing

  • Cluster bootstrap, application access, and replacement of deleted pods.
  • A worker shutdown and the original node-local storage failure.
  • Backup restores and data integrity after the availability upgrades.
  • Container builds, image promotion, Argo CD delivery, and public HTTPS access.

Recording recovery times is a next step.

Explore the infrastructure repository ↗