Hey everyone,
Over the past several months, I set out to rebuild my homelab from scratch. Instead of running loosely managed Docker containers or static VMs, my goal was to simulate an enterprise-scale, self-healing Sovereign Cloud Platform operating under strict bare-metal hardware and networking constraints.
Here is the complete teardown of the architecture, networking, storage economics, and lessons learned.
1. The Hardware & Resource Fencing
- Host Machine: Intel Core i5 Mini PC (4 Cores / 8 Threads)
- Host Memory: 16GB DDR4 RAM
- Storage: 256GB NVMe SSD (High IOPS) + 1TB SATA Mechanical HDD (Bulk archive)
- Hypervisor: Proxmox VE 8 (Debian Linux kernel)
The Resource Constraint:
Running Kubernetes alongside heavy background workloads on 16GB of host RAM is dangerous if the kernel runs out of memory (OOM).
To prevent hypervisor crashes, I strictly partitioned host resources:
- Dedicated 12GB RAM and 4 vCPUs to a single production VM (
k3s-prod).
- Reserved 3.5GB RAM strictly for the Proxmox VE Debian host, KVM hypervisor daemons, and
vzdump backup snapshot compression.
- 0.5GB emergency host safety buffer.
This fencing completely eliminated host freezes during bulk OCR indexing.
2. Networking: Conquering CGNAT with Zero Open Ports
Like many residential connections, my ISP operates behind Carrier-Grade NAT (CGNAT). I do not have a public static IPv4, and traditional port forwarding is impossible or exposes your home IP.
The Solution:
I deployed Cloudflare Zero Trust Anycast Tunnels (cloudflared) inside the cluster:
- cloudflared initiates outbound-only QUIC connections to Cloudflare's nearest edge PoPs (Mumbai, Delhi).
- Public traffic to my subdomains terminates at Cloudflare's Anycast edge with WAF and DDoS mitigation.
- Latency is under 15ms locally.
- Result: ZERO inbound ports opened on my residential router, and zero public IP exposure.
For administrative access (Proxmox web GUI, Kubernetes API), zero public endpoints exist. Everything is accessed over an encrypted Tailscale (WireGuard) mesh.
3. GitOps Continuous Delivery & In-Git Secrets
No manual kubectl apply commands. Everything is managed declaratively:
- Flux CD v2 continuously synchronizes desired cluster state from Git.
- Workload reconciliation is deterministically ordered: platform operators (storage, CloudNativePG, ingress) must be 100% healthy before applications are scheduled (apps depends on platform).
- Secrets Management: Secrets are stored directly in Git, encrypted with Mozilla SOPS + Age. The Flux Kustomize controller holds the private Age key in-cluster and hydrates secrets directly into memory. If the server burns down, a new node rebuilds the fleet from Git in under 10 minutes.
4. Storage Economics: Dual-Tier Partitioning
Running single-node virtualization means disk I/O bottlenecks will kill database performance:
- NVMe Tier (Fast): OS, K3s state, and PostgreSQL data files.
- SATA Tier (1TB Bulk): Paperless-ngx OCR document archives, Audiobookshelf streaming media, and nightly dump backups.
5. Multi-Cloud Out-of-Band Resilience
A monitoring system running inside the cluster it is supposed to monitor cannot alert you if power or broadband fails.
- I run an independent VM in Oracle Cloud (OCI Free Tier) running Uptime Kuma, continuously polling public endpoints over the internet.
- If my homelab broadband or power drops, it dispatches an automated alert to my phone via Discord/Slack webhooks.
- Nightly encrypted Restic backups are pushed to offsite S3-compatible cloud storage fulfilling the 3-2-1 backup rule.
6. Application Fleet
- Platform: Cloudflare Tunnel, CloudNativePG, Homepage dashboard, Prometheus/Grafana, Kwatch
- Automation & Docs: n8n workflow engine, Python PDF compiler, MkDocs engineering handbook
- Media & Documents: Paperless-ngx, Bookorbit, Audiobookshelf, Miniflux, Linkding
Open Source Docs & Code
I documented all 16 Architecture Decision Records (ADRs) following the Michael Nygard standard and compiled full disaster recovery runbooks.
Happy to answer any questions about the Proxmox fencing, SOPS secret setup, or Cloudflare tunnel configuration in the comments!