One outbound connection replaces port forwarding, dynamic DNS, and firewall holes for every service in your homelab. Here is how the tunnel actually works, the tradeoffs nobody mentions, and a setup that survives reboots.

Every port you forward on your router is a promise: a promise to keep that service patched, to watch its logs, to notice when something probes it at 3 a.m. Most homelabs accumulate these promises like browser tabs — a media server here, a wiki there, an SSH "just for emergencies" — until the firewall looks like Swiss cheese and nobody remembers why port 8123 is open.

Cloudflare Tunnel (the cloudflared daemon) deletes the whole category. Instead of punching inbound holes, your server opens outbound connections to Cloudflare's edge, and Cloudflare routes public traffic for your domain back through them. No port forwarding. No dynamic DNS client. Your router's firewall can stay default-deny-inbound, which is how it shipped.

How it actually works

You run cloudflared on the machine hosting your services. It dials out to Cloudflare over HTTPS — the kind of outbound traffic every network already allows — and holds the connection open. In Cloudflare's dashboard you attach hostnames to the tunnel: photos.example.com goes to http://localhost:2283, wiki.example.com goes to http://localhost:3000. A visitor hits Cloudflare's edge; Cloudflare forwards the request down your outbound tunnel; the response comes back the same way.

The mental model that matters: your services are never directly reachable from the internet. There is no public IP-and-port pair for a scanner to find. Shodan cannot index what is not listening.

The 10-minute setup

Install cloudflared, then authenticate once — this opens a browser flow that ties the daemon to your Cloudflare account:

cloudflared tunnel login
cloudflared tunnel create homelab

That creates a tunnel ID and a credentials file under ~/.cloudflared/. Route a hostname to it:

cloudflared tunnel route dns homelab photos.example.com

Then write the config — this is the file that actually matters:

tunnel: <your-tunnel-id>
credentials-file: /home/you/.cloudflared/<your-tunnel-id>.json
ingress:
  - hostname: photos.example.com
    service: http://localhost:2283
  - hostname: wiki.example.com
    service: http://localhost:3000
  - service: http_status:404

That last rule is the catch-all: anything without a matching hostname gets a 404 instead of falling through to something unintended. Run it as a service so it survives reboots (cloudflared service install wires up systemd), and you are done. New service? Add two lines and restart. No router login, no firewall rule, no dynamic DNS.

The tradeoffs, honestly

Cloudflare terminates your TLS. Traffic is encrypted to their edge and re-encrypted to your origin, which means Cloudflare can see it. For a media server or a wiki, that is the deal you already accepted by using their DNS. For anything genuinely sensitive, put Cloudflare Access (zero-trust login) in front of it — or do not expose it at all. A tunnel is not a VPN; it is per-service publishing.

It is not for everything. Latency goes up by a hop. Multi-gigabyte transfers and latency-sensitive game servers do not belong here — and Cloudflare's terms agree. Keep the tunnel for web UIs, APIs, and admin panels.

Quick tunnels are disposable. cloudflared tunnel --url http://localhost:8080 gives you a public URL in seconds — perfect for showing a client a work-in-progress. But the URL rotates on every restart, so it is a demo tool, not infrastructure. Named tunnels (the setup above) are the permanent answer.

The operator's checklist

  • One tunnel per machine, many hostnames per tunnel — do not multiply daemons.
  • Always end ingress with the http_status:404 catch-all.
  • Run it as a system service, not in a tmux session you will forget.
  • Layer Cloudflare Access on anything admin-facing; an obscure hostname is not authentication.
  • Keep SSH off the tunnel entirely. For remote shell, use a real VPN (WireGuard, Tailscale) — different tool, different job.

You got into self-hosting to own your services, not to become a firewall administrator. The tunnel gives you the first without sentencing you to the second.


Sources: Cloudflare Tunnel documentation · cloudflared origin configuration reference