Cloudflare Tunnels let you expose services on your home network without opening a single port on your router. No port forwarding, no dynamic DNS juggling, the container reaches out to Cloudflare, and Cloudflare handles the rest. I’ve been running Uptime Kuma through a Cloudflare Tunnel, and it’s one of the cleaner ways to get remote access set up without punching holes in your firewall.
This guide covers everything from creating the tunnel in the Cloudflare Zero Trust dashboard to getting the Docker container running and routing traffic to your services.
What You Need Before Starting
- A domain added to your Cloudflare account (free tier works)
- Docker and Docker Compose installed on your host
- The service you want to expose already running (Uptime Kuma, Nginx Proxy Manager, Home Assistant, etc.)
If you’re running Docker inside Proxmox, how to run Docker in Proxmox on a VM covers that setup first. If you’re managing your containers with a UI, Dockge is worth a look before you start stacking Compose files.
Create the Tunnel in the Cloudflare Zero Trust Dashboard
Before you touch Docker, you need to create the tunnel and get a token from Cloudflare.
1. Log in to the Cloudflare Zero Trust dashboard.
Go to one.dash.cloudflare.com and sign in. In the left sidebar, navigate to Networks > Tunnels.
2. Click “Create a tunnel.”
Select Cloudflared as the connector type. Give the tunnel a name, something descriptive like homelab or docker-host. Click Save.
3. Copy your tunnel token.
After naming the tunnel, Cloudflare shows you installation options. Select Docker. You’ll see a docker run command with a long token embedded in it. Copy that token, you’ll need it in the next step.
The token is specific to this tunnel. If you delete the tunnel and create a new one, the old token stops working.
4. Add a public hostname.
Still in the tunnel settings, go to the Public Hostname tab. Click Add a public hostname and fill in:
- Subdomain: whatever you want (e.g.
kuma) - Domain: your Cloudflare-managed domain
- Type: HTTP or HTTPS depending on your service
- URL: the internal IP and port of your service (e.g.
http://192.168.1.50:3001)
Save it. Cloudflare will automatically create the DNS record on your domain.
You can add multiple hostnames to a single tunnel. One container handles all of them.
Configure the Docker Compose File
Once you have the token, set up the cloudflared container. Here’s the Compose file:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=YOUR_TOKEN_HERE
Replace YOUR_TOKEN_HERE with the token you copied from the dashboard.
A few things worth noting about this config:
On the image tag: :latest is fine for getting started, but if you want stability, pin to a specific version tag like cloudflare/cloudflared:2025.4.0 and update it deliberately rather than letting it pull automatically.
On the restart policy: unless-stopped means the container comes back after a reboot but won’t restart if you manually stop it. That’s the right behavior for most setups.
On networking: By default, the cloudflared container connects to other containers on the same Docker bridge network using their container names. If your service is in a different Compose stack, use its host IP and port instead.
Start the Container
docker compose up -d
Check the logs to confirm it connected:
docker logs cloudflared
You’re looking for something like Connection registered with ID in the output. If you see that, the tunnel is up. Head to the Cloudflare Zero Trust dashboard and the tunnel status should show Healthy.
If the tunnel shows Inactive, the container either isn’t running or the token is wrong. Double-check the token value, it’s easy to accidentally trim a character when copying.
Add More Services to the Same Tunnel
You don’t need a separate container for each service. Go back to Networks > Tunnels in the dashboard, click on your tunnel, and add another public hostname pointing to a different internal IP and port.
I run several services through a single tunnel. One cloudflared container, multiple subdomains. It scales cleanly.
If you’re also running Pi-hole or AdGuard Home on your network, your DNS requests through the tunnel will use whatever resolver the Cloudflare network uses, not your local Pi-hole. That’s expected behavior, not a bug.
The Trade-off Worth Knowing
Cloudflare Tunnels are excellent for remote access, but your traffic routes through Cloudflare’s network. That means Cloudflare can see unencrypted HTTP traffic between their edge and your container. For most self-hosted services, that’s a non-issue, but for anything sensitive, run HTTPS end-to-end or consider a split tunnel VPN instead.
The other thing tunnels don’t do: they aren’t a firewall. If a service has weak authentication, a Cloudflare Tunnel still exposes it to the internet. Use Cloudflare Access (available free in the Zero Trust dashboard) to add an identity layer in front of anything you’d rather not leave wide open.
If you want to see how Cloudflare fits into a broader home lab networking strategy alongside DDNS, my video on setting up DDNS on pfSense using Cloudflare covers how the two tools complement each other.
For keeping an eye on whether your tunnels and services stay up, Uptime Kuma is what I’ve been using to monitor everything.
