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 cloudflaredVerify the install:
cloudflared --versionStep 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 loginNow create a named tunnel. The name is just a label for you.
cloudflared tunnel create homelabThis 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:404Each 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.comIf 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 homelabVisit 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 cloudflaredCheck the status and logs to confirm it stays connected:
systemctl status cloudflared
journalctl -u cloudflared -fStep 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:
- Choose a Self-hosted application type.
- Set the application domain to your hostname, for example
dashboard.example.com. - Add a policy. A common starting point is an Allow policy that matches specific email addresses or an entire email domain.
- 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.comThis 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
latesttag 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.
