Cloudflare Tunnel Setup Skill
Interactive guide for setting up a new Cloudflare Tunnel from scratch.
Quick Start
Run this skill when you need to:
- •Create a new Cloudflare Tunnel
- •Replace an expired or invalid tunnel token
- •Set up remote access for a fresh installation
Minimum requirements: Cloudflare account, domain on Cloudflare, Docker installed.
Table of Contents
- •When to Use This Skill
- •What This Skill Does
- •Instructions
- •3.1 Check Prerequisites
- •3.2 Create Tunnel in Dashboard
- •3.3 Configure Token in .env
- •3.4 Restart Cloudflared Container
- •3.5 Configure Public Hostnames
- •3.6 Verify Connectivity
- •Supporting Files
- •Expected Outcomes
- •Requirements
- •Red Flags to Avoid
When to Use This Skill
Explicit Triggers:
- •"Setup cloudflare tunnel"
- •"Create a new tunnel"
- •"Configure remote access"
- •"Replace tunnel token"
- •"Tunnel from scratch"
Implicit Triggers:
- •Fresh installation of network infrastructure
- •Tunnel token expired or invalid
- •CLOUDFLARE_TUNNEL_TOKEN missing from .env
- •Moving infrastructure to new hardware
Debugging Triggers:
- •"Tunnel token not found"
- •"cloudflared container won't start"
- •"No CLOUDFLARE_TUNNEL_TOKEN in .env"
What This Skill Does
- •Checks Prerequisites - Verifies .env exists and cloudflared is in docker-compose
- •Guides Tunnel Creation - Step-by-step instructions for Cloudflare dashboard
- •Configures Token - Adds tunnel token to .env file
- •Restarts Container - Restarts cloudflared to apply new token
- •Guides Hostname Setup - Instructions for configuring public hostnames
- •Verifies Connectivity - Confirms tunnel is connected and working
Instructions
3.1 Check Prerequisites
Step 1: Verify .env file exists
ls -la /home/dawiddutoit/projects/network/.env
If missing, create from example:
cp /home/dawiddutoit/projects/network/.env.example /home/dawiddutoit/projects/network/.env
Step 2: Check if CLOUDFLARE_TUNNEL_TOKEN exists
grep -E "^CLOUDFLARE_TUNNEL_TOKEN" /home/dawiddutoit/projects/network/.env
Possible outcomes:
- •Line exists with value: Token already configured (check if valid)
- •Line exists but empty/placeholder: Needs real token
- •Line not found: Token needs to be added
Step 3: Verify docker-compose has cloudflared service
grep -A 10 "cloudflared:" /home/dawiddutoit/projects/network/docker-compose.yml
If cloudflared service is missing, it needs to be added. See references/reference.md for service definition.
3.2 Create Tunnel in Dashboard
Provide these instructions to the user:
- •Go to: https://one.dash.cloudflare.com -> Access -> Tunnels
- •Click "Create a tunnel" -> Select "Cloudflared" connector
- •Name tunnel (e.g., "pi-home") -> Choose "Docker" as connector type
- •Copy the tunnel token (long base64 string after
--token) - •Save token securely
Token format: Base64-encoded JSON containing Account ID, Tunnel ID, and Secret.
3.3 Configure Token in .env
Step 1: Check current token status
grep "CLOUDFLARE_TUNNEL_TOKEN" /home/dawiddutoit/projects/network/.env
Step 2: Add or update token
If line exists, update it. If not, add it.
# If line exists, use sed to replace # If not, append to file # The token should look like: # CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiYWJjZDEyMzQ...
Use the Edit tool to update .env:
- •Look for existing
CLOUDFLARE_TUNNEL_TOKEN=line - •Replace with new token value
- •Or add new line if not present
Validate token format:
# Token should be base64 decodable
TUNNEL_TOKEN=$(grep "CLOUDFLARE_TUNNEL_TOKEN=" /home/dawiddutoit/projects/network/.env | cut -d'=' -f2)
echo "$TUNNEL_TOKEN" | base64 -d 2>/dev/null | python3 -c "import sys, json; d=json.load(sys.stdin); print('Valid token for account:', d.get('a', 'unknown'))"
3.4 Restart Cloudflared Container
Step 1: Restart the container
cd /home/dawiddutoit/projects/network && docker compose restart cloudflared
Or if container doesn't exist yet:
cd /home/dawiddutoit/projects/network && docker compose up -d cloudflared
Step 2: Check container is running
docker ps | grep cloudflared
Expected output:
CONTAINER ID IMAGE STATUS NAMES abc123... cloudflare/cloudflared:latest Up X seconds cloudflared
Step 3: Check logs for successful connection
docker logs cloudflared --tail 30
Look for these success indicators:
INF Registered tunnel connection connIndex=0 location=... INF Registered tunnel connection connIndex=1 location=... INF Registered tunnel connection connIndex=2 location=... INF Registered tunnel connection connIndex=3 location=...
4 registered connections = healthy tunnel (2 connections per Cloudflare edge location)
If errors appear:
- •
token is invalid: Token was copied incorrectly or expired - •
already registered: Another instance using same token (stop other instances) - •
network error: Check internet connectivity
3.5 Configure Public Hostnames
After tunnel is connected, configure services in Cloudflare dashboard:
- •Go to: https://one.dash.cloudflare.com -> Access -> Tunnels
- •Click tunnel name -> Configure -> Public Hostname -> Add
- •For each service, enter subdomain, domain (temet.ai), Type (HTTP), and URL
Example hostnames:
| Service | Subdomain | URL |
|---|---|---|
| Pi-hole | pihole | pihole:80 |
| Webhook | webhook | webhook:9000 |
| Jaeger | jaeger | 192.168.68.135:16686 |
URL patterns:
- •Docker container:
container:port(e.g., pihole:80) - •Host service:
host-ip:port(e.g., 192.168.68.135:16686) - •LAN device:
device-ip:port(e.g., 192.168.68.105:80)
Important: Use HTTP (not HTTPS) for backends. Do NOT use host.docker.internal on Linux.
3.6 Verify Connectivity
Step 1: Verify tunnel is registered
docker logs cloudflared 2>&1 | grep -E "Registered tunnel|connIndex"
Expected: 4 "Registered tunnel connection" messages
Step 2: Check tunnel status in dashboard
Go to: https://one.dash.cloudflare.com Navigate to: Access -> Tunnels Status should show: "HEALTHY" with green indicator
Step 3: Test external access (from mobile data or external network)
# From a device NOT on home network: curl -I https://pihole.temet.ai # Expected: HTTP 302 (redirect to Cloudflare Access) or HTTP 200 (if no auth)
Step 4: Run diagnostic script (optional)
./scripts/cf-tunnel-config.sh show
Supporting Files
| File | Purpose |
|---|---|
references/reference.md | Docker-compose service definition, token format details |
examples/examples.md | Example configurations for common setups |
Expected Outcomes
Success:
- •CLOUDFLARE_TUNNEL_TOKEN in .env
- •cloudflared container running
- •4 tunnel connections registered in logs
- •Tunnel shows "HEALTHY" in dashboard
- •Services accessible via *.temet.ai from external network
Partial Success:
- •Token configured but hostnames not set up (user reminder provided)
- •Tunnel connected but services return 502 (backend configuration issue)
Failure Indicators:
- •"token is invalid" in logs -> Re-copy token from dashboard
- •Container restart loop -> Check token format
- •0 registered connections -> Network/firewall issue
Requirements
Environment:
- •Cloudflare account with domain
- •Docker and Docker Compose installed
- •Internet connectivity
- •
.envfile in project root
Accounts/Access:
- •Cloudflare dashboard access
- •Zero Trust enabled (free tier works)
Tools needed:
- •Read, Write, Edit (for .env configuration)
- •Bash (for docker commands and verification)
- •Grep (for checking existing configuration)
Red Flags to Avoid
- • Do not commit tunnel token to git (keep in .env only)
- • Do not use
host.docker.internalon Linux hosts - • Do not use HTTPS URLs for internal service backends
- • Do not share tunnel tokens between environments
- • Do not skip the verification step (3.6)
- • Do not forget to configure public hostnames after tunnel connects
- • Do not leave placeholder token values in .env
Notes
- •Tunnel tokens do not expire but can be revoked in dashboard
- •Each tunnel can have multiple connectors (for redundancy)
- •Tunnel configuration changes apply automatically (no restart needed)
- •Use
./scripts/cf-tunnel-config.sh configureto update ingress via API - •For authentication, use the
configure-google-oauthskill after tunnel setup - •The existing
troubleshoot-tunnel-connectivityskill handles issues with running tunnels