A Cloudflare Tunnel lets you expose a self-hosted app to the internet without opening a single firewall port, without a static IP, and without paying for anything. Instead of forwarding ports on your router, you run a lightweight agent called cloudflared on your server; it opens an outbound connection to Cloudflare's edge, and public traffic rides that connection back to your app. This guide walks the full Cloudflare Tunnel setup two ways (dashboard and CLI), then covers when a tunnel is the wrong tool.
What is a Cloudflare Tunnel, and why use one?
A Cloudflare Tunnel is a persistent, outbound-only link between your origin server and Cloudflare's network. Because the connection is initiated from your side, there are no inbound ports to open and nothing for a port scanner to find. Traffic to app.yourdomain.com hits Cloudflare first, then travels down the existing tunnel to cloudflared, which hands it to your local service on, say, localhost:8080. You get Cloudflare's TLS certificate, DDoS protection, and WAF in front of the app for free, and your server's real IP stays hidden. Per Cloudflare's Tunnel documentation, tunnels work on every plan, including the free one, with no cap on how many you create. That combination — zero exposed ports plus a free managed edge — is why home-labbers and small teams reach for it over classic port forwarding.
Before you start: what you need
The setup is short, but three prerequisites are non-negotiable:
- A domain on Cloudflare. Your domain's nameservers must point to Cloudflare so it can create the DNS records that route to your tunnel. The domain itself can be cheap; the Cloudflare account is free.
- A running service to expose. Anything that answers on a local port — a Node app on
:3000, Nextcloud on:80, a dashboard on:8080. If you don't have a box yet, start with our secure-first-hour VPS guide. cloudflaredinstalled on that server. It's a single Go binary available for Linux, macOS, and Windows, packaged forapt,brew, and Docker via the official cloudflared repo.
That's it. No inbound firewall rule, no router configuration, no public IPv4 lease.
Method 1: The dashboard (remote-managed) tunnel
For most people this is the fastest path, and in 2026 Cloudflare steers new users here because the config lives in the cloud rather than a file you have to babysit.
- Go to the Zero Trust dashboard (
one.dash.cloudflare.com) → Networks → Tunnels → Create a tunnel → choose Cloudflared. - Name the tunnel, then copy the install command Cloudflare generates. It embeds a long token and looks like
sudo cloudflared service install <TUNNEL_TOKEN>. - Paste and run that command on your server. It registers a
systemdservice (or alaunchdjob on macOS), starts on boot, and restarts on crash. Back in the dashboard, a green Healthy status means the connection is live. - Open the Public Hostname tab → Add a public hostname. Set the subdomain (
app), the domain, and the local service (http://localhost:8080), then Save. Cloudflare auto-creates the CNAMEapp.yourdomain.com → <uuid>.cfargotunnel.com.
Visit https://app.yourdomain.com and you're serving over HTTPS with a Cloudflare-issued certificate. Your local box needs zero TLS configuration.
Method 2: The CLI (locally-managed) tunnel
If you prefer config-as-code, want everything in version control, or are routing several services through one tunnel, use the locally-managed workflow. The command names below are the stable, documented cloudflared verbs:
# 1. Authenticate this machine (opens a browser to pick your domain)
cloudflared tunnel login
# 2. Create a named tunnel — writes a credentials .json and prints a UUID
cloudflared tunnel create my-tunnel
# 3. Point a hostname at the tunnel (creates the DNS CNAME for you)
cloudflared tunnel route dns my-tunnel app.yourdomain.com
Then describe your routing in ~/.cloudflared/config.yml:
tunnel: my-tunnel
credentials-file: /home/user/.cloudflared/<UUID>.json
ingress:
- hostname: app.yourdomain.com
service: http://localhost:8080
- hostname: files.yourdomain.com
service: http://localhost:80
- service: http_status:404 # required catch-all, must be last
Every config file must end with a catch-all rule (the http_status:404 line), or cloudflared refuses to start. Test it in the foreground with cloudflared tunnel run my-tunnel, and once it works, install it as a service so it survives reboots:
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared # confirm it's active
sudo journalctl -u cloudflared -f # tail the logs
This is the approach that pairs cleanly with a self-hosting stack; see our step-by-step VPS self-hosting guide for wiring it alongside Docker.
Test it in 30 seconds with a quick tunnel
Before committing to any of that, you can prove the concept with no account and no DNS at all:
cloudflared tunnel --url http://localhost:8080
This prints a random https://<words>.trycloudflare.com URL that routes straight to your local port and dies when you stop the process. It's rate-limited and meant for demos, webhook testing, and "can a colleague see this right now?" moments — not production. But it's the fastest way to confirm your app is reachable through Cloudflare's edge.
Cloudflare Tunnel vs the alternatives
A tunnel is not the only way to reach a self-hosted app. Here's how it compares to the usual options in 2026:
| Method | Open ports? | Public IP needed | Cost | Best for |
|---|---|---|---|---|
| Cloudflare Tunnel | No | No | Free | Public web apps behind a domain |
| Port forwarding + reverse proxy | Yes | Yes | Free (+ domain) | Full control, no third party in path |
| Tailscale / WireGuard | No | No | Free tier | Private access for you and your team only |
| ngrok | No | No | Free tier, paid for custom domains | Quick demos, dev webhooks |
| Static IP + firewall rules | Yes | Yes | ISP surcharge | Legacy or non-HTTP services |
The short version: if you want something publicly reachable at your own domain with a managed edge in front, a tunnel wins. If you only need yourself to reach the box, a mesh VPN like Tailscale is simpler and keeps traffic private end to end.
When NOT to use a Cloudflare Tunnel
Tunnels are excellent, but they aren't universal:
- Heavy non-HTML media. Cloudflare's terms restrict serving large volumes of video or file downloads that aren't part of a website. A media-heavy self-hosted Plex or file server can bump into that; a reverse proxy on your own IP avoids the question.
- Cloudflare sees your traffic. TLS terminates at Cloudflare's edge, so they can technically inspect requests. For most self-hosters that trade is fine; for zero-trust-of-third-parties setups, it isn't.
- You need a domain on Cloudflare. No domain, or DNS you can't move to Cloudflare, means named tunnels aren't an option (quick tunnels still work for testing).
- Latency-sensitive or non-HTTP protocols can be trickier and sometimes need Cloudflare's paid Spectrum product rather than a plain tunnel.
Lock it down with Cloudflare Access
Exposing an app publicly and authenticating it are two different jobs. A tunnel handles the transport; to gate who can reach the app, add Cloudflare Access in front of the same hostname. It puts a login page (Google, GitHub, email one-time PIN) before your app, and Cloudflare's Zero Trust plan is free for up to 50 users. For an admin dashboard or a database UI like the one in our self-hosted Supabase guide, that login gate matters more than the tunnel itself.
FAQ
Can I use Cloudflare Tunnel for free? Yes. Tunnels are included on every Cloudflare plan, including the free tier, with no limit on the number of tunnels or routed services. You only need a (paid) domain and a free Cloudflare account. The Zero Trust features that add authentication are also free for up to 50 users.
What is the point of a Cloudflare Tunnel? It lets a server reach the public internet without exposing any inbound ports or a public IP. The server dials out to Cloudflare, and inbound requests ride that outbound connection back. You get HTTPS, DDoS protection, and a hidden origin IP without touching your firewall or router.
Can I use Cloudflare Tunnel without a domain?
For a permanent, named tunnel, no — you need a domain whose DNS is managed by Cloudflare. For throwaway testing you can run a quick tunnel (cloudflared tunnel --url ...), which hands you a random trycloudflare.com URL with no domain and no account required.
What can you use Cloudflare Tunnels for? Publishing self-hosted apps (dashboards, wikis, Nextcloud, home-lab services), exposing a local dev server for webhook testing, giving remote SSH or RDP access through Cloudflare, and putting a free managed edge in front of any HTTP service running on hardware you control.
Sources
- Cloudflare — Cloudflare Tunnel documentation: what a tunnel is, that it's outbound-only, and that it works on the free plan.
- Cloudflare — Set up Cloudflare Tunnel: the
cloudflared service installcommand and the quick-tunnel--urlflag. - Cloudflare — Tunnel configuration file: ingress rules and the required catch-all rule.
- Cloudflare — Cloudflare One / Zero Trust: Access authentication in front of a tunnel.
- GitHub — cloudflare/cloudflared: the tunnel client, install packages, and supported platforms.
Some links may earn us a commission at no extra cost to you.
Waqas Ahmed Waseer
Waqas Ahmed Waseer is a developer and automation builder with 8+ years shipping production systems used by 100k+ people. He builds custom multi-tenant SaaS, AI automation (n8n, LLM workflows, WhatsApp bots) and hosting infrastructure (WHM/cPanel, CloudLinux) — and is the maker of WaSphere, FlowMaticX, and the WaseerHost hosting brand. 100+ projects delivered for SMBs, agencies and funded startups.



