Cloudflare Tunnels for Homelab Access
Accessing your home lab from the outside used to mean open ports, dynamic DNS, and hoping your firewall rules held.
Cloudflare Tunnels change the game. No inbound ports. No exposed services. Just secure outbound connections.
How It Works
Instead of opening port 443 on your router, cloudflared runs on your server and establishes an outbound connection to Cloudflare’s edge network.
┌──────────┐ ┌─────────────────┐ ┌─────────────┐ ┌──────────────┐
│ Internet │────▶│ Cloudflare Edge │◀────│ cloudflared │────▶│ Your Service │
└──────────┘ └─────────────────┘ └─────────────┘ └──────────────┘
│
(outbound connection)
Traffic flows:
- User requests
app.yourdomain.com - Cloudflare receives the request at their edge
- Cloudflare routes it through the tunnel to your server
- Your server responds back through the same tunnel
Your firewall stays locked. No inbound rules needed.
Zero Trust Authentication
The tunnel alone isn’t the killer feature. Zero Trust Access is.
Every request gets authenticated before reaching your service:
- Identity check - Valid Google/GitHub/Okta login required
- Email domain - Only
@yourdomain.comallowed - Device posture (optional) - Require managed devices
- Geographic restrictions (optional) - Block specific countries
# Access policy example
- name: "Admin Access"
decision: allow
include:
- email_domain: yourdomain.com
require:
- login_method: google
Even if someone finds your tunnel URL, they can’t access it without authenticating through your identity provider.
Setting Up the Tunnel
Install cloudflared
# Debian/Ubuntu
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
dpkg -i cloudflared.deb
# Or via Docker
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate
Authenticate
cloudflared tunnel login
Opens a browser to authenticate with your Cloudflare account.
Create the Tunnel
cloudflared tunnel create homelab
This generates a credentials file. Keep it safe.
Configure Routes
Create ~/.cloudflared/config.yml:
tunnel: homelab
credentials-file: /root/.cloudflared/<tunnel-id>.json
ingress:
- hostname: code.yourdomain.com
service: http://localhost:8080
- hostname: grafana.yourdomain.com
service: http://localhost:3000
- hostname: requests.yourdomain.com
service: http://localhost:5055
- service: http_status:404
Each hostname routes to a different internal service.
Run as a Service
cloudflared service install
systemctl enable cloudflared
systemctl start cloudflared
The tunnel starts on boot and reconnects automatically.
Services I Expose
VS Code Server
Full IDE in the browser. Edit code from anywhere.
- hostname: code.yourdomain.com
service: http://localhost:8443
Protected by Zero Trust — requires authentication before you see the login page.
Monitoring Dashboard
Grafana dashboards showing server health, build status, network metrics.
- hostname: monitor.yourdomain.com
service: http://localhost:3000
Media Requests (Overseerr)
This one allows public access but with Cloudflare WAF rate limiting:
- hostname: requests.yourdomain.com
service: http://localhost:5055
No Zero Trust here — it’s meant for family/friends to request media.
Split-Horizon DNS
When I’m home, I don’t want traffic going through Cloudflare. It should stay local.
External (via Cloudflare):
code.yourdomain.com→ Cloudflare Tunnel → localhost:8443
Internal (local DNS):
code.yourdomain.com→ 192.168.1.100:8443
My local DNS (Pi-hole/AdGuard) returns local IPs for these domains. When I’m away, public DNS returns the Cloudflare proxy.
Security Benefits
| Port Forwarding | Cloudflare Tunnel |
|---|---|
| Expose ports to internet | No inbound ports |
| DDoS hits your connection | DDoS stopped at Cloudflare edge |
| Manual firewall rules | Zero Trust policies |
| SSL certificate management | Automatic SSL via Cloudflare |
| Your IP exposed | Your IP hidden behind Cloudflare |
Limitations
- Latency - Traffic goes through Cloudflare. Adds a few ms.
- Bandwidth - Free tier has limits on certain features
- Dependency - If Cloudflare is down, so is your access
- Not for all traffic - Gaming, streaming, high-bandwidth might suffer
For admin tools, dashboards, and remote access, the tradeoff is worth it.
Debugging
# Check tunnel status
cloudflared tunnel info homelab
# Run in foreground for debugging
cloudflared tunnel run homelab
# Test locally
curl -I https://code.yourdomain.com
Zero inbound ports. Identity-aware access. Remote admin from anywhere. This is how homelabs should connect to the internet.