If you self-host services, you face a dilemma: you want them accessible from the internet, but opening ports invites attacks. Port scanners find open SSH (22), HTTP (80), and custom ports within minutes.

Cloudflare Tunnel solves this elegantly: no open ports at all.

How Cloudflare Tunnel Works

Instead of opening a firewall port, your server establishes an outbound connection to Cloudflare's edge. Traffic comes into Cloudflare, goes through their network, and is forwarded to your server via this tunnel.


User → Cloudflare Edge → Encrypted Tunnel → Your Server (port closed)

Your ISP sees only an outbound connection to Cloudflare. No incoming ports. No port scans. No DDoS on your IP.

Step 1: Install cloudflared


# Linux (amd64)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared

# Or via package manager
apt install cloudflared  # Debian/Ubuntu

Step 2: Authenticate


cloudflared tunnel login

This opens a browser to authenticate with your Cloudflare account. In headless environments, use an API token instead:


cloudflared tunnel login --token YOUR_TOKEN

Step 3: Create a Tunnel


cloudflared tunnel create my-tunnel

This creates a tunnel with a unique ID and downloads a credentials file.

Step 4: Configure DNS


cloudflared tunnel route dns my-tunnel service.yourdomain.com

This creates a CNAME record in your Cloudflare DNS pointing to the tunnel.

Step 5: Configure and Run

Create a config file:


# ~/.cloudflared/config.yaml
tunnel: your-tunnel-uuid
credentials-file: /path/to/credentials.json

ingress:
  - hostname: service.yourdomain.com
    service: http://localhost:8080
  - hostname: docs.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404

Run:


cloudflared tunnel run my-tunnel

For production, install as a system service:


cloudflared service install

Use Cases I Run

Here's what I expose through Cloudflare Tunnel:

| Service | Local Port | Public URL |

|---------|-----------|------------|

| Vikunja (task manager) | 3456 | todo.yourdomain.com |

| Vaultwarden (password manager) | 8087 | vault.yourdomain.com |

| EasyImage (image hosting) | 8080 | images.yourdomain.com |

| Home Assistant | 8123 | home.yourdomain.com |

All of these have zero open ports on my firewall. The tunnel handles everything.

Security Features

Cloudflare Tunnel provides several security benefits out of the box:

  • **No open ports** — Port scanners find nothing
  • **Free TLS/SSL** — Automatic certificate management
  • **DDoS protection** — Cloudflare's network absorbs attacks
  • **Access policies** — Require Google login or one-time PIN for sensitive services
  • **Audit logging** — See who accessed what

Troubleshooting Common Issues

ISP Throttling UDP (QUIC)

Cloudflare Tunnel defaults to QUIC (UDP). Some ISPs throttle UDP. Force HTTP2 (TCP):


ingress:
  - hostname: service.yourdomain.com
    service: http://localhost:8080

tunnel: your-tunnel-uuid
credentials-file: /path/to/credentials.json

protocol: http2  # Force TCP instead of QUIC

Connection Drops

Add auto-restart to your tunnel service:


# In systemd service file
Restart=always
RestartSec=5

Multiple Tunnels

Each tunnel can serve multiple services. Just add more ingress rules. Use one tunnel per server.

Cost

Cloudflare Tunnel is free for unlimited tunnels and traffic. The only cost is your domain (Cloudflare DNS must be active on your domain, which is also free).

Verdict

Cloudflare Tunnel is the safest way to expose self-hosted services. It's free, secure, and takes 10 minutes to set up. For anyone running services at home, it's non-negotiable.