· Jonathan Cutrer · Engineering  · 4 min read

Cloudflare Tunnels: Expose Local Services Without Opening Ports

A step-by-step guide to running Cloudflare Tunnel on a home server — no port forwarding, no firewall rules, no static IP required.

A step-by-step guide to running Cloudflare Tunnel on a home server — no port forwarding, no firewall rules, no static IP required.

I have a home server running a handful of internal services — a Grafana instance, a small FastAPI service for personal automation, a few Docker containers that I want to reach from anywhere. For years, I ran a VPN to access them. It worked, but every time I was on a network that blocked non-standard ports, I was stuck.

Cloudflare Tunnels replaced all of that. The server makes an outbound connection to Cloudflare’s network, and Cloudflare routes traffic to it through a subdomain on your domain. No inbound ports. No firewall rules. No static IP. Works from anywhere.

Here’s how to set it up from scratch.

What You Need Before Starting

  • A domain managed by Cloudflare DNS (free)
  • A server or machine running the service you want to expose
  • cloudflared installed on that machine
  • A Cloudflare account (free tier works)

Step 1: Install cloudflared

On Ubuntu/Debian:

curl -L --output cloudflared.deb \
  https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb

Verify it’s installed:

cloudflared --version

Step 2: Authenticate cloudflared

cloudflared tunnel login

This opens a browser window and asks you to select which Cloudflare zone (domain) you want the tunnel associated with. After you authorize, cloudflared writes a certificate to ~/.cloudflared/cert.pem.

Step 3: Create the Tunnel

cloudflared tunnel create my-home-server

This creates a tunnel with a UUID and writes credentials to ~/.cloudflared/<UUID>.json. Note the UUID — you’ll need it.

cloudflared tunnel list

Should show your new tunnel with its ID.

Step 4: Write the Config File

Create ~/.cloudflared/config.yml:

tunnel: <your-tunnel-uuid>
credentials-file: /home/youruser/.cloudflared/<your-tunnel-uuid>.json

ingress:
  - hostname: grafana.yourdomain.com
    service: http://localhost:3000
  - hostname: api.yourdomain.com
    service: http://localhost:8000
  - service: http_status:404

The last service: http_status:404 is required — it’s the catch-all for requests that don’t match any hostname rule. Without it, cloudflared will refuse to start.

Step 5: Create DNS Records

cloudflared tunnel route dns my-home-server grafana.yourdomain.com
cloudflared tunnel route dns my-home-server api.yourdomain.com

This creates CNAME records in Cloudflare DNS pointing to <uuid>.cfargotunnel.com. You can verify them in the Cloudflare dashboard under DNS.

Step 6: Run the Tunnel

cloudflared tunnel run my-home-server

Test that it works by navigating to grafana.yourdomain.com. If you see your Grafana login page, it’s working.

Step 7: Run as a System Service

You don’t want to keep a terminal session open. Install it as a systemd service:

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

Check the status:

sudo systemctl status cloudflared
journalctl -u cloudflared -f

By default, your services are publicly accessible to anyone who knows the URL. If that’s not what you want, add Cloudflare Access policies in the Cloudflare Zero Trust dashboard.

For personal services, I use a simple email-based policy — anyone with my email address can authenticate through a one-time code. It adds maybe two extra clicks when opening a protected service for the first time.

Step 9: Test Failover Behavior

Kill one of your local services and try to reach it through the tunnel. You should get a Cloudflare error page rather than a connection timeout. This is correct behavior — Cloudflare handles the error gracefully rather than hanging the connection.

Bring the service back up and confirm it starts serving again within a few seconds. The tunnel reconnects automatically.

Step 10: Keep cloudflared Updated

sudo apt update && sudo apt upgrade cloudflared

Or check the GitHub releases page and re-download. The .deb update is straightforward. Tunnel credentials are not affected by updates.

What This Doesn’t Cover

Access control beyond the basic email policy, multiple tunnels from one machine, or high-availability setups with multiple machines pointing at the same hostname. Those are all possible but depend on what you’re building. The Cloudflare docs are genuinely good for the more advanced configurations.

This setup has been running on my home server for eight months without any issues. The tunnel reconnects automatically after reboots, power outages, and ISP glitches. It’s the most reliable piece of infrastructure I’m running.

Back to Blog

Related Posts

View All Posts »
The Tools I Actually Use in 2026

The Tools I Actually Use in 2026

Not a gear list for the algorithm. Just the software and setup I actually reach for every day — with honest notes on what I'd change.