Self-Hosting Without Opening Ports — Cloudflare Tunnels

I run a home server on an old HP laptop. It's behind CGNAT (common in Malaysia), so port forwarding isn't an option. Cloudflare Tunnel solves this — it creates an encrypted outbound connection from the server to Cloudflare's edge, and Cloudflare routes traffic to it.

How it works

User → cloudflared (at Cloudflare edge) → tunnel → cloudflared (on server) → localhost:port

The server never opens a port. It connects out to Cloudflare's network, and Cloudflare handles the rest.

My setup

# Install cloudflared
wrangler tunnel create my-tunnel
wrangler tunnel route dns my-tunnel games.kamalrajnaidu.com

# Run it
cloudflared tunnel run my-tunnel

The tunnel config maps hostnames to local services:

ingress:
  - hostname: games.kamalrajnaidu.com
    service: http://localhost:8050
  - hostname: ntfy.kamalrajnaidu.com
    service: http://localhost:7200
  - service: http_status:404

Why not just use a VPS?

A VPS costs $5–10/month and you still have to secure it. A tunnel is free (Cloudflare's free tier includes 50 tunnels per account), and the attack surface is tiny — there's nothing to SSH into, no open ports to scan.

DNS management

All my subdomains point to Cloudflare's edge. Some go through the tunnel (services that need a backend), others go to Cloudflare Pages (static sites like this blog). The DNS management is all in one place:

Subdomain Type Destination
games.kamalrajnaidu.com Tunnel localhost:8050
blog.kamalrajnaidu.com Pages Static site
music.kamalrajnaidu.com Pages Static site
status.kamalrajnaidu.com Pages Static site

Trade-offs

  • Latency: Traffic routes through Cloudflare's edge. Adds ~10-20ms vs direct connection.
  • Bandwidth: Free tier is unlimited but subject to Cloudflare's ToS (no video streaming).
  • SSL: Automatic via Cloudflare's edge certs. No Let's Encrypt setup needed.

For a hobbyist setup, the trade-offs are negligible. The convenience of zero open ports is worth it.