Skip to content
Back to skills

Cloudflare Tunnel Setup

ASecurity

Sets up a new Cloudflare Tunnel from scratch for secure remote access to home network services. Use when creating a new tunnel, replacing an expired tunnel token, or setting up remote access for a fresh installation. Triggers on "setup cloudflare tunnel", "create tunnel", "new tunnel token", "configure remote access", "cloudflare tunnel from scratch", or "tunnel not configured". Works with .env, docker-compose.yml, and Cloudflare Zero Trust.

  • 666 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added February 8, 2026
developmentpythonrustgobashdockerdebugginggitapibackend

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned February 10, 2026

npx -y skills add majiayu000/claude-skill-registry --skill cloudflare-tunnel-setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cloudflare Tunnel Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cloudflare Tunnel Setup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-cloudflare-tunnel-setup/badge)](https://www.skillsdirectory.com/skills/majiayu000-cloudflare-tunnel-setup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cloudflare-tunnel-setup
description: |
  Sets up a new Cloudflare Tunnel from scratch for secure remote access to home network
  services. Use when creating a new tunnel, replacing an expired tunnel token, or setting
  up remote access for a fresh installation. Triggers on "setup cloudflare tunnel", "create
  tunnel", "new tunnel token", "configure remote access", "cloudflare tunnel from scratch",
  or "tunnel not configured". Works with .env, docker-compose.yml, and Cloudflare Zero Trust.
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
  - Grep
  - Glob
---

# Cloudflare Tunnel Setup Skill

Interactive guide for setting up a new Cloudflare Tunnel from scratch.

## Quick Start

Run this skill when you need to:
1. Create a new Cloudflare Tunnel
2. Replace an expired or invalid tunnel token
3. Set up remote access for a fresh installation

**Minimum requirements:** Cloudflare account, domain on Cloudflare, Docker installed.

## Table of Contents

1. [When to Use This Skill](#1-when-to-use-this-skill)
2. [What This Skill Does](#2-what-this-skill-does)
3. [Instructions](#3-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
4. [Supporting Files](#4-supporting-files)
5. [Expected Outcomes](#5-expected-outcomes)
6. [Requirements](#6-requirements)
7. [Red Flags to Avoid](#7-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

1. **Checks Prerequisites** - Verifies .env exists and cloudflared is in docker-compose
2. **Guides Tunnel Creation** - Step-by-step instructions for Cloudflare dashboard
3. **Configures Token** - Adds tunnel token to .env file
4. **Restarts Container** - Restarts cloudflared to apply new token
5. **Guides Hostname Setup** - Instructions for configuring public hostnames
6. **Verifies Connectivity** - Confirms tunnel is connected and working

## Instructions

### 3.1 Check Prerequisites

**Step 1: Verify .env file exists**

```bash
ls -la /home/dawiddutoit/projects/network/.env
```

If missing, create from example:
```bash
cp /home/dawiddutoit/projects/network/.env.example /home/dawiddutoit/projects/network/.env
```

**Step 2: Check if CLOUDFLARE_TUNNEL_TOKEN exists**

```bash
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**

```bash
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:

1. Go to: https://one.dash.cloudflare.com -> Access -> Tunnels
2. Click "Create a tunnel" -> Select "Cloudflared" connector
3. Name tunnel (e.g., "pi-home") -> Choose "Docker" as connector type
4. Copy the tunnel token (long base64 string after `--token`)
5. 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**

```bash
grep "CLOUDFLARE_TUNNEL_TOKEN" /home/dawiddutoit/projects/network/.env
```

**Step 2: Add or update token**

If line exists, update it. If not, add it.

```bash
# 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:**

```bash
# 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**

```bash
cd /home/dawiddutoit/projects/network && docker compose restart cloudflared
```

Or if container doesn't exist yet:
```bash
cd /home/dawiddutoit/projects/network && docker compose up -d cloudflared
```

**Step 2: Check container is running**

```bash
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**

```bash
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:

1. Go to: https://one.dash.cloudflare.com -> Access -> Tunnels
2. Click tunnel name -> Configure -> Public Hostname -> Add
3. 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**

```bash
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)**

```bash
# 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)**

```bash
./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
- `.env` file 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.internal` on 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 configure` to update ingress via API
- For authentication, use the `configure-google-oauth` skill after tunnel setup
- The existing `troubleshoot-tunnel-connectivity` skill handles issues with running tunnels

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.