Public presentation
The portfolio is hosted independently through Cloudflare Pages. The website remains available even when the home lab is offline.
FLAGSHIP HOME LAB CASE STUDY
I built this environment to practice the problems that make infrastructure engineering real: network boundaries, controlled administration, Kubernetes application delivery, persistent storage, DNS, monitoring, and failure recovery. The most important result was not installing another platform. It was finding an alternate path around my intended firewall boundary, correcting the architecture, and turning the lab into a system I can explain, validate, and continue improving.
OBJECTIVE
The lab began as a Proxmox environment for virtual machines and remote administration. It grew into a segmented platform containing an OPNsense firewall, an identity-aware access path, a three-node Kubernetes cluster, GitOps-managed services, centralized DNS, monitoring, and isolated security ranges.
My design goal is not to make a home lab look like a large enterprise. It is to reproduce the same engineering questions at a smaller scale: where trust boundaries exist, how changes are controlled, what happens when a dependency fails, and whether another operator could understand the system.
CURRENT ARCHITECTURE
flowchart TB USER[Remote Operator] SITE[Public Portfolio
Cloudflare Pages] ACCESS[Cloudflare Access] TUNNEL[Outbound Cloudflare Tunnel] BASTION[Bastion
Guacamole] FIREWALL[OPNsense
Policy Enforcement] WIN[Windows Jump Workstation] K8S[Three-Node Kubernetes Cluster] DNS[Pi-hole DNS] RANGE[Isolated Security Range] STATUS[Public Read-Only Status Page] USER --> SITE USER --> ACCESS ACCESS --> TUNNEL TUNNEL --> BASTION BASTION --> FIREWALL FIREWALL --> WIN FIREWALL --> K8S FIREWALL --> DNS FIREWALL --> RANGE K8S --> STATUS
The portfolio is hosted independently through Cloudflare Pages. The website remains available even when the home lab is offline.
Cloudflare Access authenticates the operator before traffic reaches the private application path. The tunnel connector initiates its connection outbound, so administrative services do not require direct WAN forwarding.
OPNsense sits between the upstream network and the protected lab network. Access from the bastion to protected systems requires an explicit firewall rule.
Kubernetes, management workstations, DNS, and internal applications sit behind the firewall boundary. Intentionally vulnerable workloads use a separate security range.
THE SEGMENTATION PROBLEM
The bastion originally had one interface on the upstream network and another on the protected lab network. That made remote administration convenient, but it also allowed the bastion to reach protected workloads directly. Traffic did not have to traverse the OPNsense policy boundary.
flowchart LR
CF[Cloudflare Tunnel] --> B[Dual-Homed Bastion]
B --> UP[Upstream Network]
B -. Direct Path .-> LAB[Protected Lab Network]
FW[OPNsense Firewall] --> LAB
This was not a direct internet exposure or a data leak. It was an internal segmentation bypass: the architecture contained an alternate routing path that defeated the intended enforcement point.
01. Removed the protected-side interface
The bastion became single-homed on the upstream network.
02. Routed lab traffic through OPNsense
A persistent route sends protected-network traffic to the firewall instead
of directly to the destination network.
03. Added explicit policy
Firewall rules permit only the required bastion-to-service paths.
04. Revalidated the applications
Guacamole, Windows RDP, and Uptime Kuma were tested through the enforced
path, then the route and tunnel were reboot-tested.
flowchart LR
CF[Cloudflare Tunnel] --> B[Single-Homed Bastion]
B --> FW[OPNsense Firewall]
FW --> WIN[Windows RDP]
FW --> KUMA[Uptime Kuma]
FW --> LAB[Protected Services]
KUBERNETES PLATFORM
The Kubernetes environment uses one control-plane node and two workers. Cilium provides cluster networking. Argo CD reconciles the desired state from Git, and Helm is the default packaging method for platform services.
Cilium was selected because it aligns with the platform networking model I am building experience with professionally. The deployment was validated with the Cilium connectivity test suite.
Argo CD uses an app-of-apps structure to manage namespaces, infrastructure components, and applications. Git holds the intended state and Argo CD detects and reconciles drift.
Helm charts provide repeatable deployments and configurable defaults. Argo CD renders and applies the charts, rather than relying on one-time manual installation commands.
Rancher Local Path Provisioner supplies dynamic node-local volumes. It is intentionally simple and functional, but it is not treated as replicated or highly available storage.
GITOPS IMPLEMENTATION
flowchart LR
CHANGE[Configuration Change] --> GIT[Git Commit]
GIT --> ROOT[Argo CD Root Application]
ROOT --> APP[Child Application]
APP --> HELM[Helm Chart or Manifests]
HELM --> K8S[Kubernetes Resources]
K8S --> HEALTH[Health and Sync Validation]
The Local Path Provisioner is a clear example of this model. I deployed the
Rancher local-path-provisioner Helm chart, pinned to version
0.0.36, through Argo CD. The chart generated its ServiceAccount,
ClusterRole, ClusterRoleBinding, Deployment, ConfigMap, and StorageClass.
I configured local-path as the cluster default.
Uptime Kuma then requested a 2 Gi persistent volume. The claim binding successfully proved the GitOps, RBAC, storage provisioning, and application persistence path together.
DEPLOYED SERVICES
Uptime Kuma runs in Kubernetes with persistent storage. A public, read-only status page is separated from the administrative interface, which remains behind Cloudflare Access.
Pi-hole provides household DNS filtering and forwards allowed queries to Quad9. Deploying it exposed a real availability concern: centralized DNS becomes a dependency for the entire household.
Guacamole provides browser-based access to internal workstations. It runs with guacd and PostgreSQL on the bastion and is reached through the outbound tunnel path.
The Windows workstation is single-homed inside the protected network. Remote access reaches it through Cloudflare, Guacamole, and an explicit OPNsense rule instead of a direct RDP exposure.
SECURITY CONTROLS
Single-homed bastion
Removes the direct path into the protected network and preserves the
firewall as the enforcement point.
Outbound-only tunnel
The connector establishes the Cloudflare Tunnel from inside the network.
No direct inbound management port is required.
Layered authentication
Cloudflare Access validates identity before the operator reaches Guacamole
or the destination operating system.
Firewall allow rules
Protected services are reached through explicit source, destination, and
port policy instead of broad network access.
Git-reviewed state
Cluster configuration is stored in Git and reconciled by Argo CD, making
intended changes visible and repeatable.
Chart-provided RBAC
Infrastructure charts provide their required permissions. Namespace-scoped
bindings remain the preferred model for ordinary applications.
FAILURE AND RECOVERY LESSONS
After the Proxmox host restarted, the environment did not return as one indivisible system. Networking, the Kubernetes API, kubelet state, the tunnel, application containers, and DNS each had their own startup and validation requirements.
The outage made several next steps concrete: establish VM startup order, verify that containerd and kubelet start automatically, keep swap disabled across reboots, validate the API server before troubleshooting applications, and document a repeatable health check.
Pi-hole also demonstrated that availability is not the same as connectivity. The internet connection can remain operational while failed DNS makes the household appear offline. A second resolver on separate hardware is now part of the resilience plan.
CURRENT LIMITATIONS
Pi-hole is currently a single point of failure. A secondary Pi-hole on a different physical system is the preferred correction.
Local Path Provisioner does not replicate application data. A failed node can make its local volume unavailable.
Git can reconstruct configuration, but application data, etcd state, firewall configuration, and DNS configuration require separate backups.
The GitOps repository still needs a formal encrypted-secrets workflow. SOPS with age is the leading option for the next phase.
NEXT PHASE
01. Recovery validation
Finalize Proxmox startup order and document the post-outage validation
sequence.
02. Backups
Automate etcd snapshots and back up application data, Pi-hole, and OPNsense
outside the primary host.
03. DNS resilience
Add a second resolver on separate hardware so one VM or host failure does
not remove household name resolution.
04. Secrets management
Add SOPS and age so sensitive Kubernetes configuration can remain encrypted
in Git.
05. Gateway and TLS
Implement Envoy Gateway and cert-manager after the recovery baseline is
complete.
06. Observability
Add Prometheus, Grafana, and Alertmanager for cluster metrics and actionable
alerts.
PROJECT SUMMARY
I built and operated a segmented Proxmox and Kubernetes environment using OPNsense, Cloudflare Access and Tunnel, Guacamole, Cilium, Argo CD, Helm, dynamic storage, monitoring, and centralized DNS. During implementation, I identified a dual-homed bastion that bypassed the intended firewall path, redesigned the access flow, and validated the corrected route through application and reboot testing.
The lab now provides a practical foundation for infrastructure engineering, platform operations, security architecture, GitOps, troubleshooting, and recovery work. It is intentionally unfinished, but its remaining limitations are understood and sequenced into the next engineering phases.