Cloudflare Tunnel for the Homelab: Secure Access Without Opening Ports
A practical guide to setting up Cloudflare Tunnel (cloudflared) to expose self-hosted services through Cloudflare's edge — no open firewall ports, no static IP, no Dynamic DNS.
Opening ports on your home router to expose services is risky. It exposes your local network to every bot scanning IP ranges, and it means keeping Dynamic DNS working when your ISP changes your IP address.
Cloudflare Tunnel (powered by cloudflared) solves both problems. Your services stay behind your firewall, and Cloudflare securely proxies traffic to them through an outbound-only connection — no open inbound ports required.
How It Works
Browser → Cloudflare Edge → Tunnel (outbound) → Your Server → Service
- You install
cloudflaredon your server cloudflaredestablishes an outbound connection to Cloudflare’s edge- Cloudflare proxies incoming HTTPS requests through that tunnel
- Your firewall only needs to allow outbound traffic — no port forwarding, no Dynamic DNS
The connection is encrypted (QUIC or HTTP/2 under the hood), authenticated with a certificate, and monitored by Cloudflare.
Step 1: Install cloudflared
# Debian/Ubuntu
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb \
-o cloudflared.deb
sudo dpkg -i cloudflared.deb
# Verify
cloudflared --version
Step 2: Authenticate
cloudflared tunnel login
This opens a browser to authorize the tunnel in your Cloudflare account. Once authorized, a certificate is downloaded to ~/.cloudflared/cert.pem.
Step 3: Create the Tunnel
cloudflared tunnel create homelab
This command:
- Registers the tunnel named
homelabin your Cloudflare account - Creates a tunnel UUID
- Saves the credentials to
~/.cloudflared/<UUID>.json
Step 4: Configure the Tunnel
Create the configuration file:
mkdir -p ~/.cloudflared
nano ~/.cloudflared/config.yml
# ~/.cloudflared/config.yml
tunnel: <YOUR_TUNNEL_UUID>
credentials-file: /home/your-user/.cloudflared/<YOUR_TUNNEL_UUID>.json
ingress:
# Dashboard (port 3000 on different hosts)
- hostname: uptime.internal.example.com
service: http://192.168.1.50:3001
# Proxmox (HTTPS with self-signed cert)
- hostname: pve.internal.example.com
service: https://192.168.1.10:8006
originRequest:
noTLSVerify: true
# Catch-all (deny everything else)
- service: http_status:404
Important: Every ingress rule needs a public hostname configured in your Cloudflare DNS zone. Cloudflare only routes traffic for domains you own.
You can find your tunnel UUID with:
cloudflared tunnel list
Step 5: Set Up DNS Records
For each hostname in your ingress config, add a CNAME record in Cloudflare DNS:
uptime.internal → <TUNNEL_UUID>.cfargotunnel.com
pve.internal → <TUNNEL_UUID>.cfargotunnel.com
Or create them via CLI:
cloudflared tunnel route dns homelab uptime.internal.example.com
cloudflared tunnel route dns homelab pve.internal.example.com
Step 6: Run as a Systemd Service
For production use, run cloudflared as a service:
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared
Verify the tunnel is healthy:
cloudflared tunnel info homelab
Adding Cloudflare Access (Zero Trust)
A public DNS name means anyone who discovers the URL can reach your service. Add Cloudflare Access for authentication:
- Go to Cloudflare Zero Trust → Access → Applications
- Click Add an application → Self-hosted
- Configure:
- Application name: Uptime Kuma
- Subdomain:
uptime.internal - Domain:
example.com
- Under Policies, add a rule:
- Action: Allow
- Include: Emails ending in
@example.com - Selector: Emails
- Save
Now visitors must authenticate with a Cloudflare-screened email before they see the login page of your service. You can also use OTP codes for one-time access to specific URLs.
Docker Integration
If you use Docker, cloudflared integrates nicely in a Compose stack:
# docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
networks:
- proxy
networks:
proxy:
external: true
Use a Tunnel Token instead of the credentials JSON — it’s a single environment variable:
# Get the token from Cloudflare Zero Trust dashboard or via CLI
cloudflared tunnel token homelab
Reverse Proxy + Tunnel (Recommended)
For multiple services, combine Cloudflare Tunnel with a reverse proxy (Traefik or Nginx Proxy Manager):
Browser → Cloudflare Edge → Tunnel → Nginx Proxy Manager → Service
The tunnel points to your reverse proxy on port 80/443, and the proxy routes by hostname internally. This avoids having one tunnel ingress rule per service.
ingress:
- hostname: "*.internal.example.com"
service: https://nginx-proxy:443
originRequest:
originServerName: "*.internal.example.com"
- service: http_status:404
Performance Considerations
- Tunnels use Cloudflare’s global network — traffic routes through the nearest Cloudflare PoP
- For internal transfers (e.g., local Jellyfin streaming), consider direct access or split DNS
- Web traffic is fine; very high bandwidth (4K streaming, large file transfers) may show latency
- QUIC protocol gives better performance than HTTP/2 on lossy connections
Monitoring
Cloudflare provides tunnel health metrics in the Zero Trust dashboard. You can also monitor the tunnel locally:
# Health check endpoint (requires setting management.enable flag)
curl http://localhost:20000/metrics
# Journal logs
sudo journalctl -u cloudflared -f
Summary
| Benefit | How |
|---|---|
| No open ports | Only outbound connections from your server |
| No static IP | Cloudflare handles the edge IP |
| No Dynamic DNS | CNAME records to cfargotunnel.com |
| Built-in HTTPS | Cloudflare terminates TLS at the edge |
| Access control | Add Cloudflare Access for email/OTP auth |
| Free | Zero-trust tunnels are included in Cloudflare Free plan |
Cloudflare Tunnel is one of the most impactful tools in a homelab toolbox. It eliminates the need for port forwarding, reduces your attack surface to near zero, and integrates cleanly with the rest of the Cloudflare ecosystem.
Have questions about setting up tunnels for your specific setup? Get in touch.