This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Cloudflare Tunnel Configuration
Relevant source files
Purpose and Scope
This page provides a detailed walkthrough of setting up a Cloudflare Tunnel through the Cloudflare Zero Trust dashboard, obtaining the tunnel token, and configuring public hostnames to route traffic to the mosquitto MQTT broker. This configuration is required before deploying the system via docker-compose.
For information about the overall system architecture and security model, see System Architecture and Security Model. For details about configuring the mosquitto broker itself, see Mosquitto Configuration. For environment variable setup, see Environment Variables.
Cloudflare Tunnel Architecture
The following diagram illustrates how the Cloudflare Tunnel configuration integrates with the codebase:
Cloudflare Tunnel Configuration Flow
graph TB
subgraph "Cloudflare Zero Trust Dashboard"
ZT["Zero Trust Portal"]
CreateTunnel["Create Tunnel Action"]
TunnelConfig["Tunnel Configuration\nName: user-defined"]
TokenGen["Token Generator"]
HostnameConfig["Public Hostname Config\nService: HTTP\nURL: mosquitto:9001"]
end
subgraph "Local Environment"
EnvFile[".env file"]
TokenVar["CLOUDFLARE_TUNNEL_TOKEN"]
end
subgraph "docker-compose.yml"
CloudflaredService["cloudflared service"]
Command["command: tunnel --no-autoupdate run --token"]
EnvVarRef["environment:\n- CLOUDFLARE_TUNNEL_TOKEN"]
MosquittoRef["mosquitto:9001\ncontainer_name: mosquitto"]
end
subgraph "Runtime"
CloudflaredContainer["cloudflared container"]
CFNetwork["Cloudflare Edge Network"]
MosquittoContainer["mosquitto container\nport 9001"]
end
ZT --> CreateTunnel
CreateTunnel --> TunnelConfig
TunnelConfig --> TokenGen
TokenGen -->|Copy token| TokenVar
TunnelConfig --> HostnameConfig
TokenVar -->|Store in| EnvFile
EnvFile -->|Provides value to| EnvVarRef
EnvVarRef --> Command
Command -->|Runs in| CloudflaredService
CloudflaredService -->|Creates| CloudflaredContainer
HostnameConfig -->|Routes traffic to| MosquittoRef
MosquittoRef -->|Resolves to| MosquittoContainer
CloudflaredContainer -->|Authenticates with| CFNetwork
CloudflaredContainer -->|Proxies traffic to| MosquittoContainer
Sources: README.md:23-67, docker-compose.yml:11-17, .env.sample:1
Configuration Steps
Step 1: Access Cloudflare Zero Trust Dashboard
Navigate to the Cloudflare Zero Trust portal to begin tunnel configuration. This interface manages all Cloudflare Tunnel settings.
| Action | Location | Details |
|---|---|---|
| Log in | Cloudflare Dashboard | Use your Cloudflare account credentials |
| Navigate | Left sidebar | Select "Zero Trust" option |
Sources: README.md:27-29
Step 2: Create a Tunnel
The tunnel creation process generates a unique identifier and authentication token for your deployment.
| Step | Action | Configuration |
|---|---|---|
| 1 | Navigate to Networks → Tunnels | Access tunnel management interface |
| 2 | Click "Create a tunnel" | Initialize tunnel creation workflow |
| 3 | Select tunnel type | Choose "Cloudflared" connector type |
| 4 | Name the tunnel | Enter descriptive name (e.g., my_tunnel_name) |
| 5 | Save tunnel | Finalize tunnel creation |
Tunnel Creation Process
Sources: README.md:33-44
Step 3: Extract and Store the Tunnel Token
After tunnel creation, the dashboard displays a Docker command containing the CLOUDFLARE_TUNNEL_TOKEN. This token must be stored in the .env file for use by the cloudflared service.
Token Extraction and Storage
graph LR
subgraph "Cloudflare Dashboard"
DockerCmd["Docker command display\nEnvironment: Docker"]
TokenInCmd["Token embedded in command"]
end
subgraph "Local Repository"
EnvSample[".env.sample\nCLOUDFLARE_TUNNEL_TOKEN=your_token"]
EnvFile[".env\nCLOUDFLARE_TUNNEL_TOKEN=actual_token"]
end
subgraph "docker-compose.yml:16-17"
EnvDeclaration["environment:\n- CLOUDFLARE_TUNNEL_TOKEN"]
end
DockerCmd --> TokenInCmd
TokenInCmd -->|Copy token value| EnvFile
EnvSample -.->|Template for| EnvFile
EnvFile -->|Provides variable to| EnvDeclaration
| Action | File | Format |
|---|---|---|
| Copy token from dashboard | N/A | Long alphanumeric string from Docker command |
Create .env file | .env (root directory) | CLOUDFLARE_TUNNEL_TOKEN=<your_token> |
| Reference template | .env.sample:1 | Shows required format |
Important: Do not execute the Docker command displayed in the dashboard. The docker-compose.yml:11-17 configuration in this repository replaces that step by running the cloudflared container with the token from the .env file.
Sources: README.md:47-53, .env.sample:1, docker-compose.yml:16-17
Step 4: Configure Public Hostname
The public hostname configuration routes external traffic through the Cloudflare edge network to the internal mosquitto service.
Hostname Configuration Parameters
| Parameter | Value | Description |
|---|---|---|
| Public hostname | User-defined subdomain and domain | External URL for MQTT clients to connect |
| Service type | HTTP | Protocol for tunnel transport (not MQTT protocol) |
| URL | mosquitto:9001 | Internal Docker service reference |
Important Implementation Details:
- The URL
mosquitto:9001uses Docker's internal DNS resolution mosquittoresolves to the container withcontainer_name: mosquittofrom docker-compose.yml6- Port
9001corresponds to the WebSocket listener configured in mosquitto.conf - The service type
HTTPrefers to the tunnel transport protocol; MQTT over WebSocket is carried within HTTP
Public Hostname Routing Flow
graph LR
subgraph "Public Internet"
Client["MQTT Client"]
PublicHostname["public-hostname.domain.com"]
end
subgraph "Cloudflare Network"
CFEdge["Cloudflare Edge"]
TunnelRoute["Tunnel Route\nService: HTTP\nURL: mosquitto:9001"]
end
subgraph "Docker Network"
CloudflaredContainer["cloudflared container\ncontainer_name: cloudflared"]
DNSResolution["Docker DNS\nmosquitto → 172.x.x.x"]
MosquittoContainer["mosquitto container\ncontainer_name: mosquitto\nport 9001 listener"]
end
Client -->|Connect to| PublicHostname
PublicHostname -->|Resolves to| CFEdge
CFEdge -->|Routes via| TunnelRoute
TunnelRoute -->|Proxies to| CloudflaredContainer
CloudflaredContainer -->|Resolves| DNSResolution
DNSResolution -->|Forwards to| MosquittoContainer
Configuration Steps:
- Navigate to the newly created tunnel in the Zero Trust dashboard
- Click "Next" to proceed to hostname configuration
- Fill in the public hostname configuration form:
- Enter your desired subdomain and domain
- Select
HTTPas the service type - Enter
mosquitto:9001as the URL
- Click "Save hostname" to finalize
Sources: README.md:55-66, docker-compose.yml:4-9
Token Integration with docker-compose
The CLOUDFLARE_TUNNEL_TOKEN environment variable is consumed by the cloudflared service definition in docker-compose.yml:11-17:
cloudflared Service Configuration
| Configuration Element | Value | Purpose |
|---|---|---|
image | cloudflare/cloudflared:latest | Official Cloudflare tunnel client |
container_name | cloudflared | DNS-resolvable name within Docker network |
command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Runs tunnel with token from environment |
environment | - CLOUDFLARE_TUNNEL_TOKEN | Passes variable from .env to container |
restart | unless-stopped | Ensures tunnel reconnects after failures |
Environment Variable Flow
Sources: docker-compose.yml:11-17, .env.sample:1
Configuration Verification
After completing the configuration steps, verify the setup before deployment:
Pre-Deployment Checklist
| Item | Location | Verification Method |
|---|---|---|
| Tunnel created in dashboard | Cloudflare Zero Trust → Networks → Tunnels | Tunnel appears in list with "Inactive" status |
| Public hostname configured | Tunnel details → Public Hostname tab | Hostname entry shows mosquitto:9001 as URL |
.env file exists | Repository root directory | File contains CLOUDFLARE_TUNNEL_TOKEN=<token> |
| Token format valid | .env:1 | Token is long alphanumeric string (typically 150+ characters) |
docker-compose.yml unchanged | Repository root directory | File matches repository version |
Expected State Before Deployment:
- Tunnel status in Cloudflare dashboard: "Inactive" (no connector running yet)
.envfile: Contains valid token, not tracked by git (see .gitignore)- Public hostname: Configured but not yet accessible (no traffic can flow until containers start)
The next step is to deploy the system using docker compose up, covered in Deployment.
Sources: README.md:23-73, docker-compose.yml:1-18
Common Configuration Issues
| Issue | Symptom | Solution |
|---|---|---|
| Invalid token format | cloudflared container exits immediately | Verify token copied completely from dashboard, check for extra spaces or newlines |
| Public hostname not resolving | DNS lookup fails for public hostname | Check DNS propagation, verify domain is active in Cloudflare |
| Wrong service URL | cloudflared starts but clients cannot connect | Ensure URL is mosquitto:9001, not localhost:9001 or IP address |
| Service type misconfiguration | Connection errors despite tunnel active | Service type must be HTTP, not TCP or other protocols |
| Token in version control | Security warning in git status | Verify .env is listed in .gitignore never commit .env file |
Sources: docker-compose.yml:11-17, README.md:47-67