This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Cloudflared Tunnel Service
Relevant source files
Purpose and Scope
This document provides in-depth technical documentation of the cloudflared service, which establishes a secure, outbound-only tunnel connection between the Docker host environment and Cloudflare's edge network. It covers the Docker container configuration, tunnel authentication mechanism, traffic routing behavior, and operational characteristics of the tunnel client.
For information about configuring the Cloudflare Tunnel through the Zero Trust dashboard and obtaining the tunnel token, see Cloudflare Tunnel Configuration. For information about the Mosquitto service that cloudflared routes traffic to, see Mosquitto MQTT Broker.
Service Overview
The cloudflared service runs the official Cloudflare Tunnel client (cloudflare/cloudflared:latest) as a Docker container. Its primary responsibility is to establish and maintain an encrypted, outbound-only tunnel connection to Cloudflare's global edge network. This tunnel enables external MQTT clients to reach the internal mosquitto service without requiring inbound firewall rules or exposing ports directly to the internet.
The service operates as a reverse proxy within the Docker Compose stack, forwarding all traffic received from the Cloudflare edge through the tunnel to the mosquitto container's WebSocket listener on port 9001.
Sources: docker-compose.yml:11-17 README.md:15-20
Docker Container Configuration
Service Definition
The cloudflared service is defined in docker-compose.yml:11-17 with the following configuration:
| Configuration Aspect | Value | Purpose |
|---|---|---|
| Service Name | cloudflared | Identifier used by Docker Compose |
| Image | cloudflare/cloudflared:latest | Official Cloudflare Tunnel client image |
| Container Name | cloudflared | Hostname accessible within Docker network |
| Command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Starts tunnel with authentication token |
| Restart Policy | unless-stopped | Automatically restarts on failure or reboot |
| Environment Variables | CLOUDFLARE_TUNNEL_TOKEN | Tunnel authentication credential |
Command Line Arguments
The container executes with three key command line arguments:
tunnel- Invokes the tunnel subcommand of cloudflared--no-autoupdate- Prevents automatic updates of the cloudflared binary (recommended for containerized deployments where the Docker image should control versioning)run --token ${CLOUDFLARE_TUNNEL_TOKEN}- Runs the tunnel using token-based authentication
Sources: docker-compose.yml14
Container Configuration Diagram
Diagram: cloudflared Service Configuration Flow
This diagram illustrates how the service definition in docker-compose.yml translates to a running container, with the authentication token sourced from the .env file.
Sources: docker-compose.yml:11-17 .env.sample1
Tunnel Authentication Mechanism
Authentication Token
The cloudflared service authenticates with Cloudflare's edge network using CLOUDFLARE_TUNNEL_TOKEN, an opaque authentication credential generated by the Cloudflare Zero Trust dashboard. This token:
- Uniquely identifies the tunnel - Each tunnel has a unique token that maps to a specific tunnel configuration in Cloudflare's system
- Contains embedded authentication - The token includes all necessary authentication information; no username/password is required
- Associates with routing rules - The token links to public hostname configurations that determine traffic routing
The token is provided to the container via the CLOUDFLARE_TUNNEL_TOKEN environment variable, which Docker Compose populates from the .env file docker-compose.yml17
Token Security
The token grants full control over the tunnel and must be protected:
- Never committed to version control - The
.envfile is excluded via.gitignore - Stored only in local environment - Each deployment maintains its own
.envfile - Template provided - .env.sample1 shows the required format without exposing actual credentials
Sources: docker-compose.yml:14-17 .env.sample1 README.md51
Tunnel Establishment Process
sequenceDiagram
participant Compose as "docker-compose"
participant Container as "cloudflared Container"
participant Token as "CLOUDFLARE_TUNNEL_TOKEN"
participant CFEdge as "Cloudflare Edge Network"
participant CFControl as "Cloudflare Control Plane"
Compose->>Container: Start container with command:\ntunnel --no-autoupdate run --token
Container->>Token: Read environment variable
Token-->>Container: Return token value
Container->>CFControl: Authenticate with token
CFControl->>CFControl: Validate token\nRetrieve tunnel configuration
CFControl-->>Container: Authentication successful\nRouting rules provided
Container->>CFEdge: Establish outbound encrypted connection\n(typically TLS over TCP)
CFEdge-->>Container: Connection established
Container->>Container: Enter ready state\nListening for tunnel traffic
Note over Container,CFEdge: Tunnel is now active and ready to route traffic
Startup Sequence
When the cloudflared container starts, it follows this sequence to establish the tunnel:
Diagram: Cloudflared Tunnel Establishment Sequence
This diagram shows the complete startup sequence from container launch to tunnel readiness.
Sources: docker-compose.yml14 README.md:47-54
Connection Characteristics
The tunnel connection exhibits these characteristics:
| Characteristic | Behavior |
|---|---|
| Direction | Outbound-only from cloudflared to Cloudflare edge |
| Protocol | Encrypted (TLS) over TCP |
| Persistence | Long-lived connection, automatically reconnects if dropped |
| Firewall Requirements | None - no inbound ports need to be opened |
| Restart Behavior | Container restarts automatically (unless-stopped policy) if tunnel fails |
Sources: docker-compose.yml15
Traffic Routing and Proxying
Routing Configuration
The cloudflared service acts as a reverse proxy, forwarding traffic from Cloudflare's edge to the internal mosquitto service. The routing configuration is established through the Cloudflare Zero Trust dashboard, not in the Docker configuration:
- Public hostname - Configured in Cloudflare dashboard (e.g.,
mqtt.example.com) - Service type - Set to HTTP in the dashboard
- Target URL - Set to
mosquitto:9001README.md62
graph LR
subgraph "External"
Client["MQTT Client"]
CFEdge["Cloudflare Edge\n(Public IP)"]
end
subgraph "Docker Host"
subgraph "Docker Internal Network"
CFContainer["cloudflared Container"]
MosqContainer["mosquitto Container\n(container_name: mosquitto)"]
end
end
subgraph "Cloudflare Dashboard Config"
PublicHost["Public Hostname:\nmqtt.example.com"]
ServiceURL["Service URL:\nmosquitto:9001"]
end
Client -->|MQTT over Internet| CFEdge
CFEdge -->|Encrypted Tunnel| CFContainer
CFContainer -->|HTTP Proxy to mosquitto:9001| MosqContainer
PublicHost -.->|Maps to| CFEdge
ServiceURL -.->|Resolves via Docker DNS| MosqContainer
The hostname mosquitto in the target URL resolves to the mosquitto container via Docker's internal DNS, using the container_name field from the mosquitto service definition.
Traffic Flow
Diagram: Cloudflared Traffic Routing Architecture
This diagram illustrates the complete traffic path from external clients through the tunnel to the mosquitto service, showing how the mosquitto:9001 service URL resolves within Docker's network.
Sources: README.md:59-66 docker-compose.yml:6-13
Network Behavior
Internal Docker Networking
The cloudflared service participates in Docker Compose's default bridge network alongside the mosquitto service. This enables:
- DNS-based service discovery - The hostname
mosquittoautomatically resolves to the IP address of the mosquitto container - Direct container-to-container communication - Traffic between cloudflared and mosquitto stays within the Docker network
- No port exposure required - Neither service exposes ports to the host machine
No Inbound Port Requirements
A key architectural benefit of the cloudflared tunnel is that it requires no inbound firewall rules. The tunnel connection is established outbound from the cloudflared container to Cloudflare's edge network. All traffic flows through this pre-established tunnel, eliminating the need to expose any ports on the Docker host to the public internet.
Sources: docker-compose.yml:1-17 README.md:15-20
Operational Characteristics
Automatic Restart Behavior
The service is configured with restart: unless-stopped docker-compose.yml15 which means:
| Scenario | Behavior |
|---|---|
| Container crashes | Automatically restarts |
| Docker daemon restarts | Container starts automatically |
| System reboot | Container starts automatically |
| Manual stop | Container remains stopped until manually started |
This restart policy ensures high availability of the tunnel connection without manual intervention.
Update Management
The --no-autoupdate flag docker-compose.yml14 prevents cloudflared from automatically updating itself. This is recommended for containerized deployments because:
- Version control - Updates are managed by changing the Docker image tag, not by in-container updates
- Predictability - The deployed version matches the image version
- Consistency - All instances run the same version of cloudflared
To update cloudflared, pull a newer version of the cloudflare/cloudflared image and recreate the container.
Sources: docker-compose.yml:14-15
Authentication and Token Management Diagram
Diagram: Token Lifecycle and Security
This diagram traces the complete lifecycle of the tunnel authentication token, from generation in the Cloudflare dashboard to consumption by the cloudflared container, highlighting the security boundaries.
Sources: docker-compose.yml:16-17 .env.sample1 README.md:47-51
Integration with Mosquitto Service
Service Dependency
While the docker-compose.yml file does not explicitly define a depends_on relationship, the cloudflared service functionally depends on the mosquitto service being available:
- Routing target - The tunnel routes traffic to
mosquitto:9001 - Service availability - If mosquitto is not running, routed traffic will fail
- Docker DNS resolution - The hostname
mosquittomust resolve for proxying to work
The lack of an explicit dependency is acceptable because:
- Both services have
restart: unless-stoppedpolicies, ensuring they both remain running - Cloudflared will queue or retry connections if mosquitto is temporarily unavailable
- The tunnel itself remains active even if the backend service is down
Port Mapping
The cloudflared service routes traffic specifically to port 9001 on the mosquitto container README.md62 This port corresponds to mosquitto's WebSocket listener configuration. The choice of port 9001 rather than 1883 (standard MQTT) is significant because:
- Cloudflare Tunnel works with HTTP/WebSocket protocols
- MQTT over WebSockets is supported on port 9001
- Standard MQTT (port 1883) uses raw TCP, which requires WebSocket encapsulation when tunneled
Sources: README.md62 docker-compose.yml:4-9
Operational Monitoring
Container Status
The health and status of the cloudflared service can be monitored using standard Docker commands:
Expected Log Output
When functioning correctly, the cloudflared container logs will show:
- Successful authentication with Cloudflare
- Tunnel registration confirmation
- Active connection status
- Traffic routing information
Common Operational States
| State | Indication | Meaning |
|---|---|---|
| Running | Container status shows "Up" | Tunnel is active and routing traffic |
| Restarting | Container repeatedly starts and stops | Authentication failure or network issue |
| Exited | Container stopped with exit code | Configuration error or manual stop |
Sources: docker-compose.yml:11-17
Summary
The cloudflared service provides secure, managed tunnel connectivity from Cloudflare's global edge network to the internal mosquitto MQTT broker. Key characteristics include:
- Container:
cloudflare/cloudflared:latestrunning withcontainer_name: cloudflared - Authentication: Token-based via
CLOUDFLARE_TUNNEL_TOKENenvironment variable - Command:
tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} - Restart Policy:
unless-stoppedfor high availability - Network Role: Reverse proxy forwarding traffic to
mosquitto:9001 - Security Model: Outbound-only connection, no inbound firewall rules required
- Configuration Management: Tunnel routing configured in Cloudflare dashboard, not in Docker files
Sources: docker-compose.yml:11-17 README.md:15-73 .env.sample1