Building Cloudflare Tunnel access to our homelab staging so the team stops saying "works on my machine"
Our k3s staging cluster sat behind my home network. Cloudflare Tunnels gave the whole team stable HTTPS access without exposing my home IP or managing VPN credentials.
Our k3s staging cluster runs on a mini PC in my home office. For six months, the team accessed it through a WireGuard VPN that I managed manually. The VPN dropped connections every 20 minutes on certain ISPs, required configuration files that had to be regenerated whenever someone got a new laptop, and did not work at all from coffee shops with aggressive NAT. The result was that nobody used staging unless absolutely necessary, which defeated the entire purpose of having a staging environment.
This is where the homelab stopped being a hobby and started acting like a leadership tool. It also builds on what I learned earlier in “TypeScript strict mode migration: the six-month project I wish I had done in month one.” The infrastructure and ctrlpane work gave me a cheap place to pressure-test release habits, GitOps discipline, and failure modes before I asked the team to trust those defaults at work.
Why Cloudflare Tunnels
Cloudflare Tunnels solve the problem of exposing services running behind NAT without opening ports or managing VPN infrastructure. A lightweight daemon called cloudflared runs on your network, establishes an outbound connection to Cloudflare edge, and routes traffic to your internal services. No inbound ports. No static IP required. No VPN client installation.
The key advantage for a homelab staging environment is that the team accesses staging the same way they access production: through HTTPS on a real domain. There is no VPN to connect, no special client to install, and no configuration that varies by ISP or network. If you can access the internet, you can access staging.
The security model is also cleaner than VPN. With a VPN, anyone who has the configuration file has network-level access to everything on my home network. With Cloudflare Tunnels, access is scoped to specific hostnames routing to specific services. The tunnel only exposes what I explicitly configure. My home network stays private.
The Setup
The entire setup took about two hours including DNS configuration, tunnel creation, and access policy setup.
# Install cloudflared on the k3s nodecurl -L --output cloudflared.deb \ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.debsudo dpkg -i cloudflared.deb
# Authenticate with Cloudflarecloudflared tunnel login
# Create the tunnelcloudflared tunnel create staging-homelab
# Configure the tunnel routes# config.yml:tunnel: <tunnel-id>credentials-file: /root/.cloudflared/<tunnel-id>.jsoningress: - hostname: staging.financeops.internal service: http://localhost:80 - hostname: staging-api.financeops.internal service: http://localhost:3000 - hostname: argocd.financeops.internal service: https://localhost:8443 originRequest: noTLSVerify: true - service: http_status:404Each service running on the k3s cluster gets its own hostname. The nginx ingress controller on the cluster routes traffic based on the hostname, so the tunnel only needs to forward to the ingress on port 80. ArgoCD runs its own TLS, so it gets a direct route with TLS verification disabled for the internal hop.
- staging.financeops.internal routes to the main application
- staging-api.financeops.internal routes to the API service
- argocd.financeops.internal routes to the ArgoCD dashboard
- All three are accessible via HTTPS with Cloudflare-managed certificates
Access Control
Exposing staging services on the internet requires access control. We use Cloudflare Access to require authentication before any request reaches the tunnel. The policy requires either a company email domain or a one-time PIN sent to an approved email address.
Access Policy: staging-homelab Action: Allow Include: - Email domain: financeops.com Require: - Email OTP verification Session duration: 24 hoursWhen an engineer navigates to staging.financeops.internal, Cloudflare Access intercepts the request and presents a login page. The engineer enters their email, receives a one-time code, and after verification gets a 24-hour session cookie. No VPN client. No configuration file. No port forwarding. Just a browser and a company email address.
The session duration of 24 hours was a deliberate choice. Engineers check staging once or twice during a work day. A 24-hour session means they authenticate once in the morning and do not need to re-authenticate for the rest of the day. The VPN had a 20-minute timeout that required re-authentication, which was the single most complained-about aspect of the old setup.
Running as a Kubernetes Deployment
For reliability, cloudflared runs as a Kubernetes deployment on the k3s cluster rather than as a system service. This means it benefits from Kubernetes health checks, automatic restarts, and rolling updates.
apiVersion: apps/v1kind: Deploymentmetadata: name: cloudflared namespace: cloudflarespec: replicas: 1 selector: matchLabels: app: cloudflared template: spec: containers: - name: cloudflared image: cloudflare/cloudflared:latest args: - tunnel - --config - /etc/cloudflared/config.yml - run volumeMounts: - name: config mountPath: /etc/cloudflared readOnly: trueThe Impact
Within a week of deploying the tunnel, staging usage went from sporadic to daily. Engineers started deploying feature branches to staging before opening PRs. QA testing moved from local machines to staging. Cross-browser testing happened on the actual deployment instead of on localhost with different configurations.
By this stage the job had changed. I was no longer just picking a tool or fixing a bug. I was carrying the blast radius across product, compliance, sales, and hiring. That is exactly why I kept pressure-testing the same lesson inside infrastructure and ctrlpane.
The best infrastructure is infrastructure nobody has to think about. The VPN required thinking about credentials, connections, and troubleshooting. The Cloudflare Tunnel requires typing a URL. When accessing staging is as easy as accessing any website, engineers will actually use staging. That is the point.
Total cost: zero. Cloudflare Tunnels are free. Cloudflare Access is free for up to 50 users. The two-hour setup replaced a VPN infrastructure that consumed several hours per month in maintenance and support. If you are running a homelab staging environment and your team is not using it because access is painful, Cloudflare Tunnels is the fix.