This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Docker Compose Orchestration
Relevant source files
Purpose and Scope
This document provides detailed documentation of the docker-compose.yml:1-18 file, which serves as the primary orchestration configuration for the MQTT Mosquitto Cloudflare Tunnel system. It explains how the file defines, configures, and coordinates the two core services (mosquitto and cloudflared), including their Docker images, network connectivity, volume mounts, environment variables, and lifecycle management.
For information about configuring the Mosquitto broker itself, see Mosquitto Configuration. For details about setting up the Cloudflare Tunnel token, see Environment Variables. For deployment procedures, see Deployment.
Service Composition Overview
The docker-compose.yml:1-18 file defines a minimal two-service architecture. Both services run as separate Docker containers but share a common Docker network, enabling seamless inter-container communication.
graph TB
subgraph "docker-compose.yml"
ComposeFile["version: '3.8'"]
ServicesBlock["services:]
end
subgraph Service Definitions"
MosquittoService["mosquitto"]
CloudflaredService["cloudflared"]
end
subgraph "mosquitto Service Configuration"
MosquittoImage["image: eclipse-mosquitto:latest"]
MosquittoContainer["container_name: mosquitto"]
MosquittoVolumes["volumes:\n./mosquitto.conf mapped to\n/mosquitto/config/mosquitto.conf"]
MosquittoRestart["restart: unless-stopped"]
end
subgraph "cloudflared Service Configuration"
CloudflaredImage["image: cloudflare/cloudflared:latest"]
CloudflaredContainer["container_name: cloudflared"]
CloudflaredCommand["command: tunnel --no-autoupdate run\n--token ${CLOUDFLARE_TUNNEL_TOKEN}"]
CloudflaredEnv["environment:\nCLOUDFLARE_TUNNEL_TOKEN"]
CloudflaredRestart["restart: unless-stopped"]
end
subgraph "Docker Runtime"
DockerNetwork["Default Docker Network\nAuto-created"]
MosquittoRuntime["mosquitto container\nHostname: mosquitto"]
CloudflaredRuntime["cloudflared container\nHostname: cloudflared"]
end
ComposeFile --> ServicesBlock
ServicesBlock --> MosquittoService
ServicesBlock --> CloudflaredService
MosquittoService --> MosquittoImage
MosquittoService --> MosquittoContainer
MosquittoService --> MosquittoVolumes
MosquittoService --> MosquittoRestart
CloudflaredService --> CloudflaredImage
CloudflaredService --> CloudflaredContainer
CloudflaredService --> CloudflaredCommand
CloudflaredService --> CloudflaredEnv
CloudflaredService --> CloudflaredRestart
MosquittoService -.->|Creates| MosquittoRuntime
CloudflaredService -.->|Creates| CloudflaredRuntime
MosquittoRuntime --> DockerNetwork
CloudflaredRuntime --> DockerNetwork
CloudflaredRuntime -.->|Proxies to mosquitto:9001| MosquittoRuntime
Service Topology
Sources : docker-compose.yml:1-18
Service Definitions
mosquitto Service
The mosquitto service definition docker-compose.yml:4-9 configures the Eclipse Mosquitto MQTT broker container.
| Property | Value | Purpose |
|---|---|---|
image | eclipse-mosquitto:latest | Specifies the official Eclipse Mosquitto Docker image from Docker Hub |
container_name | mosquitto | Assigns a fixed container name for predictable hostname resolution within the Docker network |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | Mounts the local configuration file into the container's expected config path |
restart | unless-stopped | Configures automatic container restart on failures or host reboots, unless manually stopped |
Configuration File Mount : The volume mapping docker-compose.yml8 ensures that the local mosquitto.conf file is available to the broker at runtime. The Mosquitto container automatically reads configuration from /mosquitto/config/mosquitto.conf on startup.
Image Version Strategy : The use of the latest tag docker-compose.yml5 means the system will pull the most recent stable Mosquitto release. For production deployments, consider pinning to a specific version tag (e.g., eclipse-mosquitto:2.0.18).
Sources : docker-compose.yml:4-9
cloudflared Service
The cloudflared service definition docker-compose.yml:11-18 configures the Cloudflare Tunnel client container.
| Property | Value | Purpose |
|---|---|---|
image | cloudflare/cloudflared:latest | Specifies the official Cloudflare Tunnel client Docker image |
container_name | cloudflared | Assigns a fixed container name for network identification |
command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Overrides the default container command to execute the tunnel client with authentication |
environment | CLOUDFLARE_TUNNEL_TOKEN | Declares the environment variable that Docker Compose should inject from the .env file |
restart | unless-stopped | Configures automatic container restart behavior |
Command Structure : The command directive docker-compose.yml14 breaks down as follows:
tunnel # cloudflared subcommand
--no-autoupdate # Prevents automatic updates that could disrupt service
run # Executes the tunnel in persistent mode
--token ${CLOUDFLARE_TUNNEL_TOKEN} # Authenticates using the token from environment variable
Environment Variable Injection : The environment section docker-compose.yml:16-17 instructs Docker Compose to read CLOUDFLARE_TUNNEL_TOKEN from the .env file (not committed to version control) and inject it into the container's environment. The variable is then referenced in the command using standard shell variable syntax.
Sources : docker-compose.yml:11-18
graph LR
subgraph "Docker Host"
subgraph "Default Compose Network"
MosquittoContainer["mosquitto\ncontainer_name: mosquitto\nAccessible as: mosquitto"]
CloudflaredContainer["cloudflared\ncontainer_name: cloudflared\nAccessible as: cloudflared"]
end
HostInterface["Host Network Interface\nNot directly accessible\nfrom containers"]
end
subgraph "External Network"
CloudflareEdge["Cloudflare Edge Network"]
end
CloudflaredContainer -->|DNS resolution: mosquitto resolves to mosquitto container IP| MosquittoContainer
CloudflaredContainer -->|Outbound connection to Cloudflare| CloudflareEdge
MosquittoContainer -.->|No direct external connectivity| HostInterface
Docker Compose Networking
Docker Compose automatically creates a default bridge network for all services defined in the same compose file. This network enables service-to-service communication using container names as hostnames.
Hostname Resolution : Because the mosquitto service has container_name: mosquitto docker-compose.yml6 the container is accessible within the Docker network using the hostname mosquitto. The Cloudflare Tunnel configuration (configured through the Cloudflare Dashboard) references mosquitto:9001 as the backend service URL, which Docker resolves to the mosquitto container's internal IP address.
Network Isolation : Neither container exposes ports to the host network. All communication flows through:
- External → cloudflared : Inbound traffic arrives via the Cloudflare Tunnel (outbound-initiated connection)
- cloudflared → mosquitto : Internal Docker network communication on port 9001
- mosquitto → External : Not applicable; the broker only receives proxied traffic
No Explicit Network Declaration : The compose file does not include a networks: section, so Docker Compose uses its default behavior of creating a network named <project_name>_default (where <project_name> is typically the directory name).
Sources : docker-compose.yml:1-18
graph LR
subgraph "Docker Host Filesystem"
HostMosquittoConf["./mosquitto.conf\nLine: listener 1883\nLine: listener 9001\nLine: protocol websockets\nLine: allow_anonymous true"]
end
subgraph "mosquitto Container"
ContainerPath["/mosquitto/config/mosquitto.conf"]
MosquittoProcess["mosquitto process\nReads config on startup"]
end
HostMosquittoConf -.->|Volume mount Read-only access| ContainerPath
ContainerPath --> MosquittoProcess
Volume Mounts
The system uses a single volume mount to inject the Mosquitto configuration into the broker container.
Mount Specification : The volume directive docker-compose.yml:7-8 uses the syntax:
This creates a bind mount where:
- Host path :
./mosquitto.conf(relative to the directory containingdocker-compose.yml) - Container path :
/mosquitto/config/mosquitto.conf(the default configuration location for Eclipse Mosquitto) - Mount type : Bind mount (inferred from the
./prefix) - Read/Write mode : Read-only (implicit default for configuration files)
Configuration Reloading : Mosquitto does not automatically reload configuration changes. To apply modifications to mosquitto.conf:
- Edit the file on the host filesystem
- Restart the mosquitto container:
docker compose restart mosquitto
No Data Persistence : The configuration does not define any volumes for persisting MQTT retained messages or other runtime data. If data persistence is required, additional volume mounts should be configured for /mosquitto/data.
Sources : docker-compose.yml:7-8
Restart Policies
Both services use the unless-stopped restart policy docker-compose.yml:9-15 which ensures high availability and automatic recovery from failures.
Restart Policy Behavior Matrix
| Scenario | Container Behavior | Rationale |
|---|---|---|
| Container crashes | Automatically restarts | Recovers from application failures |
| Docker daemon restarts | Automatically restarts | Maintains service availability after host reboot |
Manual docker stop | Does not restart | Respects operator intent to stop service |
Manual docker compose down | Does not restart | Stops all services as expected |
| Host system reboot | Automatically restarts (if Docker is configured to start on boot) | Resumes services after maintenance |
Alternative Restart Policies
The unless-stopped policy is appropriate for production services. Other available policies include:
no: Never automatically restart (requires manual intervention for failures)always: Restart even after manual stop (aggressive auto-recovery)on-failure: Restart only on non-zero exit codes (useful for debugging)
Configuration Example : To change the restart policy, modify docker-compose.yml9 or docker-compose.yml15:
Sources : docker-compose.yml:9-15
sequenceDiagram
participant User
participant DockerCompose as "docker compose"
participant DockerEngine as "Docker Engine"
participant EnvFile as ".env File"
participant MosquittoConf as "mosquitto.conf File"
participant MosquittoContainer as "mosquitto Container"
participant CloudflaredContainer as "cloudflared Container"
User->>DockerCompose: docker compose up
DockerCompose->>EnvFile: Read environment variables
EnvFile-->>DockerCompose: CLOUDFLARE_TUNNEL_TOKEN=xxx
DockerCompose->>DockerEngine: Create network (if not exists)
DockerEngine-->>DockerCompose: Network ready
DockerCompose->>DockerEngine: Pull eclipse-mosquitto:latest
DockerEngine-->>DockerCompose: Image ready
DockerCompose->>DockerEngine: Pull cloudflare/cloudflared:latest
DockerEngine-->>DockerCompose: Image ready
DockerCompose->>DockerEngine: Create mosquitto container
Note over DockerEngine,MosquittoConf: Volume mount ./mosquitto.conf
DockerEngine->>MosquittoContainer: Start with mounted config
MosquittoContainer->>MosquittoConf: Read configuration
MosquittoContainer-->>DockerEngine: Listening on ports 1883, 9001
DockerCompose->>DockerEngine: Create cloudflared container
Note over DockerEngine,CloudflaredContainer: Inject CLOUDFLARE_TUNNEL_TOKEN
DockerEngine->>CloudflaredContainer: Start with command:\ntunnel --no-autoupdate run --token
CloudflaredContainer-->>DockerEngine: Tunnel established
CloudflaredContainer->>MosquittoContainer: Verify mosquitto:9001 reachable
MosquittoContainer-->>CloudflaredContainer: Connection successful
DockerCompose-->>User: Services running
Service Orchestration Flow
The following diagram illustrates the complete lifecycle from docker compose up to running services.
Startup Order : Docker Compose does not guarantee a specific startup order between the two services. However, this is acceptable because:
- The
mosquittoservice can start independently - The
cloudflaredservice will retry connections tomosquitto:9001if the broker is not yet ready
Dependency Management : For strict startup ordering, Docker Compose supports depends_on directives. This system does not use them because the cloudflared tunnel client includes built-in retry logic.
Sources : docker-compose.yml:1-18
Command-Line Operations
Common Docker Compose commands for managing the service stack:
Graceful Shutdown : The docker compose down command sends SIGTERM to both containers, allowing them to shut down gracefully before forcing termination.
Sources : docker-compose.yml:1-18
Service Dependency Graph
External Dependencies : The system requires:
- Docker Engine (to run containers)
- Active internet connection (for cloudflared to reach Cloudflare's network)
- Valid Cloudflare Tunnel token (from Cloudflare Zero Trust dashboard)
Configuration Dependencies :
mosquittoservice depends on the existence of./mosquitto.confdocker-compose.yml8cloudflaredservice depends onCLOUDFLARE_TUNNEL_TOKENbeing set in.envdocker-compose.yml:16-17
Sources : docker-compose.yml:1-18
Version Specification
The compose file uses version 3.8 docker-compose.yml1 which corresponds to Docker Compose file format 3.8, compatible with Docker Engine 19.03.0+.
Version 3.8 Features :
- Support for
initflag (not used in this configuration) - Enhanced credential helper support (not used in this configuration)
- Improved service dependency controls (not used in this configuration)
Backward Compatibility : The configuration uses only basic features that are compatible with earlier 3.x versions. The version could be lowered to 3.0 without affecting functionality.
Docker Compose V2 : This configuration is compatible with both Docker Compose V1 (docker-compose) and Docker Compose V2 (docker compose). The modern V2 syntax uses docker compose (space, not hyphen).
Sources : docker-compose.yml1