Running a self-hosted home lab is one of the best ways to learn about infrastructure, networking, and the services that power modern applications. The hard part usually is not standing up the services. It is exposing them safely. Port forwarding on your home router opens attack surface, leaks your residential IP address, and often runs into carrier-grade NAT that makes inbound connections impossible.

A Cloudflare Zero Trust Tunnel solves this cleanly. It creates an outbound-only connection from a lightweight daemon on your network to Cloudflare’s edge. No inbound ports, no exposed home IP, and you can layer authentication in front of everything. This guide walks through the full setup, from a fresh tunnel to protecting a service with access policies.

How Cloudflare Tunnel works

The tunnel model inverts the usual approach. Instead of the internet reaching into your network, a small agent called cloudflared runs on a machine in your lab and dials outbound to Cloudflare. Cloudflare routes public requests for your hostname to that agent over the established connection, and the agent proxies them to the local service.

Because the connection is outbound only, you never open a single port on your firewall. Requests to your service also terminate on Cloudflare’s network first, so you get TLS, DDoS protection, and access controls before traffic ever reaches your hardware.

What you need before starting

  • A domain managed through Cloudflare (the nameservers must point to Cloudflare).
  • A Cloudflare account with Zero Trust enabled (the free tier is generous and works fine for home labs).
  • A machine inside your lab network that can run cloudflared. This can be a Raspberry Pi, a Docker host, a NAS, or any always-on server.
  • At least one local service to expose, for example a dashboard at http://localhost:8080.

Step 1: Install cloudflared

Install the connector on your lab machine. On Debian or Ubuntu you can use the official package repository. On other platforms, download the binary from Cloudflare’s releases or use the Docker image.

# Debian/Ubuntu example
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared

Verify the install:

cloudflared --version

Step 2: Authenticate and create the tunnel

Log in to link the daemon with your Cloudflare account. This opens a browser window where you authorize a domain.

cloudflared tunnel login

Now create a named tunnel. The name is just a label for you.

cloudflared tunnel create homelab

This generates a credentials file (a JSON file containing a tunnel token) and prints a tunnel UUID. Keep that file safe. It is the secret that lets your connector authenticate.

Step 3: Configure ingress rules

Create a configuration file that maps public hostnames to local services. On Linux this typically lives at ~/.cloudflared/config.yml or /etc/cloudflared/config.yml.

tunnel: homelab
credentials-file: /home/youruser/.cloudflared/<TUNNEL-UUID>.json

ingress:
  - hostname: dashboard.example.com
    service: http://localhost:8080
  - hostname: files.example.com
    service: http://localhost:9000
  # Catch-all rule is required and must be last
  - service: http_status:404

Each entry proxies a public hostname to a local address. The final catch-all rule is mandatory. It handles any hostname that does not match an earlier rule.

Step 4: Route DNS to the tunnel

Point each hostname at the tunnel. Cloudflare creates a proxied CNAME record automatically.

cloudflared tunnel route dns homelab dashboard.example.com
cloudflared tunnel route dns homelab files.example.com

If you prefer, you can add these records manually in the Cloudflare dashboard as CNAMEs pointing to <TUNNEL-UUID>.cfargotunnel.com with the proxy enabled.

Step 5: Run the tunnel as a service

You can start the tunnel in the foreground to test it:

cloudflared tunnel run homelab

Visit your hostname in a browser to confirm the service loads. Once it works, install it as a system service so it survives reboots.

sudo cloudflared service install
sudo systemctl enable --now cloudflared

Check the status and logs to confirm it stays connected:

systemctl status cloudflared
journalctl -u cloudflared -f

Step 6: Protect services with Zero Trust access

At this point your services are reachable over the public internet. For a home lab that is rarely what you want. The real value of Zero Trust is putting an authentication layer in front of your services so only you (and people you approve) can reach them.

In the Cloudflare Zero Trust dashboard, go to Access and create an application:

  1. Choose a Self-hosted application type.
  2. Set the application domain to your hostname, for example dashboard.example.com.
  3. Add a policy. A common starting point is an Allow policy that matches specific email addresses or an entire email domain.
  4. Optionally require multi-factor authentication or restrict by country and IP range.

Now when someone visits the hostname, Cloudflare intercepts the request and requires them to authenticate before it ever reaches your tunnel. Your local service never sees unauthenticated traffic.

Handling non-HTTP services

Not everything in a home lab is a web app. For SSH, RDP, or arbitrary TCP services, you can still route through the tunnel. Add an ingress rule pointing at the local port, then use cloudflared access on the client side to open a short-lived authenticated connection.

# In config.yml
  - hostname: ssh.example.com
    service: ssh://localhost:22
# On your client machine
cloudflared access ssh --hostname ssh.example.com

This gives you authenticated SSH without ever exposing port 22 to the internet.

Running in Docker

If your lab is containerized, run the connector as a container instead. Create the tunnel and generate a token in the dashboard, then pass it to the container.

docker run -d --restart unless-stopped \
  --name cloudflared \
  cloudflare/cloudflared:latest \
  tunnel --no-autoupdate run --token <YOUR-TUNNEL-TOKEN>

When you use the token-based (remote) configuration, you manage ingress rules from the Cloudflare dashboard rather than a local config file, which keeps everything in one place.

Operational tips

  • Keep cloudflared updated. Security fixes ship regularly. Package installs and the Docker latest tag make this easy.
  • Use one tunnel with many ingress rules rather than a tunnel per service. It is simpler to manage and monitor.
  • Lock down internal services too. The tunnel protects the edge, but keep your local services on a segmented network or VLAN so a compromised container cannot roam freely.
  • Monitor the connection. The Zero Trust dashboard shows tunnel health and recent access logs, which are useful for spotting failed logins.
  • Back up your credentials file and config. Losing the tunnel credentials means recreating the tunnel and its DNS records.

Takeaway

A Cloudflare Zero Trust Tunnel gives home lab builders a secure, port-free way to reach self-hosted services from anywhere. The core pattern is simple: install cloudflared, create a named tunnel, map hostnames to local services with ingress rules, route DNS, and run it as a service. Layering Access policies on top turns a publicly reachable endpoint into an authenticated one, which is exactly what you want for anything running on hardware in your home. Start with one service, confirm the flow end to end, then expand your ingress rules as your lab grows.