Cloudflare Tunnels for Homelab: Zero Ports, Zero VPN, Full Access

Expose your homelab services securely without opening a single port. Cloudflare Tunnels give you public URLs, SSL, and DDoS protection for free.

• 9 min read
homelabcloudflaretunnelself-hostednetworking

If you’ve ever wanted to access your Home Assistant dashboard from work, or let friends stream from your Jellyfin server without opening a single port on your router—this is for you.

I used to do what everyone does: forward ports, set up dynamic DNS, wrestle with Let’s Encrypt certificates, and hope my ISP doesn’t notice. It worked, mostly. But every time I read about a new vulnerability in router firmware or saw my access logs filling with bot traffic, I’d think: there has to be a better way.

Turns out, there is. Cloudflare Tunnels give you public URLs for your homelab services, automatic SSL, enterprise-grade DDoS protection, and access controls—all without exposing your home IP or opening a single firewall port. And the best part? It’s completely free for personal use.

What Are Cloudflare Tunnels?

Cloudflare Tunnels (powered by cloudflared) create an outbound-only connection from your homelab to Cloudflare’s global network. Think of it as a reverse proxy that runs in reverse—instead of waiting for incoming connections, your server initiates an outbound connection to Cloudflare, and Cloudflare routes traffic back through that tunnel.

The key insight here: all connections are outbound. Your firewall sees nothing but your server reaching out to Cloudflare. From the outside world’s perspective, your services are hosted on Cloudflare’s edge. From your homelab’s perspective, you’re just talking to a friendly CDN.

Here’s how the traffic flows:

Your Browser → Cloudflare Edge → Encrypted Tunnel → cloudflared → Your Homelab Service

Cloudflare handles SSL termination, filters malicious traffic, and enforces any access policies you configure. Your only job is to keep cloudflared running.

Why This Beats Traditional Port Forwarding

Let me count the ways:

No open ports. Every port you forward is an attack surface. Eliminate them all, and your attack surface shrinks to… nothing. Your router doesn’t accept incoming connections anymore.

Your home IP stays private. Anyone scanning your public domain sees Cloudflare’s IP, not yours. DDoS attacks hit Cloudflare’s infrastructure, not your residential connection.

Automatic SSL. Cloudflare provisions and renews certificates for your domains. No more certbot cron jobs or expired certificates at awkward moments.

DDoS protection included. Cloudflare’s network absorbs volumetric attacks before they reach your homelab. For free. This is the same protection they sell to enterprises.

Granular access control. Want to require email authentication for your Home Assistant dashboard? Easy. GitHub SSO for your development environment? Done. No VPN required.

Cloudflare Tunnel vs Tailscale: Which Should You Use?

I wrote about Tailscale previously, and it’s a fantastic tool. But they serve different purposes. Here’s my breakdown:

FeatureCloudflare TunnelTailscale
CostFree (up to 50 users)Free (up to 100 devices), then $6/user/mo
Public URLs✅ Custom domains❌ Limited (Funnel feature)
SSL Certificates✅ Automatic⚠️ Self-signed or manual
DDoS Protection✅ Enterprise-grade❌ None
Access Control✅ Built-in policies⚠️ Requires ACL setup
Mobile AppsBrowser only✅ Native VPN apps
LatencyRouted through CloudflareDirect P2P (lower latency)
PrivacyTraffic via CloudflareDirect P2P (more private)

Use Cloudflare Tunnel when:

  • You want public URLs for web services (perfect for sharing)
  • You need SSL without managing certificates
  • You’re exposing dashboards, media servers, or web apps
  • You want access controls without a VPN

Use Tailscale when:

  • You need full network access (not just web apps)
  • Latency matters (e.g., SSH, development work)
  • Privacy is paramount (no third-party sees traffic)
  • You want native mobile VPN apps

The hybrid approach. I run both. Cloudflare Tunnel handles public-facing services like Home Assistant and media servers. Tailscale handles internal network access—SSH, databases, development environments. They’re complementary, not competing.

Setting Up Cloudflare Tunnel with Docker

Let’s get practical. I’ll walk through setting up a tunnel using Docker, which is my preferred deployment method.

Prerequisites

  • A Cloudflare account (free tier is fine)
  • A domain managed by Cloudflare (update your nameservers)
  • Docker and Docker Compose installed

Step 1: Create Your Tunnel

  1. Navigate to the Zero Trust Dashboard
  2. Go to Networks → Tunnels
  3. Click Create a Tunnel
  4. Select Cloudflared as the connector type
  5. Name it something memorable (I use homelab-tunnel)
  6. Click Save Tunnel

Cloudflare will generate a token. Copy it—you’ll need it in a moment.

Step 2: Docker Compose Setup

Create your compose file. I keep this minimal:

version: '3.8'

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN}
    command: tunnel --no-autoupdate run
    networks:
      - homelab

networks:
  homelab:
    external: true

Create a .env file in the same directory:

TUNNEL_TOKEN=your_cloudflare_tunnel_token_here

The homelab network should be the same network your services use. This lets cloudflared reach your containers by name.

Step 3: Full Homelab Stack Example

Here’s a more complete setup with Home Assistant, Jellyfin, and a Git server all exposed through your tunnel:

version: '3.8'

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN}
    command: tunnel --no-autoupdate run
    networks:
      - homelab

  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    restart: unless-stopped
    volumes:
      - ./homeassistant_config:/config
      - /etc/localtime:/etc/localtime:ro
    privileged: true
    networks:
      - homelab

  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    volumes:
      - ./jellyfin_config:/config
      - ./media:/media:ro
    networks:
      - homelab

  forgejo:
    image: codeberg.org/forgejo/forgejo:latest
    container_name: forgejo
    restart: unless-stopped
    volumes:
      - ./forgejo_data:/data
    networks:
      - homelab

networks:
  homelab:
    driver: bridge

Run it:

docker compose up -d

Step 4: Configure Public Hostnames

Back in the Zero Trust Dashboard, under your tunnel settings, add public hostnames:

SubdomainDomainTypeURL
hayourdomain.comHTTPhttp://homeassistant:8123
mediayourdomain.comHTTPhttp://jellyfin:8096
gityourdomain.comHTTPhttp://forgejo:3000

The Docker container names resolve correctly because they’re on the same network. No IP addresses required.

Securing Your Services with Zero Trust Access

Right now, anyone who visits ha.yourdomain.com can access your Home Assistant dashboard. Let’s fix that.

Email OTP Authentication

The simplest approach for personal use: require email authentication.

  1. Go to Access → Applications in the Zero Trust Dashboard
  2. Click Add an Application → Self-hosted
  3. Set the application domain to ha.yourdomain.com
  4. Create a policy:
    • Action: Allow
    • Include: Emails ending with @yourdomain.com
    • Or specific email addresses: [email protected]
  5. Save and test

When someone visits your Home Assistant URL, they’ll see a Cloudflare login page. Enter your email, receive a one-time code, and you’re in. No passwords to remember, no VPN required.

GitHub Authentication (Better for Teams)

If you want more control, use GitHub as an identity provider:

  1. Access → Auth → Login Methods
  2. Add GitHub as a provider
  3. Create an OAuth app in GitHub Developer Settings
  4. Add your client ID and secret
  5. Update your Access policy to use GitHub identity

Now visitors authenticate with their GitHub account, and you can restrict access by organization membership or team.

Home Assistant Configuration

Home Assistant needs to know it’s behind a proxy. Add this to your configuration.yaml:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.16.0.0/12  # Docker bridge network
    - 10.0.0.0/8     # Additional internal ranges
  ip_ban_enabled: true
  login_attempts_threshold: 5

This ensures Home Assistant logs the correct client IPs and trusts the proxy headers from cloudflared.

Homelab Use Cases

Home Assistant

This is my primary use case. Remote access to my smart home from anywhere, with email authentication protecting the dashboard. No more port forwarding, no more dynamic DNS, no more certificate warnings.

The best part: integration with voice assistants works seamlessly. Google Home and Alexa can reach your Home Assistant cloud hook endpoint through the tunnel without any special configuration.

Media Servers

Jellyfin and Plex both work beautifully through Cloudflare Tunnel. Jellyfin is straightforward—just point the tunnel to the web interface port.

For Plex, the tunnel approach replaces the need for Plex Pass remote access. Though you’ll want to consider bandwidth: streaming 4K content through Cloudflare’s free tier works, but heavy use might warrant their paid plan. For personal streaming to <10 users, free tier handles it fine.

Development Environments

I expose several development tools through my tunnel:

  • Forgejo (self-hosted GitHub alternative) for private repositories
  • VS Code Server for remote development from any browser
  • Portainer for Docker container management
  • Grafana for monitoring dashboards

Each gets its own subdomain and access policy. Git repositories require GitHub authentication; monitoring dashboards use email OTP.

IoT Dashboards

If you’re running IoT management interfaces (Node-RED, Zigbee2MQTT, MotionEye for cameras), Cloudflare Tunnel provides a secure way to access them remotely. These interfaces often lack robust authentication, so the Access policies add a critical security layer.

Security Best Practices

Enable Access Policies. Every service exposed through your tunnel should have some form of authentication. No exceptions.

Use HTTP protocol. Configure your tunnels to use http:// internally and let Cloudflare handle HTTPS termination. Your services don’t need SSL configuration; Cloudflare manages certificates.

Limit trusted proxies. Only include the IP ranges your cloudflared container uses. For Docker, this is typically 172.16.0.0/12 or your custom network range.

Monitor your logs. Cloudflare retains 24 hours of logs on the free tier. Set up alerts for unusual access patterns, especially failed authentication attempts.

Consider Cloudflare’s Web Application Firewall. The free tier includes basic bot filtering. If you’re running sensitive services, the paid WAF adds protection against common attacks.

Don’t expose everything. Some services should stay off the tunnel. Database admin interfaces, sensitive development environments, and anything without proper authentication should stay on your internal network—or use Tailscale instead.

Performance Considerations

Latency is higher than direct access. Your traffic goes: Client → Cloudflare Edge → Tunnel → Your Server. For web dashboards and media streaming, the delay is imperceptible. For SSH sessions or database queries, you might feel the extra hop.

If latency matters, use Tailscale for those services. I run both: Cloudflare for web services I want to share, Tailscale for development work where latency is critical.

Conclusion

Cloudflare Tunnels have fundamentally changed how I approach homelab networking. No more port forwarding. No more router configuration. No more certificate management. No more worrying about my home IP.

For exposing web services publicly, it’s the best solution I’ve found—especially considering the free tier includes DDoS protection and access controls. The setup is simple, maintenance is minimal, and security is significantly better than traditional approaches.

If you want private network access with lower latency, pair it with Tailscale. But for public-facing homelab services? Cloudflare Tunnel is hard to beat.

Quick Reference

Tunnel setup:

# Create tunnel in Zero Trust Dashboard, copy token
mkdir cloudflare-tunnel && cd cloudflare-tunnel
echo "TUNNEL_TOKEN=your_token_here" > .env
# Add docker-compose.yml
docker compose up -d

Access policy for single user:

  1. Access → Applications → Self-hosted
  2. Domain: service.yourdomain.com
  3. Policy: Emails contains [email protected]

Home Assistant proxy config:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.16.0.0/12

Your homelab services, publicly accessible, securely behind Cloudflare’s network. Zero ports, zero VPN, full access.

Anthony Lattanzio

Anthony Lattanzio

Tech Enthusiast & Builder

I'm a tech enthusiast who loves building things with hardware and software. By night, I run a homelab that's grown way beyond what any reasonable person needs. Check out about me for more.

Comments

Powered by GitHub Discussions