Cloudflare 5 min read

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.

cloudflaretunnelhomelabnetworkingzero-trustnginx-proxy-managersecurity

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
  1. You install cloudflared on your server
  2. cloudflared establishes an outbound connection to Cloudflare’s edge
  3. Cloudflare proxies incoming HTTPS requests through that tunnel
  4. 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 homelab in 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:

  1. Go to Cloudflare Zero TrustAccessApplications
  2. Click Add an applicationSelf-hosted
  3. Configure:
    • Application name: Uptime Kuma
    • Subdomain: uptime.internal
    • Domain: example.com
  4. Under Policies, add a rule:
    • Action: Allow
    • Include: Emails ending in @example.com
    • Selector: Emails
  5. 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

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

BenefitHow
No open portsOnly outbound connections from your server
No static IPCloudflare handles the edge IP
No Dynamic DNSCNAME records to cfargotunnel.com
Built-in HTTPSCloudflare terminates TLS at the edge
Access controlAdd Cloudflare Access for email/OTP auth
FreeZero-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.