← All guides
Software Install Tutorials 5 min read • 12 views

Reach Your Homelab with Cloudflare Tunnel and Protect It with Access

CnP
CnP Author

Cloudflare Tunnel lets a connector inside your home network make outbound connections to Cloudflare. You can reach a web application through a public HTTPS hostname without opening an inbound port on your router. Cloudflare Access then decides who may reach that hostname.

We will use a small test application first. Once authentication works, you can point the route at your actual dashboard. This guide covers a browser-based HTTP application, not a VPN for every protocol or a public RTSP video relay.

An approved browser passes through Cloudflare Access and the outbound tunnel to an unexposed Docker service.
An approved browser passes through Cloudflare Access and the outbound tunnel to an unexposed Docker service.

What you need

  • A domain with an active Cloudflare DNS zone and a Cloudflare account.
  • A configured Zero Trust organization and a login method such as an identity provider or email one-time PIN.
  • A Linux server with Docker Engine and the Compose plugin.
  • Outbound network access for the tunnel connector. No router port-forwarding rule is required.

The example hostname is lab.example.com. Replace it with your own subdomain. Keep your application's own login enabled as an additional layer.

1. Create the Access application first

In the Cloudflare dashboard, open Zero Trust → Access controls → Applications. Create a new Self-hosted and private application and add the public hostname lab.example.com. Older dashboard layouts may call this simply “Self-hosted.”

Add an Allow policy that includes only your specific email address or the appropriate identity-provider group. Select the intended login method and choose a session duration. Protect the whole hostname unless you intentionally want different rules for individual paths.

Save the application before publishing the tunnel route. Avoid an “Everyone” Allow policy or a Bypass policy for an admin tool. Access applications deny users by default until an Allow policy matches.

2. Create a remotely managed tunnel

Open Networking → Tunnels, create a cloudflared tunnel, and name it homelab. Choose the Docker connector instructions. Copy only the tunnel token from the provided command; do not paste the entire Docker command into the file below.

On the server, create a protected working directory and edit the token file locally:

mkdir -p ~/homelab-tunnel
cd ~/homelab-tunnel
umask 077
touch tunnel.env
chmod 600 tunnel.env
nano tunnel.env
TUNNEL_TOKEN=REPLACE_WITH_YOUR_TUNNEL_TOKEN

Keep the actual file out of Git, screenshots, and support messages. An environment file avoids putting the token in your Compose source or shell history, but Docker administrators can still inspect a container's environment. Rotate the tunnel token if it is disclosed.

3. Start the connector and demo service

Save this as compose.yaml in the same directory:

services:
  demo:
    image: traefik/whoami:v1.11
    restart: unless-stopped
    networks: [homelab]

  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    env_file:
      - tunnel.env
    depends_on:
      - demo
    networks: [homelab]

networks:
  homelab:
docker compose config --quiet
docker compose up -d
docker compose ps
docker compose logs --tail 50 cloudflared

The connector should register tunnel connections, and the dashboard should show the tunnel as healthy. The demo application has no published host port. Both containers share the same Docker network so the connector can resolve demo.

The example uses the current cloudflared release via its latest tag for initial setup. For a repeatable production deployment, select and pin an approved release tag or image digest, then update deliberately.

4. Add the published application route

Under your tunnel's Routes, add a Published application route. Use hostname lab.example.com, service type HTTP, and service URL demo:80 (equivalent to http://demo:80).

Inside the connector container, localhost means the connector itself. It does not mean another Compose service or the Docker host. For an existing containerized dashboard, join the connector to its network and use that service's name and internal port.

The public URL uses HTTPS even though this isolated demonstration uses HTTP between the connector and demo container. For an origin on another machine, evaluate that network segment and use properly verified HTTPS where appropriate. Do not disable origin certificate verification merely to hide a certificate configuration error.

5. Test both access outcomes

  1. Open https://lab.example.com in a private browser window. You should reach Access authentication before seeing the demo output.
  2. Sign in with the allowed identity. The demo should show request information.
  3. Use a separate private session with a disallowed identity. It must not reach the application.
  4. Review Access logs and confirm there is no second unprotected hostname exposing the same admin service.

Troubleshooting

  • 502: the connector may be online but unable to reach the origin. Check the service name, internal port, network membership, and application logs.
  • Tunnel disconnected: check the token, container logs, DNS resolution, and outbound firewall rules.
  • No login prompt: verify the exact hostname and path coverage in Access, then check for Bypass policies or an existing authenticated session.
  • Redirect loop: check the application's public base URL and reverse-proxy settings. An origin that automatically redirects HTTP to HTTPS may require an HTTPS service URL.

Once these checks pass, replace the demo origin with your intended app and repeat the allowed/denied tests. Uptime Kuma is a useful next project to host behind this setup.

Official references

CnP

About Copy And Paste

Just a curious soul dabbling in the world of IT. Exploring coding, sysadmin tricks, and tech automation — because who doesn’t love copying and pasting clean code that just works?