Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

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.

PropertyValuePurpose
imageeclipse-mosquitto:latestSpecifies the official Eclipse Mosquitto Docker image from Docker Hub
container_namemosquittoAssigns a fixed container name for predictable hostname resolution within the Docker network
volumes./mosquitto.conf:/mosquitto/config/mosquitto.confMounts the local configuration file into the container's expected config path
restartunless-stoppedConfigures 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.

PropertyValuePurpose
imagecloudflare/cloudflared:latestSpecifies the official Cloudflare Tunnel client Docker image
container_namecloudflaredAssigns a fixed container name for network identification
commandtunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}Overrides the default container command to execute the tunnel client with authentication
environmentCLOUDFLARE_TUNNEL_TOKENDeclares the environment variable that Docker Compose should inject from the .env file
restartunless-stoppedConfigures 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:

  1. External → cloudflared : Inbound traffic arrives via the Cloudflare Tunnel (outbound-initiated connection)
  2. cloudflared → mosquitto : Internal Docker network communication on port 9001
  3. 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 containing docker-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:

  1. Edit the file on the host filesystem
  2. 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

ScenarioContainer BehaviorRationale
Container crashesAutomatically restartsRecovers from application failures
Docker daemon restartsAutomatically restartsMaintains service availability after host reboot
Manual docker stopDoes not restartRespects operator intent to stop service
Manual docker compose downDoes not restartStops all services as expected
Host system rebootAutomatically 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 mosquitto service can start independently
  • The cloudflared service will retry connections to mosquitto:9001 if 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:

  1. Docker Engine (to run containers)
  2. Active internet connection (for cloudflared to reach Cloudflare's network)
  3. Valid Cloudflare Tunnel token (from Cloudflare Zero Trust dashboard)

Configuration Dependencies :

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 init flag (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