This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Overview
Loading…
Overview
Relevant source files
Purpose and Scope
This document provides a comprehensive introduction to the Docker MQTT Mosquitto with Cloudflare Tunnel system, explaining its architecture, components, and operational model. This system deploys a containerized Eclipse Mosquitto MQTT broker and securely exposes it to the internet via Cloudflare Tunnel, eliminating the need for publicly exposed ports or inbound firewall rules.
For detailed architectural diagrams and component interactions, see System Architecture. For security principles and Zero Trust implementation details, see Security Model. For step-by-step deployment instructions, see Getting Started.
Sources: README.md:1-23
What is This System?
The Docker MQTT Mosquitto with Cloudflare Tunnel system is a production-ready deployment pattern that combines:
- Eclipse Mosquitto : An open-source MQTT message broker that facilitates publish/subscribe messaging between IoT devices and applications
- Cloudflare Tunnel : A secure tunneling service that exposes local services to the internet without opening inbound ports
- Docker Compose : Container orchestration for reproducible, multi-container deployments
The system implements a Zero Trust security model where the MQTT broker has no internet-facing attack surface. All external traffic is routed through Cloudflare’s global edge network, which provides DDoS protection, SSL termination, and geographic distribution.
Sources: README.md:17-23
Key Features
The system provides the following capabilities:
| Feature | Description | Implementation |
|---|---|---|
| Zero Trust Access | No publicly exposed inbound ports on origin server | Cloudflare Tunnel with outbound-only connections |
| Protocol Support | Native MQTT and WebSocket protocols | Mosquitto listeners on ports 1883 and 9001 |
| Container Orchestration | Reproducible multi-container deployment | Docker Compose with service definitions |
| Automated Testing | Service health verification on every commit | GitHub Actions CI workflow |
| Documentation | Auto-generated and published documentation | GitHub Actions with DeepWiki integration |
| Secret Management | Secure credential handling excluded from version control | .env file with .gitignore protection |
| TLS Encryption | End-to-end encrypted connections | Cloudflare edge network SSL termination |
| Global Distribution | Low-latency access from any geographic region | Cloudflare’s global edge network |
Sources: README.md:1-23 .github/workflows/ci.yml:1-41 .github/workflows/build-docs.yml:1-84
Core Components
The system consists of three primary components orchestrated by Docker Compose:
Component Summary
| Component | Container Name | Purpose | Configuration |
|---|---|---|---|
| Mosquitto Broker | mosquitto | MQTT message broker handling pub/sub messaging | mosquitto.conf:1-5 volume mount |
| Cloudflare Connector | cloudflared | Tunnel client establishing secure outbound connection | Environment variable CLOUDFLARE_TUNNEL_TOKEN |
| Docker Compose | N/A | Orchestration layer managing container lifecycle | docker-compose.yml:1-19 |
Supporting Configuration Files
| File | Purpose | Version Control Status |
|---|---|---|
mosquitto.conf | Defines MQTT listener ports and protocols | Committed to repository |
.env | Contains CLOUDFLARE_TUNNEL_TOKEN secret | Excluded via .gitignore |
.env.sample | Template showing required environment variables | Committed to repository |
.gitignore | Prevents secret leakage to version control | Committed to repository |
Sources: docker-compose.yml:1-19 mosquitto.conf:1-5 .env.sample1 .gitignore:1-2
graph TB
subgraph Internet["Internet / Public Network"]
Client["External MQTT Clients\n(IoT Devices, Applications)"]
end
subgraph CF["Cloudflare Network"]
Edge["Cloudflare Edge\nTLS Termination\nPort 443"]
Tunnel["Cloudflare Tunnel Service"]
end
subgraph DockerHost["Docker Host"]
Compose["docker-compose.yml\nServices Orchestration"]
subgraph Network["Docker Network (Bridge)"]
MosqContainer["Container: mosquitto\neclipse-mosquitto:latest\ncontainer_name: mosquitto"]
CFDContainer["Container: cloudflared\ncloudflare/cloudflared:latest\ncontainer_name: cloudflared"]
end
MosqConf["mosquitto.conf\nVolume Mount\n/mosquitto/config/mosquitto.conf"]
EnvFile[".env\nCLOUDFLARE_TUNNEL_TOKEN"]
end
Client -->|HTTPS/WSS Port 443| Edge
Edge -->|Secure Tunnel| Tunnel
Tunnel <-->|Outbound Connection Token Authentication| CFDContainer
Compose -->|manages| MosqContainer
Compose -->|manages| CFDContainer
MosqConf -->|volumes: ./mosquitto.conf| MosqContainer
EnvFile -->|environment: CLOUDFLARE_TUNNEL_TOKEN| CFDContainer
CFDContainer -->|HTTP to mosquitto:9001| MosqContainer
MosqContainer -->|listener 1883 protocol mqtt| ListenerMQTT["MQTT Protocol\nInternal Port 1883"]
MosqContainer -->|listener 9001 protocol websockets| ListenerWS["WebSocket Protocol\nInternal Port 9001"]
System Topology
The following diagram illustrates the complete system architecture, mapping high-level concepts to specific code entities and configuration elements:
Container and Network Topology
Key Code Entities:
- Service Name :
mosquittoin docker-compose.yml4 - used for Docker internal DNS resolution - Container Name :
mosquittoin docker-compose.yml5 - named container instance - Container Name :
cloudflaredin docker-compose.yml12 - tunnel connector instance - Volume Mount :
./mosquitto.conf:/mosquitto/config/mosquitto.confin docker-compose.yml:8-9 - Environment Variable :
CLOUDFLARE_TUNNEL_TOKENreferenced in docker-compose.yml:16-17 - Listener Directives :
listener 1883andlistener 9001in mosquitto.conf:1-5 - Command :
tunnel runin docker-compose.yml13 - starts tunnel connector with token authentication
Sources: docker-compose.yml:1-19 mosquitto.conf:1-5 README.md:64-67
sequenceDiagram
participant Client as "External Client\n(MQTT/WebSocket)"
participant CFEdge as "Cloudflare Edge\nPort 443 HTTPS"
participant CFTunnel as "Cloudflare Tunnel\nInfrastructure"
participant CloudflaredContainer as "Container: cloudflared\nCommand: tunnel run"
participant DockerDNS as "Docker Internal DNS\nmosquitto:9001"
participant MosquittoContainer as "Container: mosquitto\nPort 9001 Listener"
Note over Client,MosquittoContainer: Initial Connection Phase
CloudflaredContainer->>CFTunnel: Establish tunnel using\nCLOUDFLARE_TUNNEL_TOKEN
CFTunnel-->>CloudflaredContainer: Tunnel authenticated and ready
Note over Client,MosquittoContainer: Client CONNECT Request
Client->>CFEdge: CONNECT to https://subdomain.domain:443
CFEdge->>CFTunnel: Route via tunnel config\nPublic Hostname mapping
CFTunnel->>CloudflaredContainer: Forward through secure tunnel
CloudflaredContainer->>DockerDNS: Resolve "mosquitto:9001"
DockerDNS-->>CloudflaredContainer: Return container IP
CloudflaredContainer->>MosquittoContainer: HTTP request to port 9001\nprotocol websockets
MosquittoContainer-->>CloudflaredContainer: WebSocket upgrade response
CloudflaredContainer-->>CFTunnel: Return response
CFTunnel-->>CFEdge: Return response
CFEdge-->>Client: HTTPS/WSS connection established
Note over Client,MosquittoContainer: Client PUBLISH Message
Client->>CFEdge: PUBLISH to topic
CFEdge->>CFTunnel: Forward
CFTunnel->>CloudflaredContainer: Via tunnel
CloudflaredContainer->>MosquittoContainer: HTTP POST to mosquitto:9001
MosquittoContainer->>MosquittoContainer: Process message\nRoute to subscribers
MosquittoContainer-->>CloudflaredContainer: PUBACK
CloudflaredContainer-->>CFTunnel: Return
CFTunnel-->>CFEdge: Return
CFEdge-->>Client: PUBACK confirmation
Request Flow Architecture
This diagram traces a complete MQTT message flow from external client to broker, mapping each step to specific code entities and configuration elements:
End-to-End Message Flow
Key Code References in Flow:
- Tunnel Authentication :
CLOUDFLARE_TUNNEL_TOKENfrom docker-compose.yml:16-17 passed tocloudflaredcontainer - Tunnel Command :
tunnel runin docker-compose.yml13 establishes outbound connection - DNS Resolution : Service name
mosquittodefined in docker-compose.yml4 resolved by Docker’s internal DNS - Port Configuration :
9001listener defined in mosquitto.conf:4-5 handles WebSocket connections - Protocol Setting :
protocol websocketsin mosquitto.conf5 enables WebSocket support - Public Hostname : Configured in Cloudflare dashboard (Step 3 of README.md:57-72) maps public URL to internal
mosquitto:9001
Sources: docker-compose.yml:1-19 mosquitto.conf:1-5 README.md:57-72
Deployment Model
The system uses a two-phase deployment model that separates cloud configuration from local execution:
Phase 1: Cloud Configuration (One-Time Setup)
| Step | Action | Outcome |
|---|---|---|
| 1 | Create Cloudflare Tunnel via Zero Trust dashboard | Generates unique tunnel identifier |
| 2 | Configure tunnel connector type (Cloudflared) | Provides Docker deployment command |
| 3 | Copy CLOUDFLARE_TUNNEL_TOKEN from generated command | Token for authenticating local connector |
| 4 | Configure Public Hostname mapping | Maps public URL to mosquitto:9001 internal address |
Sources: README.md:29-72
Phase 2: Local Deployment (Repeatable)
| Step | Command/Action | File Reference |
|---|---|---|
| 1 | Create .env file from .env.sample template | .env.sample1 |
| 2 | Add CLOUDFLARE_TUNNEL_TOKEN=<token> to .env | docker-compose.yml:16-17 |
| 3 | Verify .env excluded from Git | .gitignore1 |
| 4 | Run docker compose up | docker-compose.yml1 |
Sources: README.md:53-78 .env.sample1 .gitignore:1-2
Security Architecture
The system implements defense-in-depth security through multiple layers:
Security Layers
Key Security Implementations:
- No Inbound Ports : docker-compose.yml:1-19 defines no
ports:sections, preventing host exposure - Outbound-Only Connection :
tunnel runcommand in docker-compose.yml13 establishes connection from origin to Cloudflare - Secret Exclusion :
.envpattern in .gitignore1 prevents accidental token commits - Anonymous Access : Currently configured as
allow_anonymous truein mosquitto.conf3 - see Authentication and Access Control for implications
For comprehensive security documentation, see Security Model.
Sources: docker-compose.yml:1-19 mosquitto.conf:1-5 .gitignore:1-2
Advanced Features
The repository includes an alternative branch with additional security features:
Protected Branch Features
The protected-no-wildcard branch implements:
| Feature | Implementation | Documentation |
|---|---|---|
| Wildcard Topic Restrictions | ACL file restricting cross-user wildcard subscriptions | README.md9 |
| Encrypted Retained Messages | gocryptfs filesystem encryption | README.md10 |
| Auto-Save Persistence | Automatic message persistence after every publish | README.md11 |
See Protected Branch Features for detailed documentation.
Sources: README.md:7-13
CI/CD Integration
The system includes automated quality assurance through GitHub Actions:
Continuous Integration Workflow
The .github/workflows/ci.yml:1-41 workflow performs:
- Service Provisioning : Starts
mosquittocontainer viadocker-compose up -d mosquitto - Health Check Loop : Polls container status up to 10 times with 10-second intervals
- Verification : Ensures
docker ps | grep mosquitto | grep "Up"succeeds - Cleanup : Executes
docker-compose downregardless of test outcome
Documentation Pipeline
The .github/workflows/build-docs.yml:1-84 workflow:
- Generates documentation using DeepWiki integration
- Builds mdBook static site
- Deploys to GitHub Pages on weekly schedule or manual trigger
For complete CI/CD documentation, see CI/CD and Automation.
Sources: .github/workflows/ci.yml:1-41 .github/workflows/build-docs.yml:1-84
Quick Start Reference
To deploy this system:
- Prerequisites : Docker, Docker Compose, Cloudflare account (see Prerequisites)
- Cloudflare Setup : Create tunnel and obtain token (see Cloudflare Tunnel Setup)
- Local Configuration : Create
.envfile with token (see Local Configuration) - Deployment : Run
docker compose up(see Deployment)
Your MQTT broker will be accessible at https://<subdomain>.<domain>:443 as configured in the Cloudflare Public Hostname settings.
Important : The URL mosquitto:9001 referenced in README.md64 is internal to Docker and resolved via the service name in docker-compose.yml4 The public URL is configured separately in the Cloudflare dashboard and uses HTTPS on port 443, not HTTP on port 9001.
For complete deployment instructions, see Getting Started.
Sources: README.md:25-84 docker-compose.yml:1-19
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Getting Started
Loading…
Getting Started
Relevant source files
This page guides you through the complete deployment process for the Docker MQTT Mosquitto Cloudflare Tunnel system. The setup consists of four phases: verifying prerequisites, configuring Cloudflare Tunnel, setting up local configuration files, and deploying containers.
Related Pages:
- Prerequisites (page 2.1): System requirements and account setup
- Cloudflare Tunnel Setup (page 2.2): Detailed tunnel creation steps
- Local Configuration (page 2.3): Environment and configuration file setup
- Deployment (page 2.4): Container deployment and verification
Deployment Workflow
The deployment process follows a linear sequence of configuration and initialization steps:
Diagram: Deployment Phase Sequence
stateDiagram-v2
[*] --> Prerequisites
Prerequisites --> CloudflareTunnel : Accounts verified
CloudflareTunnel --> LocalConfig : CLOUDFLARE_TUNNEL_TOKEN obtained
LocalConfig --> Deployment : .env file created
Deployment --> [*] : Containers running
state CloudflareTunnel {
[*] --> CreateTunnel
CreateTunnel --> ConfigureHostname
ConfigureHostname --> [*]
}
state LocalConfig {
[*] --> CreateEnvFile
CreateEnvFile --> PopulateToken
PopulateToken --> [*]
}
state Deployment {
[*] --> DockerComposeUp
DockerComposeUp --> VerifyContainers
VerifyContainers --> [*]
}
Sources : README.md:29-78
Deployment Phases Summary
| Phase | Key Actions | Output Artifacts | Page Reference |
|---|---|---|---|
| Prerequisites | Install Docker, Docker Compose; create Cloudflare account | System readiness | Page 2.1 |
| Cloudflare Tunnel Setup | Create tunnel in Zero Trust dashboard; configure public hostname | CLOUDFLARE_TUNNEL_TOKEN | Page 2.2 |
| Local Configuration | Create .env file; populate token; verify mosquitto.conf | .env file | Page 2.3 |
| Deployment | Execute docker compose up; verify container status | Running mosquitto and cloudflared containers | Page 2.4 |
Sources : README.md:29-78 docker-compose.yml:1-18
Configuration File Flow
Diagram: File-to-Container Mapping
Sources : docker-compose.yml:1-18 .env.sample:1-3 .gitignore:1-2
Configuration File Purposes
| File | Version Controlled | Purpose | Used By |
|---|---|---|---|
docker-compose.yml | Yes | Defines service orchestration, container names, image references, volume mounts, environment variable injection | Docker Compose |
mosquitto.conf | Yes | Configures MQTT broker listeners on ports 1883 and 9001 | mosquitto container |
.env.sample | Yes | Template showing required environment variables | Developers (documentation) |
.env | No | Contains actual CLOUDFLARE_TUNNEL_TOKEN value | cloudflared container |
.gitignore | Yes | Prevents .env from being committed | Git |
Sources : docker-compose.yml:1-18 mosquitto.conf:1-5 .env.sample:1-3 .gitignore:1-2
Quick Start Summary
This section provides an abbreviated setup sequence. For detailed instructions, see the child pages (2.1 through 2.4).
Step 1: Cloudflare Tunnel Configuration
Create a tunnel in the Cloudflare Zero Trust dashboard and obtain the CLOUDFLARE_TUNNEL_TOKEN. Configure a public hostname that routes to mosquitto:9001.
Detailed instructions : See page 2.2 (Cloudflare Tunnel Setup)
Key outputs :
- Tunnel token string (begins with
eyJ...) - Public hostname (e.g.,
mqtt.example.com)
Sources : README.md:29-73
Step 2: Local Environment Setup
Create .env file from template and populate with tunnel token:
Edit .env:
CLOUDFLARE_TUNNEL_TOKEN=<token_from_step_1>
Detailed instructions : See page 2.3 (Local Configuration)
File locations :
- Template: .env.sample:1-3
- Configuration to create:
.env(excluded by .gitignore1)
Sources : .env.sample:1-3 README.md53
Step 3: Container Deployment
Execute Docker Compose to start both services:
Detailed instructions : See page 2.4 (Deployment)
Services started :
mosquittoservice (docker-compose.yml:4-9)cloudflaredservice (docker-compose.yml:11-17)
Sources : README.md:76-78 docker-compose.yml:1-18
Container Initialization
Diagram: Docker Compose Service Startup Sequence
Sources : docker-compose.yml:1-18 mosquitto.conf:1-5
Post-Deployment Verification
Verify both containers are operational:
Expected output shows two running containers:
| Container Name | Image | Status | Ports |
|---|---|---|---|
mosquitto | eclipse-mosquitto:latest | Up | 1883/tcp, 9001/tcp |
cloudflared | cloudflare/cloudflared:latest | Up | (no exposed ports) |
Check cloudflared logs for tunnel establishment confirmation:
Check mosquitto logs for listener initialization:
Sources : docker-compose.yml:4-9 docker-compose.yml:11-17
Docker Compose Service Configuration
Diagram: Service Definition to Runtime Mapping
Sources : docker-compose.yml:1-18
Service Configuration Parameters
mosquitto Service
| Parameter | Value | Purpose |
|---|---|---|
container_name | mosquitto | DNS-resolvable name for internal routing |
image | eclipse-mosquitto:latest | Official Mosquitto MQTT broker image |
restart | unless-stopped | Automatic restart unless explicitly stopped |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | Inject broker configuration |
File reference : docker-compose.yml:4-9
cloudflared Service
| Parameter | Value | Purpose |
|---|---|---|
container_name | cloudflared | Container identifier for logging and management |
image | cloudflare/cloudflared:latest | Official Cloudflare Tunnel connector image |
restart | unless-stopped | Automatic restart unless explicitly stopped |
command | tunnel --no-autoupdate run | Execute tunnel with auto-update disabled |
environment | CLOUDFLARE_TUNNEL_TOKEN from .env | Authenticates tunnel connection |
File reference : docker-compose.yml:11-17
Sources : docker-compose.yml:1-18
Connection Flow After Deployment
Once both containers are operational, the request flow follows this path:
- External clients connect to public hostname (e.g.,
https://mqtt.example.com:443) - Cloudflare edge network receives connection
- Traffic routes through tunnel to
cloudflaredcontainer cloudflaredproxies tomosquitto:9001via Docker internal DNSmosquittocontainer handles connection on WebSocket listener (port 9001)
Diagram: Client-to-Broker Request Path
graph LR
Client["MQTT Client"]
CFEdge["Cloudflare Edge\n(Port 443)"]
Tunnel["Tunnel Infrastructure"]
CFDContainer["cloudflared container"]
MosqContainer["mosquitto container\n(Port 9001)"]
Client -->|HTTPS/WSS| CFEdge
CFEdge -->|Tunnel protocol| Tunnel
Tunnel -->|Encrypted tunnel| CFDContainer
CFDContainer -->|HTTP to mosquitto:9001| MosqContainer
Sources : README.md:64-67 README.md82
System Management Commands
| Command | Action | Effect |
|---|---|---|
docker compose up | Start services in foreground | Containers run, logs visible in terminal |
docker compose up -d | Start services in background | Containers run detached from terminal |
docker compose down | Stop and remove containers | Containers stopped, network removed, volumes persist |
docker compose restart | Restart both services | Containers restarted with existing configuration |
docker logs mosquitto | View mosquitto container logs | Display broker initialization and connection logs |
docker logs cloudflared | View cloudflared container logs | Display tunnel establishment and proxy logs |
Sources : docker-compose.yml:1-18
Next Steps
For detailed setup instructions , refer to child pages:
| Page | Title | Content |
|---|---|---|
| 2.1 | Prerequisites | System requirements, Docker installation, Cloudflare account setup |
| 2.2 | Cloudflare Tunnel Setup | Step-by-step tunnel creation, hostname configuration, token extraction |
| 2.3 | Local Configuration | .env file creation, mosquitto.conf customization, security validation |
| 2.4 | Deployment | Container startup, health verification, troubleshooting common errors |
For advanced topics , see:
- Component details : Pages 3.1 (Mosquitto), 3.2 (Cloudflared), 3.3 (Docker Compose)
- Configuration reference : Pages 4.1, 4.2, 4.3
- Production deployment : Page 6.2
- Advanced security features : Page 6.1 (see
protected-no-wildcardbranch for ACL and encryption)
Sources : README.md:7-13 (protected branch reference)
Common Initial Setup Issues
| Issue | Symptom | Resolution |
|---|---|---|
| Missing .env file | cloudflared fails to start with authentication error | Create .env file from .env.sample:1-3 template and add valid token |
| Invalid tunnel token | cloudflared logs show authentication failure | Verify token in Cloudflare dashboard and update .env file |
| Port conflicts | Container fails to start with port binding error | Check if ports 1883 or 9001 are already in use on the host |
| Tunnel not found | cloudflared reports tunnel does not exist | Ensure tunnel was created in Cloudflare dashboard and token matches |
| Public hostname not configured | Clients cannot connect | Configure public hostname in Cloudflare dashboard as described in Step 2 |
| Volume mount errors | mosquitto fails to load configuration | Verify mosquitto.conf exists in repository root as defined in docker-compose.yml8 |
For comprehensive troubleshooting, see Troubleshooting.
Sources : README.md:23-73 docker-compose.yml:1-18 .env.sample:1-3
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Prerequisites
Loading…
Prerequisites
Relevant source files
This page documents the required accounts, software, system resources, and knowledge needed before deploying the Docker MQTT Mosquitto with Cloudflare Tunnel system. Complete all prerequisites in this section before proceeding to Cloudflare Tunnel Setup.
For information about the actual setup process, see Cloudflare Tunnel Setup. For details about the system’s architectural requirements, see System Architecture.
Required Accounts
Cloudflare Account with Zero Trust Access
A Cloudflare account with Zero Trust dashboard access is required to create and manage the Cloudflare Tunnel. The tunnel connector authenticates using a token generated through this interface.
Account Requirements:
- Active Cloudflare account (free tier sufficient)
- Access to the Zero Trust dashboard at
https://one.dash.cloudflare.com/ - Permission to create tunnels under the Networks → Tunnels section
Why Required: The system architecture depends on the Cloudflare Tunnel service to provide secure, outbound-only connectivity between the cloudflared container and Cloudflare’s edge network. Without Zero Trust access, you cannot generate the CLOUDFLARE_TUNNEL_TOKEN that authenticates the tunnel connection specified in docker-compose.yml14
| Account Feature | Usage in System | Configuration Location |
|---|---|---|
| Zero Trust Dashboard | Tunnel creation and management | N/A (external UI) |
| Tunnel Token | Container authentication | docker-compose.yml14 |
| Public Hostname | DNS routing configuration | Configured via dashboard |
Sources: README.md:29-31 README.md:36-51
Required Software
Docker Engine
Docker Engine is required to run the containerized services defined in docker-compose.yml The system has been tested with Docker Engine version 20.10+ but should work with any recent release supporting Compose file format version 3.8.
Installation Verification:
Expected output format: Docker version XX.XX.X, build XXXXXXX
Why Required: The system consists of two containerized services (mosquitto and cloudflared) that must run in an isolated network environment. Docker Engine provides the container runtime, networking layer, and volume management capabilities referenced in docker-compose.yml:1-18
Docker Compose
Docker Compose orchestrates the multi-container application lifecycle. The docker-compose.yml1 file uses format version 3.8, requiring Docker Compose v1.28.0+ or Docker Engine with integrated Compose v2.
Installation Verification:
Expected output format: Docker Compose version vX.XX.X or docker-compose version X.XX.X
Why Required: The system requires coordinated startup, networking, and lifecycle management of two containers: mosquitto (MQTT broker) and cloudflared (tunnel connector). Docker Compose provides service discovery via internal DNS, allowing the cloudflared container to resolve mosquitto:9001 as configured in docker-compose.yml:4-17
Git (Optional but Recommended)
Git version control is recommended for cloning the repository and tracking local configuration changes.
Installation Verification:
Why Required: While you can download the repository as a ZIP archive, Git provides version tracking for local modifications to mosquitto.conf and allows you to easily integrate updates from upstream. The .gitignore1 configuration ensures that your .env file containing secrets is never committed.
Sources: docker-compose.yml1 .gitignore1
System Requirements
Hardware Resources
The system has minimal resource requirements due to the lightweight nature of both containers.
| Component | Minimum | Recommended | Purpose |
|---|---|---|---|
| CPU Cores | 1 core | 2+ cores | Handle concurrent MQTT connections |
| RAM | 512 MB | 1 GB+ | Container overhead + message buffering |
| Disk Space | 500 MB | 2 GB+ | Container images + log storage |
| Network | Outbound internet | Low latency connection | Cloudflare Tunnel connectivity |
Container-Specific Resources:
The mosquitto container (docker-compose.yml:4-9) runs Eclipse Mosquitto, a minimal MQTT broker with low memory footprint. The cloudflared container (docker-compose.yml:11-17) maintains a persistent WebSocket connection to Cloudflare’s network and proxies traffic to the Mosquitto service.
Why These Requirements:
- Mosquitto’s memory usage scales with the number of retained messages and active connections
- The cloudflared tunnel requires stable outbound connectivity to maintain the persistent connection
- No inbound ports are exposed, so firewall configuration is not required
Operating System
The system runs on any platform supporting Docker Engine:
- Linux : Native Docker support (Ubuntu, Debian, RHEL, CentOS, etc.)
- macOS : Docker Desktop for Mac
- Windows : Docker Desktop for Windows (WSL2 backend recommended)
The containers use multi-architecture images (eclipse-mosquitto:latest and cloudflare/cloudflared:latest) that support both AMD64 and ARM64 architectures.
Sources: docker-compose.yml5 docker-compose.yml12
Domain and DNS Prerequisites
Cloudflare-Managed Domain
You must have a domain registered with and managed by Cloudflare to configure public hostname routing.
Domain Requirements:
- Domain added to your Cloudflare account
- Nameservers pointing to Cloudflare’s DNS servers
- DNS zone active (orange-clouded or proxied)
Why Required: When configuring the public hostname in Cloudflare Tunnel Setup, you specify a subdomain (e.g., mqtt.example.com) that routes to your tunnel. Cloudflare must control the DNS zone to create the CNAME record pointing to the tunnel endpoint.
Diagram: DNS Resolution Flow Requiring Cloudflare-Managed Domain
graph LR
subgraph "External DNS"
CLIENT["Client"]
PUBLIC_DNS["Public DNS Resolver"]
end
subgraph "Cloudflare Network"
CF_DNS["Cloudflare DNS\n(Authoritative)"]
CF_TUNNEL["Cloudflare Tunnel\nInfrastructure"]
end
subgraph "Local Docker Environment"
CLOUDFLARED["cloudflared container"]
MOSQUITTO["mosquitto container"]
end
CLIENT -->|1. DNS query: mqtt.example.com| PUBLIC_DNS
PUBLIC_DNS -->|2. Query nameservers| CF_DNS
CF_DNS -->|3. Return CNAME: tunnel-id.cfargotunnel.com| PUBLIC_DNS
PUBLIC_DNS -->|4. Resolve to Cloudflare edge IP| CLIENT
CLIENT -->|5. HTTPS request| CF_TUNNEL
CF_TUNNEL <-->|6. Tunnel protocol| CLOUDFLARED
CLOUDFLARED -->|7. HTTP proxy| MOSQUITTO
The diagram illustrates why domain management through Cloudflare is required. The tunnel’s public hostname must resolve to Cloudflare’s infrastructure, which requires authoritative DNS control.
Sources: README.md:61-67
File System Prerequisites
Working Directory Structure
The deployment requires a local directory containing the repository files with specific structure:
docker-mqtt-mosquitto-cloudflare-tunnel/
├── docker-compose.yml # Service orchestration definition
├── mosquitto.conf # Mosquitto broker configuration
├── .env # Secret environment variables (created by user)
├── .env.sample # Template for .env file
└── .gitignore # Ensures .env is not committed
Required Files:
- docker-compose.yml:1-18: Defines both container services
- mosquitto.conf:1-5: Configures MQTT and WebSocket listeners
User-Created Files:
.env: Must containCLOUDFLARE_TUNNEL_TOKEN=<your_token>(see .env.sample1)
Protected Files:
- .gitignore1: Ensures
.envis excluded from version control
Write Permissions
The Docker Engine process requires read access to:
- docker-compose.yml
- mosquitto.conf
.envfile
The Mosquitto container requires write access to its internal directories for log files and persistence (not exposed as volumes in the base configuration).
Sources: docker-compose.yml:7-8 .env.sample1 .gitignore1
graph TB
subgraph "Local Network"
DOCKER_HOST["Docker Host"]
CLOUDFLARED["cloudflared Container"]
end
subgraph "Firewall Rules"
OUTBOUND_443["Allow Outbound\nTCP/443\n(HTTPS)"]
INBOUND_BLOCKED["Block All Inbound\n(No ports exposed)"]
end
subgraph "Cloudflare Network"
CF_EDGE["Cloudflare Edge\nTunnel Endpoint"]
end
CLOUDFLARED -->|Persistent WebSocket Over HTTPS| OUTBOUND_443
OUTBOUND_443 --> CF_EDGE
INBOUND_BLOCKED -.->|Not required| DOCKER_HOST
Network Prerequisites
Outbound Internet Access
The cloudflared container requires persistent outbound HTTPS connectivity (port 443) to Cloudflare’s network.
Required Connectivity:
Diagram: Required Network Access Pattern
Firewall Configuration:
- Outbound: Port 443 (HTTPS) must be allowed for tunnel connectivity
- Inbound: No inbound ports required; tunnel uses outbound-only connections
- Internal: Docker’s default bridge network handles inter-container routing
Why This Design: The outbound-only architecture (README.md17) eliminates the need for port forwarding, dynamic DNS, or public IP addresses. The cloudflared container (docker-compose.yml:11-17) initiates the connection to Cloudflare’s network, which then proxies inbound client traffic through the established tunnel.
Sources: docker-compose.yml:11-17 README.md17
Knowledge Prerequisites
Required Technical Knowledge
Users deploying this system should have working knowledge of:
| Knowledge Area | Required For | Documentation Reference |
|---|---|---|
| Docker containers | Understanding service architecture | Components |
| Docker Compose | Managing multi-container deployments | Docker Compose Orchestration |
| MQTT protocol | Configuring broker listeners and client connections | MQTT Protocol Listeners |
| Environment variables | Configuring secrets and runtime parameters | Environment Variables |
| DNS concepts | Understanding public hostname routing | Cloudflare Tunnel Setup |
Optional Advanced Knowledge
The following knowledge areas are optional but helpful for troubleshooting and customization:
- Linux command line: For log inspection and container debugging
- Cloudflare Zero Trust: For advanced access policies and authentication
- MQTT ACLs: For implementing user-based topic restrictions (see [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch))
- Docker networking: For custom network configurations
Sources: README.md:7-13
Prerequisites Verification Checklist
Before proceeding to Cloudflare Tunnel Setup, verify all prerequisites:
Diagram: Prerequisites Verification Flow
After completing this checklist, proceed to Cloudflare Tunnel Setup to begin the deployment process.
Sources: README.md:29-78 docker-compose.yml:1-18
graph TB
subgraph "Prerequisites"
CF_ACCT["Cloudflare Account"]
DOCKER_ENG["Docker Engine"]
COMPOSE_CLI["Docker Compose"]
CF_DOMAIN["Cloudflare-Managed\nDomain"]
OUTBOUND_NET["Outbound Network\nAccess"]
end
subgraph "Configuration Files"
ENV_FILE[".env"]
ENV_SAMPLE[".env.sample"]
COMPOSE_YML["docker-compose.yml"]
MOSQ_CONF["mosquitto.conf"]
GITIGNORE[".gitignore"]
end
subgraph "Runtime Artifacts"
TOKEN_VAR["CLOUDFLARE_TUNNEL_TOKEN\nenvironment variable"]
MOSQ_SERVICE["mosquitto service"]
CFD_SERVICE["cloudflared service"]
MOSQ_MOUNT["mosquitto.conf\nvolume mount"]
end
CF_ACCT -->|generates| TOKEN_VAR
TOKEN_VAR -->|stored in| ENV_FILE
ENV_SAMPLE -->|template for| ENV_FILE
DOCKER_ENG -->|executes| COMPOSE_YML
COMPOSE_CLI -->|orchestrates| COMPOSE_YML
COMPOSE_YML -->|defines| MOSQ_SERVICE
COMPOSE_YML -->|defines| CFD_SERVICE
COMPOSE_YML -->|references| TOKEN_VAR
COMPOSE_YML -->|mounts| MOSQ_MOUNT
MOSQ_CONF -->|mounted as| MOSQ_MOUNT
MOSQ_MOUNT -->|configures| MOSQ_SERVICE
CF_DOMAIN -->|routes to| CFD_SERVICE
OUTBOUND_NET -->|enables| CFD_SERVICE
GITIGNORE -.->|protects| ENV_FILE
Relationship to System Files
The following diagram maps prerequisites to the specific files and configurations they enable:
Diagram: Prerequisites Mapping to System Files
This diagram shows how each prerequisite enables specific parts of the system configuration. For example:
- The Cloudflare account generates the token stored in
.env - Docker Engine and Compose execute docker-compose.yml:1-18
- The mosquitto.conf:1-5 file is mounted as a volume into the
mosquittocontainer - The domain and network prerequisites enable the
cloudflaredservice to function
Sources: docker-compose.yml:1-18 mosquitto.conf:1-5 .env.sample1 .gitignore1
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
System Architecture
Loading…
System Architecture
Relevant source files
Purpose and Scope
This page provides a detailed technical explanation of the system architecture for the Docker MQTT Mosquitto with Cloudflare Tunnel deployment. It covers container topology, network routing, external access patterns, and configuration mechanisms. For information about security principles and threat models, see Security Model. For step-by-step deployment instructions, see Getting Started.
The architecture implements a Zero Trust access pattern where the MQTT broker remains completely unexposed to the public internet, with all external access routed through Cloudflare’s network via an outbound-only tunnel connection.
Sources: README.md:1-93
Container Topology
Service Definitions
The system consists of two containerized services orchestrated by Docker Compose:
| Service Name | Container Name | Image | Purpose |
|---|---|---|---|
mosquitto | mosquitto | eclipse-mosquitto:latest | MQTT message broker with WebSocket support |
cloudflared | cloudflared | cloudflare/cloudflared:latest | Cloudflare Tunnel connector for secure external access |
Both services are defined in docker-compose.yml:3-17 and run on Docker Compose’s default bridge network, enabling service-to-service communication via Docker’s internal DNS resolver.
graph TB
subgraph DockerComposeOrchestration["Docker Compose (version 3.8)"]
subgraph MosquittoService["mosquitto service"]
MosquittoContainer["mosquitto container\n(eclipse-mosquitto:latest)"]
MosquittoVolume["Volume Mount:\n./mosquitto.conf\n→\n/mosquitto/config/mosquitto.conf"]
MosquittoRestart["restart: unless-stopped"]
end
subgraph CloudflaredService["cloudflared service"]
CloudflaredContainer["cloudflared container\n(cloudflare/cloudflared:latest)"]
CloudflaredCommand["command:\ntunnel --no-autoupdate run --token"]
CloudflaredEnv["environment:\nCLOUDFLARE_TUNNEL_TOKEN"]
CloudflaredRestart["restart: unless-stopped"]
end
DockerDNS["Docker Internal DNS"]
end
MosquittoVolume -->|configures| MosquittoContainer
MosquittoRestart -.->|lifecycle policy| MosquittoContainer
CloudflaredEnv -->|authenticates| CloudflaredCommand
CloudflaredCommand -->|runs in| CloudflaredContainer
CloudflaredRestart -.->|lifecycle policy| CloudflaredContainer
CloudflaredContainer -->|resolves 'mosquitto:9001'| DockerDNS
DockerDNS -->|maps to container IP| MosquittoContainer
Container Dependency Graph
Sources: docker-compose.yml:1-18
Network Architecture
Internal Docker Networking
Docker Compose creates an isolated bridge network where containers communicate using service names as hostnames. The cloudflared service connects to the Mosquitto broker using the internal hostname mosquitto:9001, which Docker’s DNS resolver maps to the mosquitto container’s IP address on the bridge network.
Key Architectural Properties:
graph LR
subgraph DockerBridgeNetwork["Docker Bridge Network"]
CloudflaredProc["cloudflared process\n(cloudflared container)"]
MosquittoProc["mosquitto process\n(mosquitto container)"]
CloudflaredProc -->|HTTP request to mosquitto:9001| DockerInternalDNS["Docker DNS Resolver"]
DockerInternalDNS -->|resolves to container IP| MosquittoProc
subgraph MosquittoListeners["Mosquitto Listeners"]
Port1883["Port 1883\n(MQTT protocol)"]
Port9001["Port 9001\n(WebSocket/HTTP)"]
end
MosquittoProc -->|binds| Port1883
MosquittoProc -->|binds| Port9001
end
HostMachine["Host Machine\n(NO port exposure)"]
DockerBridgeNetwork -.->|isolated from| HostMachine
- No Port Exposure: Neither service exposes ports to the host machine via the
portsdirective in docker-compose.yml - Service Discovery: The
cloudflaredcontainer resolvesmosquittovia Docker’s internal DNS without IP address configuration - Network Isolation: All inter-service communication occurs within the Docker bridge network
Sources: docker-compose.yml:4-17 README.md:64-67
External Access Flow
Cloudflare Tunnel Architecture
External clients connect to the MQTT broker through Cloudflare’s infrastructure, never directly accessing the origin server. This inverted connection model eliminates the need for inbound firewall rules or public IP addresses.
sequenceDiagram
participant ExtClient as "External MQTT Client"
participant CFEdge as "Cloudflare Edge Network\n(Global PoPs)"
participant CFTunnel as "Cloudflare Tunnel Infrastructure"
participant CloudflaredContainer as "cloudflared container"
participant MosquittoContainer as "mosquitto container"
Note over CloudflaredContainer,CFTunnel: Tunnel Establishment (Startup)
CloudflaredContainer->>CFTunnel: Outbound connection with token
CFTunnel-->>CloudflaredContainer: Tunnel established
Note over ExtClient,MosquittoContainer: Client Connection Flow
ExtClient->>CFEdge: Connect to subdomain.domain.com:443
CFEdge->>CFTunnel: Route via tunnel infrastructure
CFTunnel->>CloudflaredContainer: Forward through encrypted tunnel
CloudflaredContainer->>MosquittoContainer: HTTP request to mosquitto:9001
MosquittoContainer-->>CloudflaredContainer: HTTP response
CloudflaredContainer-->>CFTunnel: Return
CFTunnel-->>CFEdge: Return
CFEdge-->>ExtClient: HTTPS response on port 443
Note over CloudflaredContainer,MosquittoContainer: Protocol Transformation\nExternal: HTTPS/WSS (port 443)\nInternal: HTTP/WS (port 9001)
Access Pattern
- Outbound-Only Connection: The
cloudflaredcontainer initiates a persistent outbound connection to Cloudflare’s network using the commandtunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}docker-compose.yml14 - Token Authentication: The
CLOUDFLARE_TUNNEL_TOKENenvironment variable docker-compose.yml17 authenticates the tunnel to Cloudflare’s infrastructure - Protocol Transformation: Cloudflare receives external HTTPS/WSS connections on port 443 and forwards them as HTTP to
mosquitto:9001README.md:63-67 - DNS Routing: The public hostname configured in Cloudflare’s dashboard (e.g.,
subdomain.domain.com) routes to the tunnel, which forwards to the internalmosquitto:9001service endpoint
Sources: docker-compose.yml:11-17 README.md:57-82
Configuration Mechanisms
Configuration Injection Points
The system employs two distinct configuration injection mechanisms:
Configuration Details
| Component | Mechanism | File Path | Container Path | Purpose |
|---|---|---|---|---|
cloudflared | Environment Variable | .env (host) | CLOUDFLARE_TUNNEL_TOKEN env var | Tunnel authentication token |
mosquitto | Volume Mount | ./mosquitto.conf (host) | /mosquitto/config/mosquitto.conf | Broker listener and protocol configuration |
Token Injection: The CLOUDFLARE_TUNNEL_TOKEN is read from the .env file on the host system docker-compose.yml17 and passed to the cloudflared container as an environment variable. The token is referenced in the container’s command via shell variable substitution: --token ${CLOUDFLARE_TUNNEL_TOKEN} docker-compose.yml14
Configuration File Mounting: The mosquitto.conf file is mounted from the host filesystem into the container at /mosquitto/config/mosquitto.conf docker-compose.yml8 This allows the Mosquitto process to read its configuration without rebuilding the container image.
Sources: docker-compose.yml:7-17 README.md53
Service Restart Policies
Both services implement the restart: unless-stopped policy docker-compose.yml:9-15 which provides automatic recovery from failures:
- Automatic Restart: Containers restart automatically if they crash or exit unexpectedly
- Manual Control: Containers do not restart if explicitly stopped by
docker-compose stopordocker-compose down - System Reboot: Containers automatically start when the Docker daemon starts (unless manually stopped)
This policy ensures high availability while allowing manual intervention for maintenance operations.
Sources: docker-compose.yml:9-15
Container Lifecycle
Sources: docker-compose.yml:1-18
Communication Protocol Stack
The system implements a multi-layer protocol transformation:
| Layer | External (Internet) | Internal (Docker) |
|---|---|---|
| Transport | TLS 1.3 (port 443) | Unencrypted HTTP (port 9001) |
| Application | WebSocket Secure (WSS) or MQTT over TLS | WebSocket (WS) or MQTT |
| Network | Public internet via Cloudflare | Docker bridge network |
| Routing | DNS to Cloudflare Edge PoPs | Docker DNS to container IP |
Security Boundary: TLS termination occurs at Cloudflare’s edge network. The internal Docker network segment is trusted and does not require encryption, as it is isolated from external access.
Sources: README.md:63-82
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Cloudflare Tunnel Setup
Loading…
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
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Security Model
Loading…
Security Model
Relevant source files
Purpose and Scope
This document explains the security architecture of the Docker MQTT Mosquitto Cloudflare Tunnel system, including threat model, attack surface reduction, secret management, and network isolation mechanisms. It covers the security principles embedded in the system design and the trade-offs involved in the default configuration.
For operational deployment considerations including production hardening and monitoring, see Production Deployment Considerations. For authentication and access control configuration specifics, see Authentication and Access Control.
Core Security Principles
The system implements three foundational security principles:
| Principle | Implementation | Benefit |
|---|---|---|
| Zero Trust Network Access | Cloudflare Tunnel with outbound-only connections | Origin server has no exposed attack surface |
| Separation of Code and Secrets | .gitignore exclusion of .env file | Prevents credential leakage through version control |
| Defense in Depth | Multiple isolation layers (Cloudflare Edge, Docker network, container boundaries) | Compromising one layer does not expose entire system |
Zero Trust Architecture
The system inverts the traditional client-server model. Instead of the MQTT broker accepting inbound connections from the internet, the cloudflared container establishes a persistent outbound connection to Cloudflare’s network. External clients never connect directly to the origin server.
Sources : README.md:17, README.md:22, README.md:67
Attack Surface Analysis
Traditional MQTT Deployment vs. Cloudflare Tunnel Deployment
Eliminated Attack Vectors
The Cloudflare Tunnel architecture eliminates entire categories of attacks:
| Attack Type | Traditional Deployment | Tunnel Deployment |
|---|---|---|
| Port Scanning | Vulnerable - ports 1883 and 9001 discoverable | Immune - no listening ports on public internet |
| Direct DDoS | Vulnerable - origin server is attack target | Protected - Cloudflare absorbs attack |
| SSL/TLS Certificate Management | Required - operator manages certificates | Unnecessary - Cloudflare manages certificates |
| IP-based Access Control | Fragile - IP addresses change, easily spoofed | N/A - origin has no public IP exposure |
| Zero-day Exploits in Mosquitto | High impact - direct exploitation | Reduced impact - traffic filtered through Cloudflare WAF |
Sources : README.md:17, Diagram 1 (High-Level System Architecture), Diagram 3 (External Access and Security Flow)
Secret Management Model
Token-Based Authentication
The CLOUDFLARE_TUNNEL_TOKEN serves as the cryptographic secret that authenticates the cloudflared container to Cloudflare’s network. This token grants the right to route traffic for the configured tunnel.
stateDiagram-v2
[*] --> Generated : User creates tunnel in Cloudflare Dashboard
Generated --> Copied : Token displayed in UI
Copied --> Local : User creates .env file
state Local {
[*] --> FileCreated
FileCreated --> TokenAdded : Paste CLOUDFLARE_TUNNEL_TOKEN=...
TokenAdded --> GitignoreProtected : .gitignore prevents commit
}
Local --> Runtime : docker-compose reads .env
state Runtime {
[*] --> EnvLoaded
EnvLoaded --> ContainerStarted : Environment variable injected
ContainerStarted --> TunnelAuth : cloudflared authenticates
}
Runtime --> Operational : Tunnel established
state GitignoreProtected {
[*] --> CheckGitStatus
CheckGitStatus --> NotTracked : .env excluded
NotTracked --> [*]
}
Operational --> Rotated : Token rotation (manual)
Rotated --> Generated
note right of GitignoreProtected
Critical security boundary:
.env file must never be
committed to version control
end note
Secret Lifecycle
Version Control Protection
The repository implements multiple layers of protection against credential leakage:
| Protection Layer | Implementation | File Reference |
|---|---|---|
| Explicit exclusion | .gitignore contains .env | .gitignore1 |
| Template file | .env.sample shows format without secrets | .env.sample1 |
| Documentation | README instructs users to create .env manually | README.md53 |
The .gitignore file explicitly excludes the .env file containing the tunnel token:
.env
data/*
The .env.sample template provides structure without exposing actual credentials:
CLOUDFLARE_TUNNEL_TOKEN=your_token
Important : The .env.sample file is committed to version control as documentation. Operators must create their own .env file locally and populate it with their actual token.
Sources : .gitignore:1-2 .env.sample1 README.md53
Network Security Boundaries
Container Network Isolation
Security Boundaries Defined
| Boundary | Trust Level | Entry Requirements | Enforcement Mechanism |
|---|---|---|---|
| Public Internet → Cloudflare Edge | Untrusted → Trusted | TLS handshake, valid hostname | Cloudflare network infrastructure |
| Cloudflare Edge → Tunnel Infrastructure | Trusted → Trusted | Valid tunnel configuration | Cloudflare internal routing |
Tunnel Infrastructure → cloudflared container | Trusted → Trusted | CLOUDFLARE_TUNNEL_TOKEN authentication | Tunnel protocol cryptographic verification |
cloudflared container → mosquitto container | Trusted → Trusted | Docker internal DNS resolution | Docker bridge network isolation |
External clients → mosquitto container | Forbidden | N/A - no direct route exists | Docker network isolation, no port exposure |
Port Exposure Analysis
The docker-compose.yml configuration deliberately omits port mappings to the host system. Neither container exposes any ports outside the Docker bridge network:
This architectural decision ensures that:
- The Mosquitto broker is not accessible via the host’s network interfaces
- Port scanning of the host reveals no MQTT services
- Firewall rules on the host are irrelevant to MQTT access
- The only route to the broker is through the authenticated Cloudflare Tunnel
Sources : docker-compose.yml:1-19 mosquitto.conf:1-5 Diagram 2 (Docker Container Network Topology)
Authentication and Authorization
Default Configuration: Anonymous Access Allowed
The default mosquitto.conf configuration permits anonymous connections and does not enforce authentication:
listener 1883
protocol mqtt
listener 9001
protocol websockets
Security Implication : Any client that can reach the broker (via the Cloudflare Tunnel) can publish and subscribe to any topic without authentication.
graph LR
Client["Client with URL"]
subgraph CloudflareLayer["Cloudflare Layer - Transport Security"]
TLS["TLS Termination\nCertificate validation"]
WAF["Web Application Firewall\nOptional rules"]
end
subgraph MQTTLayer["MQTT Layer - Application Security"]
Anonymous["Anonymous access permitted\nNo username/password required"]
NoACL["No topic-level restrictions\nFull pub/sub access"]
end
Client -->|HTTPS connection| TLS
TLS -->|Decrypted, inspected| WAF
WAF -->|Forwarded if allowed| Anonymous
Anonymous -->|All operations permitted| NoACL
NoACL -->|Can publish to any topic| Topics["All MQTT Topics"]
NoACL -->|Can subscribe to any topic| Topics
Access Control Model
Security Trade-offs
| Security Feature | Default Configuration | Security Benefit | Operational Complexity |
|---|---|---|---|
| Anonymous access | Enabled | None - reduces security | Low - no credential management |
| Password authentication | Disabled | Would prevent unauthorized access | Medium - requires credential distribution |
| Topic-level ACLs | Not configured | Would limit topic access per client | High - requires ACL file management |
| TLS client certificates | Not configured | Would provide mutual TLS authentication | Very High - PKI infrastructure required |
Enhanced Security Branch
The repository includes an alternate configuration branch (protected-no-wildcard) that implements:
- Topic-based Access Control : ACL file restricts wildcard subscriptions and enforces username-based topic prefixes
- Encrypted Retained Messages : Uses
gocryptfsto encrypt persistent message storage - Auto-save retained messages : Ensures message persistence
This branch demonstrates production-grade security hardening. See Protected Branch Features for details.
Sources : mosquitto.conf:1-5 README.md:7-13
TLS and Encryption
Multi-layer Encryption Architecture
Encryption Scope
| Network Segment | Encryption Status | Justification |
|---|---|---|
| Client → Cloudflare Edge | Encrypted (TLS 1.3) | Public internet traversal requires encryption |
| Cloudflare Edge → Tunnel Infrastructure | Encrypted (Cloudflare proprietary) | Cloudflare’s internal network, encrypted by default |
Tunnel Infrastructure → cloudflared | Encrypted (Tunnel protocol) | Traverses internet between Cloudflare PoP and origin |
cloudflared → mosquitto | Unencrypted (HTTP) | Isolated Docker bridge network, no external exposure |
Rationale for unencrypted internal segment : The traffic between cloudflared and mosquitto occurs entirely within the Docker bridge network, which is isolated from external networks. Adding TLS to this segment would:
- Increase CPU overhead without security benefit
- Require certificate management for internal services
- Complicate debugging and troubleshooting
Sources : README.md:67-68 docker-compose.yml13 Diagram 3 (External Access and Security Flow)
Operational Security Considerations
Token Rotation
The CLOUDFLARE_TUNNEL_TOKEN does not automatically expire. Best practices for production deployments:
- Periodic rotation : Rotate tokens on a scheduled basis (e.g., quarterly)
- Incident response : Immediately rotate if token exposure is suspected
- Access logging : Monitor Cloudflare logs for tunnel usage patterns
Secret Storage in Production
The .env file approach is suitable for development but should be replaced in production:
| Environment | Recommended Secret Storage | Rationale |
|---|---|---|
| Development | .env file (local) | Simple, adequate for local testing |
| Production (single host) | Docker secrets, HashiCorp Vault | Encrypted at rest, access logging |
| Production (orchestrated) | Kubernetes secrets, AWS Secrets Manager | Integrated with orchestration platform |
Container Image Security
Both containers use official images from trusted registries:
eclipse-mosquitto:latest- Official Eclipse Foundation imagecloudflare/cloudflared:latest- Official Cloudflare image
Recommendation : Pin specific image versions rather than using latest to ensure reproducible deployments and controlled updates:
Sources : docker-compose.yml:3-4 docker-compose.yml:11-12
Threat Model Summary
Mitigated Threats
| Threat | Mitigation | Effectiveness |
|---|---|---|
| Network reconnaissance | No exposed ports | Complete - port scanning reveals nothing |
| Direct exploitation of Mosquitto | Traffic filtered through Cloudflare | High - reduces exploit surface |
| DDoS against origin | Cloudflare absorbs attack | Very High - Cloudflare’s network capacity |
| Man-in-the-middle | End-to-end TLS encryption | High - certificate validation required |
| Credential leakage via Git | .gitignore exclusion | High - if properly configured |
Residual Risks
| Risk | Severity | Mitigation Strategy |
|---|---|---|
| Token compromise | High | Token rotation, access monitoring, secrets management |
| Anonymous MQTT access | Medium-High | Enable authentication, implement ACLs (see Protected Branch) |
| Cloudflare service dependency | Medium | Accept as operational requirement, or implement alternative tunnel |
| Container escape | Low | Use updated Docker versions, implement AppArmor/SELinux profiles |
Insider threat (access to .env) | Medium | Implement principle of least privilege, audit file access |
Defense-in-Depth Layers
The system implements multiple independent security layers. An attacker must bypass all layers to compromise the system:
- Layer 1 - Cloudflare Edge : DDoS protection, rate limiting, WAF rules
- Layer 2 - Tunnel Authentication : Valid tunnel token required
- Layer 3 - Network Isolation : No direct route to containers from external networks
- Layer 4 - Application Security : Mosquitto’s internal security (minimal in default config)
- Layer 5 - Secret Management :
.gitignoreprevents token leakage through version control
Sources : .gitignore:1-2 README.md82 Diagram 1 (High-Level System Architecture), Diagram 6 (Data and Secret Flow)
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Local Configuration
Loading…
Local Configuration
Relevant source files
Purpose and Scope
This document guides you through setting up the local configuration files required to deploy the Docker MQTT Mosquitto with Cloudflare Tunnel system. This includes creating the environment variable file (.env) and understanding the Mosquitto broker configuration (mosquitto.conf).
This page assumes you have already completed the Cloudflare Tunnel Setup and obtained your CLOUDFLARE_TUNNEL_TOKEN. For deployment instructions after configuration is complete, see Deployment.
Configuration Files Overview
The system requires two primary configuration files:
| File | Purpose | Version Control | Security Level |
|---|---|---|---|
.env | Contains CLOUDFLARE_TUNNEL_TOKEN secret | Excluded (via .gitignore) | Critical - Never commit |
.env.sample | Template showing required variables | Committed to repository | Safe - Contains no secrets |
mosquitto.conf | MQTT broker listener configuration | Committed to repository | Safe - Contains no secrets |
.gitignore | Prevents accidental secret commits | Committed to repository | Safety mechanism |
Diagram: Configuration File Relationships
Sources: .env.sample1 .gitignore:1-2 mosquitto.conf:1-6
Environment Variable Configuration
Creating the .env File
The .env file stores sensitive configuration that must never be committed to version control. The repository includes .env.sample1 as a template.
Step 1: Copy the template
Step 2: Populate the token
Edit the .env file and replace your_token with the actual CLOUDFLARE_TUNNEL_TOKEN obtained from the Cloudflare dashboard during Cloudflare Tunnel Setup:
CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiYWJjMTIzLi4uLnJlYWxfdG9rZW5faGVyZSJ9
Environment Variable Reference
| Variable | Required | Description | Source |
|---|---|---|---|
CLOUDFLARE_TUNNEL_TOKEN | Yes | Authentication token for cloudflared container | Cloudflare Zero Trust dashboard |
Diagram: Environment Variable Flow to Containers
Sources: .env.sample1
Security Verification
The .gitignore1 file explicitly excludes .env from version control:
.env
data/*
Verify.env is excluded:
If .env appears in git status output, verify that .gitignore1 is present and contains the correct exclusion rule.
Sources: .gitignore:1-2
Mosquitto Broker Configuration
The mosquitto.conf file configures the Eclipse Mosquitto MQTT broker. This file is committed to version control as it contains no secrets.
Listener Configuration
The mosquitto.conf:1-6 file defines two protocol listeners:
Diagram: Mosquitto Listener Configuration
graph TB
mosq_conf["mosquitto.conf"]
listener_1883["listener 1883\nStandard MQTT protocol"]
listener_9001["listener 9001\nWebSocket protocol"]
allow_anon["allow_anonymous true\nNo authentication required"]
mosq_conf -->|Line 1| listener_1883
mosq_conf -->|Line 2| allow_anon
mosq_conf -->|Lines 4-5| listener_9001
listener_1883 -.->|Used by| mqtt_clients["Native MQTT clients\nIoT devices"]
listener_9001 -.->|Used by| ws_clients["WebSocket clients\nBrowsers, web apps"]
Sources: mosquitto.conf:1-6
Configuration Directives
| Directive | Value | Purpose | Line |
|---|---|---|---|
listener | 1883 | Standard MQTT protocol port (TCP) | mosquitto.conf1 |
allow_anonymous | true | Permits unauthenticated connections | mosquitto.conf2 |
listener | 9001 | WebSocket protocol port (HTTP/WS) | mosquitto.conf4 |
protocol | websockets | Enable WebSocket framing on port 9001 | mosquitto.conf5 |
Protocol Explanation
Port 1883: Native MQTT
- Binary MQTT protocol (MQTT v3.1.1 / v5.0)
- Used by: IoT devices, MQTT client libraries, native applications
- Routed through Cloudflare Tunnel as-is (protocol preservation)
Port 9001: MQTT over WebSockets
- MQTT protocol encapsulated in WebSocket frames
- Used by: Browser-based clients, web applications
- Required for environments without raw TCP socket support
- Cloudflare routes this as standard HTTP/WebSocket traffic
Diagram: Configuration File to Container Mount
graph LR
host_fs["Host Filesystem\n./mosquitto.conf"]
compose_volume["docker-compose.yml\nvolumes:\n./mosquitto.conf:/mosquitto/config/mosquitto.conf"]
container_fs["mosquitto container\n/mosquitto/config/mosquitto.conf"]
mosq_process["Mosquitto process\nreads config on startup"]
host_fs -->|1. Volume mount defined| compose_volume
compose_volume -->|2. Bind mounted| container_fs
container_fs -->|3. Loaded by| mosq_process
mosq_process -->|4. Opens listeners| ports["Port 1883\nPort 9001"]
Sources: mosquitto.conf:1-6
graph TB
subgraph "Host System"
env_file[".env\nCLOUDFLARE_TUNNEL_TOKEN=..."]
mosq_conf["mosquitto.conf\nlistener 1883\nlistener 9001"]
end
subgraph "docker-compose.yml"
compose_env["environment:\nCLOUDFLARE_TUNNEL_TOKEN"]
compose_volume["volumes:\n./mosquitto.conf:/mosquitto/config/mosquitto.conf"]
end
subgraph "Containers"
cloudflared_container["cloudflared\nEnvironment: CLOUDFLARE_TUNNEL_TOKEN\nCommand: tunnel run"]
mosquitto_container["mosquitto\nMounted: /mosquitto/config/mosquitto.conf\nReads config on startup"]
end
env_file -->|Loaded by Docker Compose| compose_env
mosq_conf -->|Volume definition| compose_volume
compose_env -->|Injected as env var| cloudflared_container
compose_volume -->|Bind mounted| mosquitto_container
cloudflared_container -->|Authenticates with| cf_network["Cloudflare Network"]
mosquitto_container -->|Opens listeners| listeners["1883: MQTT\n9001: WebSocket"]
Configuration Flow to Containers
When Docker Compose starts the system, configuration is injected into containers through two mechanisms:
Diagram: Complete Configuration Injection Flow
Sources: .env.sample1 mosquitto.conf:1-6
Security Best Practices
Secret Management Checklist
Before proceeding to Deployment, verify the following security measures:
| Check | Verification Command | Expected Result |
|---|---|---|
.env file exists | ls -la .env | File present |
.env contains token | cat .env | CLOUDFLARE_TUNNEL_TOKEN=eyJ... |
.env is git-ignored | git status | .env not listed |
.gitignore exists | cat .gitignore | Contains .env on line 1 |
mosquitto.conf exists | ls -la mosquitto.conf | File present |
Common Configuration Mistakes
Mistake 1: Committing.env to Git
The .gitignore1 file prevents this, but if you modify .gitignore, ensure .env remains excluded:
Mistake 2: Missing Token in.env
If .env.sample1 placeholder remains:
CLOUDFLARE_TUNNEL_TOKEN=your_token # WRONG - must be actual token
The cloudflared container will fail to authenticate. Verify token format (JWT-like structure starting with eyJ).
Mistake 3: Incorrect File Permissions
The .env file should be readable by the Docker daemon but protected from other users:
Sources: .gitignore:1-2 .env.sample1
Configuration Validation
Before proceeding to deployment, validate your configuration:
1. Environment Variables
2. Mosquitto Configuration Syntax
The Mosquitto configuration syntax can be pre-validated (requires mosquitto installed locally):
If mosquitto is not installed locally, syntax validation will occur during container startup in Deployment.
3. File Structure Verification
Expected repository structure after configuration:
docker-mqtt-mosquitto-cloudflare-tunnel/
├── .env # Created by you, contains token
├── .env.sample # Repository template
├── .gitignore # Excludes .env
├── docker-compose.yml # Orchestration configuration
├── mosquitto.conf # MQTT broker configuration
└── README.md
Sources: .env.sample1 mosquitto.conf:1-6 .gitignore:1-2
Next Steps
With local configuration complete:
- Environment Variables :
.envfile created with validCLOUDFLARE_TUNNEL_TOKEN - Mosquitto Configuration :
mosquitto.confdefines listeners on ports 1883 and 9001 - Security :
.envexcluded from version control via .gitignore1
Proceed to Deployment to start the containerized services using Docker Compose.
For detailed configuration reference, see Configuration Reference.
For understanding how these configurations map to Docker containers, see Docker Compose Orchestration.
Sources: .env.sample1 mosquitto.conf:1-6 .gitignore:1-2
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Deployment
Loading…
Deployment
Relevant source files
This page covers the process of deploying the Docker MQTT Mosquitto with Cloudflare Tunnel system using Docker Compose. It assumes you have completed the Cloudflare Tunnel setup (see Cloudflare Tunnel Setup) and local configuration (see Local Configuration). For production deployment considerations, see Production Deployment Considerations. For detailed troubleshooting, see Troubleshooting.
Starting the System
With the .env file configured and mosquitto.conf in place, deploy the system using Docker Compose:
This command starts both the mosquitto and cloudflared containers as defined in docker-compose.yml:3-17 The command runs in the foreground, displaying logs from both containers in real-time.
Deployment Modes
| Command | Behavior | Use Case |
|---|---|---|
docker compose up | Foreground mode with logs | Development, debugging, initial testing |
docker compose up -d | Detached mode (background) | Production, long-running deployments |
docker compose up --build | Rebuild images before starting | After configuration changes requiring rebuild |
Sources: README.md:74-78 docker-compose.yml:1-18
Deployment Sequence
The following sequence diagram shows the complete deployment lifecycle from command execution to operational state:
Deployment Initialization Flow
sequenceDiagram
participant User
participant DockerCompose as "docker-compose\n(Orchestrator)"
participant DockerEngine as "Docker Engine"
participant MosqContainer as "mosquitto\n(Container)"
participant CFDContainer as "cloudflared\n(Container)"
participant CFTunnel as "Cloudflare Tunnel\n(Service)"
participant MosqProcess as "Mosquitto Process\n(MQTT Broker)"
User->>DockerCompose: docker compose up
DockerCompose->>DockerEngine: Parse docker-compose.yml
DockerEngine->>DockerEngine: Create bridge network
Note over DockerEngine,MosqContainer: Start mosquitto service
DockerEngine->>MosqContainer: Pull eclipse-mosquitto:latest
DockerEngine->>MosqContainer: Create container
DockerEngine->>MosqContainer: Mount ./mosquitto.conf to /mosquitto/config/
MosqContainer->>MosqProcess: Start mosquitto daemon
MosqProcess->>MosqProcess: Read /mosquitto/config/mosquitto.conf
MosqProcess->>MosqProcess: Bind listener on port 1883
MosqProcess->>MosqProcess: Bind listener on port 9001 (websockets)
MosqProcess-->>DockerEngine: Container running
Note over DockerEngine,CFDContainer: Start cloudflared service
DockerEngine->>CFDContainer: Pull cloudflare/cloudflared:latest
DockerEngine->>CFDContainer: Create container
DockerEngine->>CFDContainer: Inject CLOUDFLARE_TUNNEL_TOKEN from .env
CFDContainer->>CFDContainer: Execute: tunnel --no-autoupdate run --token
CFDContainer->>CFTunnel: Establish tunnel connection
CFTunnel-->>CFDContainer: Tunnel established
CFDContainer->>CFDContainer: Resolve mosquitto via DNS
CFDContainer-->>DockerEngine: Container running
DockerEngine-->>DockerCompose: Both services started
DockerCompose-->>User: Display container logs
Note over User,MosqProcess: System operational
Sources: docker-compose.yml:1-18 README.md:74-78
Container Startup Behavior
Each service defined in docker-compose.yml:3-17 has specific startup characteristics:
mosquitto Container
The mosquitto container starts with the following configuration:
- Image:
eclipse-mosquitto:latest(docker-compose.yml5) - Container Name:
mosquitto(docker-compose.yml6) - Volume Mount:
./mosquitto.conf→/mosquitto/config/mosquitto.conf(docker-compose.yml:7-8) - Restart Policy:
unless-stopped(docker-compose.yml9)
Upon startup, the Mosquitto process reads the mounted configuration file and initializes two listeners as specified in mosquitto.conf.
cloudflared Container
The cloudflared container starts with the following configuration:
- Image:
cloudflare/cloudflared:latest(docker-compose.yml12) - Container Name:
cloudflared(docker-compose.yml13) - Command:
tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}(docker-compose.yml14) - Environment:
CLOUDFLARE_TUNNEL_TOKENinjected from.env(docker-compose.yml:16-17) - Restart Policy:
unless-stopped(docker-compose.yml15)
The --no-autoupdate flag prevents the container from attempting to update itself, maintaining version consistency with the Docker image.
Sources: docker-compose.yml:3-18
Verifying Deployment
After starting the system, verify that both containers are running and the tunnel is operational.
Container Status Verification Flow
graph TD
Start["Deployment Started"]
CheckContainers["docker compose ps"]
VerifyStatus{"Both containers\nSTATUS=Up?"}
CheckLogs["docker compose logs"]
VerifyMosquitto{"Mosquitto logs show\nlisteners bound?"}
VerifyCloudflared{"Cloudflared logs show\ntunnel registered?"}
TestConnection["Test MQTT connection\nto public hostname"]
Success["Deployment Verified"]
Failure["Deployment Failed"]
Start --> CheckContainers
CheckContainers --> VerifyStatus
VerifyStatus -->|No| Failure
VerifyStatus -->|Yes| CheckLogs
CheckLogs --> VerifyMosquitto
VerifyMosquitto -->|No| Failure
VerifyMosquitto -->|Yes| VerifyCloudflared
VerifyCloudflared -->|No| Failure
VerifyCloudflared -->|Yes| TestConnection
TestConnection -->|Connection successful| Success
TestConnection -->|Connection failed| Failure
Sources: README.md:82-84
Check Container Status
List running containers and their status:
Expected output should show both containers with STATUS as Up:
| NAME | IMAGE | STATUS | PORTS |
|---|---|---|---|
| mosquitto | eclipse-mosquitto:latest | Up X minutes | 1883/tcp, 9001/tcp |
| cloudflared | cloudflare/cloudflared:latest | Up X minutes |
Sources: docker-compose.yml:4-17
View Container Logs
View logs from all services:
View logs from a specific service:
Follow logs in real-time:
Expected Mosquitto Logs
Successful Mosquitto startup logs include:
mosquitto | 1234567890: mosquitto version X.X.X starting
mosquitto | 1234567890: Config loaded from /mosquitto/config/mosquitto.conf
mosquitto | 1234567890: Opening ipv4 listen socket on port 1883.
mosquitto | 1234567890: Opening websockets listen socket on port 9001.
mosquitto | 1234567890: mosquitto version X.X.X running
Expected Cloudflared Logs
Successful tunnel connection logs include:
cloudflared | Registered tunnel connection
cloudflared | Connection <UUID> registered connIndex=0
cloudflared | Route propagating
Sources: docker-compose.yml:1-18
Verify Public Accessibility
After deployment, the MQTT broker is accessible at https://<subdomain>.<domain>:443, where the subdomain and domain are configured in the Cloudflare Tunnel public hostname settings (see Cloudflare Tunnel Setup).
Important: The broker is not directly accessible on ports 1883 or 9001 from the public internet. All traffic is routed through Cloudflare’s network on port 443 using HTTPS/WSS protocols. Internal container communication uses HTTP on port 9001.
Sources: README.md:82-84
Container Lifecycle Management
Container State Transitions
Sources: docker-compose.yml:9-15
Stopping the System
Stop all containers while preserving their state:
This sends a SIGTERM signal to each container, allowing graceful shutdown. Containers can be restarted with docker compose start.
Sources: docker-compose.yml:1-18
Removing the Deployment
Stop and remove all containers, networks, and anonymous volumes:
This completely tears down the deployment. The next docker compose up will create fresh containers.
To also remove named volumes (use with caution):
Sources: docker-compose.yml:1-18
Restarting Services
Restart all services:
Restart a specific service:
Sources: docker-compose.yml:4-17
Viewing Live Container Processes
Inspect running processes inside containers:
This displays process information for each running container.
Executing Commands in Containers
Execute a command in a running container:
This opens an interactive shell inside the specified container. Useful for debugging and inspecting the container’s filesystem.
Sources: docker-compose.yml:6-13
Deployment Verification Checklist
Use this checklist to confirm successful deployment:
.envfile exists with validCLOUDFLARE_TUNNEL_TOKENmosquitto.conffile exists in the project rootdocker compose psshows both containers withSTATUS: Updocker compose logs mosquittoshows listeners bound on ports 1883 and 9001docker compose logs cloudflaredshows tunnel registered and connection established- Public hostname resolves to Cloudflare’s network
- MQTT client can connect to
https://<subdomain>.<domain>:443
Sources: README.md:74-84 docker-compose.yml:1-18
Common Deployment Issues
This section provides brief guidance on common deployment failures. For comprehensive troubleshooting, see Troubleshooting.
| Issue | Possible Cause | Quick Resolution |
|---|---|---|
| Container exits immediately | Missing or invalid CLOUDFLARE_TUNNEL_TOKEN | Verify .env file and token validity |
cloudflared fails to start | Token expired or revoked | Regenerate token in Cloudflare dashboard |
mosquitto fails to start | Syntax error in mosquitto.conf | Check configuration file syntax |
| Cannot connect to public URL | Tunnel not registered or hostname misconfigured | Verify public hostname configuration in Cloudflare |
| Port binding errors | Port already in use on host | Containers do not expose ports to host by default; check for conflicting services |
Sources: README.md84 docker-compose.yml:1-18
Restart Policy Behavior
Both containers are configured with restart: unless-stopped (docker-compose.yml:9-15), which provides automatic recovery:
- Container Crash: Automatically restarts
- Docker Daemon Restart: Automatically restarts (unless manually stopped)
- Manual Stop: Does not automatically restart until explicitly started
This policy ensures high availability without manual intervention after transient failures or system reboots.
Sources: docker-compose.yml:9-15
Next Steps
After successful deployment:
- Test MQTT connectivity using an MQTT client (see Testing Connection)
- Review component documentation (see Components)
- Implement monitoring (see Monitoring and Health Checks)
- Consider production hardening (see Production Deployment Considerations)
Sources: README.md:82-84
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Components
Loading…
Components
Relevant source files
This document provides detailed technical documentation of the major components that constitute the Docker MQTT Mosquitto Cloudflare Tunnel system. Each component is examined in terms of its Docker service configuration, runtime behavior, and integration with other system components.
For architectural overview and how these components interact at a system level, see System Architecture. For deployment instructions, see Deployment. For advanced branch-specific features like ACLs and encryption, see Advanced Topics.
Component Overview
The system consists of three primary components orchestrated by Docker Compose:
| Component | Container Name | Image | Primary Function |
|---|---|---|---|
| Mosquitto MQTT Broker | mosquitto | eclipse-mosquitto:latest | MQTT message broker providing pub/sub messaging |
| Cloudflared Tunnel Client | cloudflared | cloudflare/cloudflare:latest | Secure tunnel client connecting broker to Cloudflare network |
| Docker Compose | N/A | N/A | Service orchestration and configuration management |
Component Interaction Diagram
Sources : docker-compose.yml, README.md, mosquitto.conf
Mosquitto MQTT Broker
The Mosquitto service provides MQTT protocol brokering functionality. This component runs the Eclipse Mosquitto broker inside a Docker container, configured for WebSocket and TCP connections with anonymous access enabled.
Service Definition
The Mosquitto service is defined in docker-compose.yml:4-9 with the following configuration:
| Property | Value | Purpose |
|---|---|---|
image | eclipse-mosquitto:latest | Official Mosquitto Docker image |
container_name | mosquitto | Fixed container name for internal DNS resolution |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | Mounts configuration file into container |
restart | unless-stopped | Automatic restart policy for service reliability |
The container name mosquitto is critical as it serves as the internal DNS hostname that cloudflared uses to proxy traffic (referenced in Cloudflare tunnel configuration as mosquitto:9001).
Sources : docker-compose.yml:4-9, README.md:62
graph LR
subgraph MosquittoContainer["mosquitto Container"]
subgraph ListenerConfig["mosquitto.conf Configuration"]
Listener1["listener 1883\nprotocol mqtt"]
Listener2["listener 9001\nprotocol websockets"]
end
subgraph BrokerCore["Mosquitto Broker Process"]
MQTTEngine["MQTT Protocol Engine"]
end
Listener1 -->|standard TCP/IP| MQTTEngine
Listener2 -->|WebSocket framing| MQTTEngine
end
subgraph ExternalAccess["External Access Paths"]
TCPClient["TCP MQTT Clients\n(port 1883)"]
WSClient["WebSocket Clients\n(via cloudflared proxy)"]
end
TCPClient -.->|internal network only not exposed via tunnel| Listener1
WSClient -->|proxied through cloudflared:mosquitto:9001| Listener2
MQTT Listeners
The Mosquitto broker is configured with two distinct listeners, each serving different client connection patterns:
Listener Configuration Overview
Sources : mosquitto.conf, README.md:62, docker-compose.yml:4-9
Standard TCP Listener (Port 1883)
Configured in mosquitto.conf as:
listener 1883
protocol mqtt
This listener provides standard MQTT-over-TCP connectivity on the default MQTT port. It is accessible within the Docker internal network but is not exposed through the Cloudflare tunnel in the default configuration.
Use cases :
- Direct connections from other Docker containers on the same network
- Internal service-to-service MQTT communication
- Local development and testing
WebSocket Listener (Port 9001)
Configured in mosquitto.conf as:
listener 9001
protocol websockets
This listener provides MQTT-over-WebSocket connectivity, which is the primary listener exposed through the Cloudflare tunnel. The cloudflared service proxies external traffic to mosquitto:9001 as configured in the Cloudflare Zero Trust dashboard.
Use cases :
- Browser-based MQTT clients
- Web applications requiring MQTT connectivity
- Clients behind restrictive firewalls that block non-HTTP protocols
- The primary access method for external clients via Cloudflare tunnel
Sources : mosquitto.conf, README.md:55-66
Anonymous Access
The Mosquitto broker is configured with anonymous access enabled, meaning no authentication is required to connect or publish/subscribe to topics.
Configuration :
allow_anonymous true
This setting in mosquitto.conf permits any client that can reach the broker to perform MQTT operations without credentials.
Security implications :
| Aspect | Implication |
|---|---|
| Authentication | None - all clients are accepted |
| Authorization | No topic-level access control in main branch |
| Trust model | Broker trusts all traffic from cloudflared |
| Network security | Security relies entirely on Cloudflare tunnel and network isolation |
The security model assumes that network-level access control (via Cloudflare tunnel) provides sufficient protection. For environments requiring topic-level access control, see Topic Access Control (ACL) which documents the protected-no-wildcard branch implementation.
Sources : mosquitto.conf, README.md:5-11
Cloudflared Tunnel Service
The cloudflared service establishes a secure, outbound-only connection to Cloudflare’s network, enabling external MQTT clients to reach the internal Mosquitto broker without exposing inbound ports on the host.
Service Definition
The cloudflared service is defined in docker-compose.yml:11-17:
| Property | Value | Purpose |
|---|---|---|
image | cloudflare/cloudflared:latest | Official Cloudflare tunnel client image |
container_name | cloudflared | Fixed container name |
command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Starts tunnel with authentication token |
restart | unless-stopped | Automatic restart policy |
environment | CLOUDFLARE_TUNNEL_TOKEN | Passes tunnel authentication token from .env file |
Sources : docker-compose.yml:11-17
Tunnel Authentication and Establishment
Tunnel Connection Flow
Sources : docker-compose.yml:11-17, .env.sample, README.md:47-53
Command Line Arguments
The command directive in docker-compose.yml14 specifies:
tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}
| Argument | Purpose |
|---|---|
tunnel | Primary cloudflared operation mode |
--no-autoupdate | Disables automatic updates (container image updates handled via Docker) |
run | Runs the tunnel in persistent mode |
--token | Authenticates with Cloudflare using provided token |
${CLOUDFLARE_TUNNEL_TOKEN} | Environment variable interpolation from .env file |
The token is obtained during tunnel creation in the Cloudflare Zero Trust dashboard (see Cloudflare Tunnel Configuration) and stored in the .env file as documented in .env.sample
Sources : docker-compose.yml:14, .env.sample, README.md:47-53
Traffic Proxying
Once the tunnel is established, cloudflared proxies incoming traffic from Cloudflare’s edge to the internal Mosquitto broker. The routing configuration is managed in the Cloudflare Zero Trust dashboard, not in local configuration files.
The public hostname configuration in Cloudflare maps to the service URL mosquitto:9001, where:
mosquittoresolves via Docker’s internal DNS to the container withcontainer_name: mosquitto- Port
9001corresponds to the WebSocket listener configured in mosquitto.conf
Sources : README.md:55-66, docker-compose.yml
Docker Compose Orchestration
Docker Compose provides service orchestration, dependency management, and configuration binding for the system.
Service Orchestration Model
Docker Compose Component Relationships
Sources : docker-compose.yml
Network Configuration
Docker Compose creates a default bridge network for service communication. The network configuration is implicit (no explicit networks section in docker-compose.yml).
Network characteristics :
| Aspect | Behavior |
|---|---|
| Network type | Bridge network (default) |
| Network name | {project_name}_default (typically docker-mqtt-mosquitto-cloudflare-tunnel_default) |
| DNS resolution | Container names resolve to container IPs (mosquitto → container IP) |
| Inter-service communication | All services can reach each other using container names |
| External connectivity | Services can initiate outbound connections (used by cloudflared) |
The internal DNS resolution is critical: when the Cloudflare tunnel configuration specifies mosquitto:9001, Docker’s embedded DNS server resolves mosquitto to the IP address of the container with container_name: mosquitto.
Sources : docker-compose.yml, README.md:62
Volume Management
The Mosquitto service uses a bind mount to inject configuration:
Volume mapping details :
| Source (Host) | Target (Container) | Mount Type | Purpose |
|---|---|---|---|
./mosquitto.conf | /mosquitto/config/mosquitto.conf | Bind mount | Provides broker configuration at runtime |
The configuration file is read by the Mosquitto broker on startup. Changes to mosquitto.conf require container restart to take effect:
No data persistence volumes are configured in the default setup. For persistent message storage, see Advanced Topics and the protected-no-wildcard branch documentation.
Sources : docker-compose.yml:7-8, mosquitto.conf
Environment Variable Injection
The cloudflared service receives environment variables via docker-compose.yml:16-17:
This syntax instructs Docker Compose to:
- Read
CLOUDFLARE_TUNNEL_TOKENfrom the.envfile in the same directory - Pass it as an environment variable to the
cloudflaredcontainer - Make it available for substitution in the
commanddirective via${CLOUDFLARE_TUNNEL_TOKEN}
The .env file is not version-controlled (excluded via .gitignore). Developers create it from .env.sample as documented in Environment Variables.
Sources : docker-compose.yml:16-17, .env.sample
Restart Policies
Both services use restart: unless-stopped:
Restart behavior :
| Scenario | Behavior |
|---|---|
| Container exits with error | Automatically restarts |
| Container exits cleanly (exit 0) | Automatically restarts |
| Docker daemon restarts | Containers restart automatically |
Manual docker stop | Container remains stopped after Docker daemon restart |
| System reboot | Containers restart if Docker daemon starts automatically |
This policy ensures service availability while allowing manual control via docker compose down or docker stop.
Sources : docker-compose.yml:9,15
Service Lifecycle Management
Common Docker Compose Commands :
| Command | Effect |
|---|---|
docker compose up | Creates and starts both services in foreground |
docker compose up -d | Creates and starts both services in background (detached) |
docker compose down | Stops and removes containers, networks |
docker compose restart | Restarts all services |
docker compose restart mosquitto | Restarts only Mosquitto service |
docker compose logs | Shows logs from all services |
docker compose logs -f cloudflared | Follows logs from cloudflared service |
For detailed deployment procedures, see Deployment.
Sources : docker-compose.yml, README.md:68-72
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
MQTT Protocol Listeners
Loading…
MQTT Protocol Listeners
Relevant source files
Purpose and Scope
This document describes the MQTT listener configuration in the Mosquitto broker, specifically the two listeners defined in mosquitto.conf:1-6 It explains the purpose, protocol, and routing behavior of each listener. For broader Mosquitto broker configuration, see Mosquitto Configuration. For access control settings, see Anonymous Access. For Cloudflare tunnel routing configuration, see Cloudflared Tunnel Service.
Sources : mosquitto.conf, README.md
Listener Configuration Overview
The Mosquitto broker is configured with two distinct listeners, each serving different transport protocols and use cases. Both listeners are defined in mosquitto.conf:1-6 and operate simultaneously within the mosquitto container.
<old_str>
TCP Listener"] end
subgraph external["External Network"]
Internet["Public Internet"]
end
OtherContainer -->|"Direct MQTT/TCP"| MosquittoPort1883
Internet -.->|"Not Accessible"| MosquittoPort1883
**Listener Configuration Structure**
| Listener | Port | Protocol | Anonymous Access | Primary Use Case |
|----------|------|----------|------------------|------------------|
| <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1" min=1 file-path="mosquitto.conf">Hii</FileRef> | 1883 | Standard MQTT/TCP | Enabled globally <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2" min=2 file-path="mosquitto.conf">Hii</FileRef> | Direct MQTT client connections |
| <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L5" min=4 max=5 file-path="mosquitto.conf">Hii</FileRef> | 9001 | MQTT over WebSockets | Enabled globally <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2" min=2 file-path="mosquitto.conf">Hii</FileRef> | Browser-based clients, Cloudflare tunnel |
**Sources**: mosquitto.conf
---
## Listener 1883: Standard MQTT TCP
The first listener, configured at <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1" min=1 file-path="mosquitto.conf">Hii</FileRef> binds to port 1883 and provides standard MQTT protocol over TCP. This is the default MQTT port and is used by most native MQTT client libraries.
### Configuration Directives
listener 1883 allow_anonymous true
**Directive Breakdown**:
- `listener 1883` <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1" min=1 file-path="mosquitto.conf">Hii</FileRef>: Binds the Mosquitto broker to TCP port 1883
- `allow_anonymous true` <FileRef file-url="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2" min=2 file-path="mosquitto.conf">Hii</FileRef>: Applies globally to all listeners, allowing clients to connect without authentication
### Protocol Characteristics
This listener implements the standard MQTT 3.1.1 and MQTT 5.0 protocols over raw TCP connections. It supports:
- Binary MQTT protocol framing
- Persistent TCP connections
- Quality of Service (QoS) levels 0, 1, and 2
- Last Will and Testament (LWT) messages
- Retained messages
### Network Accessibility
The 1883 listener is **not** directly exposed through the Cloudflare tunnel in the current configuration. It operates only within Docker's internal network, accessible to other containers in the same Docker Compose stack.
```mermaid
graph LR
subgraph "Docker Internal Network"
OtherContainer["Other Docker Container"]
MosquittoPort1883["mosquitto:1883<br/>TCP Listener"]
end
subgraph "External Network"
Internet["Public Internet"]
end
OtherContainer -->|"Direct MQTT/TCP"| MosquittoPort1883
Internet -.->|"Not Accessible"| MosquittoPort1883
**Sources** : mosquitto.conf, README.md
* * *
## Listener 9001: MQTT over WebSockets
The second listener, configured at [mosquitto.conf:4-5](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L5) binds to port 9001 and provides MQTT protocol encapsulated over WebSockets. This is the primary listener exposed to external clients via the Cloudflare tunnel.
### Configuration Directives
listener 9001
protocol websockets
**Directive Breakdown** :
* `listener 9001` [mosquitto.conf4](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L4): Binds the Mosquitto broker to TCP port 9001
* `protocol websockets` [mosquitto.conf5](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L5-L5): Configures this listener to accept WebSocket connections with MQTT payloads
### Protocol Characteristics
This listener encapsulates MQTT protocol messages within WebSocket frames, enabling:
* HTTP/WebSocket upgrade handshake
* Browser-based MQTT clients (JavaScript)
* Compatibility with web proxies and CDNs
* TLS/SSL termination at the proxy layer (Cloudflare)
The WebSocket protocol wrapper does not alter the MQTT protocol itself; it only provides an HTTP-compatible transport layer.
### Cloudflare Tunnel Routing
The 9001 listener is configured as the target for the Cloudflare tunnel in the Zero Trust dashboard. The tunnel configuration specifies `mosquitto:9001` as the service URL [README.md62](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/README.md#L62-L62) routing external MQTT WebSocket connections through the `cloudflared` container to this listener.
### Network Accessibility
Unlike the 1883 listener, the 9001 listener is indirectly exposed to the public internet through the Cloudflare tunnel. The routing path is:
1. External client connects to Cloudflare public hostname
2. Cloudflare Edge routes to `cloudflared` container via tunnel
3. `cloudflared` proxies HTTP traffic to `mosquitto:9001`
4. Mosquitto 9001 listener accepts WebSocket connection
**Sources** : mosquitto.conf, README.md
* * *
## Traffic Routing Architecture
The following diagram illustrates how traffic is routed to each listener based on the source and transport protocol:
**Routing Configuration References** :
* Cloudflare tunnel target: `mosquitto:9001` [README.md62](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/README.md#L62-L62)
* WebSocket listener: [mosquitto.conf:4-5](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L5)
* TCP listener: [mosquitto.conf1](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1)
* Container name resolution: `mosquitto` from `docker-compose.yml`
**Sources** : mosquitto.conf, README.md
* * *
## Configuration Reference
### Listener Directive Syntax
The `listener` directive defines a network interface and port for the broker to bind to:
listener <port> [bind_address]
**Current Configuration** :
* [mosquitto.conf1](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1): `listener 1883` \- Binds to port 1883 on all interfaces (no bind_address specified)
* [mosquitto.conf4](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L4): `listener 9001` \- Binds to port 9001 on all interfaces
### Protocol Directive
The `protocol` directive specifies the transport protocol for a listener:
protocol <mqtt|websockets>
**Current Configuration** :
* Listener 1883: No protocol directive (defaults to `mqtt`)
* [mosquitto.conf5](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L5-L5): `protocol websockets` \- Configures listener 9001 for WebSocket transport
### Anonymous Access Directive
The `allow_anonymous` directive controls whether clients can connect without authentication:
allow_anonymous <true|false>
**Current Configuration** :
* [mosquitto.conf2](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2): `allow_anonymous true` \- Applies globally to all listeners
This directive appears once and affects both listeners. For production deployments requiring authentication, this should be set to `false` and appropriate authentication mechanisms configured. See [Anonymous Access](2-1-2-authentication-and-access-control.md) for detailed security implications.
### Configuration Interaction Matrix
Configuration Aspect| Listener 1883| Listener 9001
---|---|---
**Port**| 1883 [mosquitto.conf1](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L1-L1)| 9001 [mosquitto.conf4](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L4-L4)
**Protocol**| MQTT/TCP (implicit default)| WebSockets [mosquitto.conf5](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L5-L5)
**Anonymous Access**| Enabled [mosquitto.conf2](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2)| Enabled [mosquitto.conf2](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/mosquitto.conf#L2-L2)
**Cloudflare Routing**| Not configured| Configured [README.md62](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/README.md#L62-L62)
**External Accessibility**| Docker internal only| Via Cloudflare tunnel
**Sources** : mosquitto.conf, README.md
* * *
## Use Case Selection
### When to Use Listener 1883 (TCP)
* **Native MQTT client libraries** : Python Paho, Mosquitto C library, MQTT.js with TCP
* **High-performance IoT devices** : Minimal protocol overhead
* **Internal Docker services** : Direct container-to-container communication
* **Legacy MQTT applications** : Standard MQTT port compatibility
### When to Use Listener 9001 (WebSockets)
* **Browser-based clients** : JavaScript MQTT clients in web applications
* **Cloudflare tunnel access** : External clients connecting via the public hostname
* **Firewall-restricted networks** : HTTP/WebSocket traffic often allowed through corporate firewalls
* **CDN/proxy compatibility** : Works with HTTP-based reverse proxies
### Current System Configuration
The current deployment configures the Cloudflare tunnel to route exclusively to listener 9001. This means:
* External clients **must** use MQTT over WebSockets
* The public hostname [README.md60](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/README.md#L60-L60) resolves to listener 9001
* Listener 1883 remains available only for internal Docker network access
To expose listener 1883 externally, additional Cloudflare tunnel configuration would be required, though this is uncommon since WebSocket transport provides broader compatibility.
**Sources** : mosquitto.conf, README.md
Dismiss
Refresh this wiki
Enter email to refresh
<hr style="margin-top: 3rem; border: none; border-top: 1px solid #e0e0e0;">
<div class="doc-footer" style="text-align: center; padding: 1.5rem 0; color: #666">
<p style="margin: 0;">Documentation generated on January 04, 2026 at 00:40 UTC</p>
<p style="margin: 0.5rem 0 0 0;">
<a href="https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel" rel="noopener noreferrer" style="color: #667eea; text-decoration: none;">jzombie/docker-mqtt-mosquitto-cloudflare-tunnel</a>
</p>
</div>
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Authentication and Access Control
Loading…
Authentication and Access Control
Relevant source files
This document explains the authentication and access control mechanisms available for the Mosquitto MQTT broker. The current configuration uses anonymous access, allowing any client that can reach the broker to connect without credentials. This page covers the implementation details, security implications, and alternative authentication strategies.
The Mosquitto broker supports multiple authentication methods:
- Anonymous Access : No credentials required (current configuration)
- Username/Password Authentication : Credential-based access control with password files
- Access Control Lists (ACLs) : Topic-level permissions based on usernames
- Plugin-Based Authentication : Custom authentication mechanisms via plugins
For the complete system security architecture, see Security Model. For advanced authentication configurations, see Protected Branch Features.
Current Configuration: Anonymous Access
The Mosquitto broker in this repository is configured for anonymous access, meaning no authentication is required for client connections. This is controlled by a single directive in the configuration file.
Configuration Location
The anonymous access setting is defined in mosquitto.conf2:
allow_anonymous true
This directive applies globally to all listeners defined in the configuration file. In this system, anonymous access affects both the standard MQTT listener on port 1883 and the WebSocket listener on port 9001 (see MQTT Protocol Listeners for details on these listeners).
Whatallow_anonymous true Means
| Configuration | Behavior |
|---|---|
allow_anonymous true | Clients can connect without username/password |
allow_anonymous false | Clients must provide valid credentials |
| (directive omitted) | Defaults to false in Mosquitto 2.0+ |
Sources: mosquitto.conf
Anonymous Access Connection Flow
Connection Flow Diagram
When allow_anonymous true is set in mosquitto.conf2 the Mosquitto broker accepts MQTT CONNECT packets regardless of whether they contain username/password credentials. The broker does not perform any authentication checks and immediately grants access to all MQTT operations.
Authentication Decision Logic
| MQTT CONNECT Contains | Credentials Checked? | Connection Result |
|---|---|---|
| Username + Password | No | Accepted |
| Username only | No | Accepted |
| No credentials | No | Accepted |
Sources: mosquitto.conf, README.md
Access Permissions with Anonymous Access
Unrestricted Topic Access
With allow_anonymous true and no ACL file configured in mosquitto.conf clients have unrestricted access:
| Permission Type | Anonymous Client Capabilities |
|---|---|
| SUBSCRIBE | Can subscribe to any topic, including wildcards (#, +) |
| PUBLISH | Can publish to any topic |
| RETAIN | Can set retained messages on any topic |
| QoS Levels | Full access to QoS 0, 1, and 2 |
| Topic Restrictions | None - all topics accessible |
| Will Messages | Can set last-will-and-testament messages |
Authentication Check Flow
Sources: mosquitto.conf
Security Implications
Trust Boundary Analysis
The security model for this configuration relies entirely on network-level access control rather than application-level authentication:
| Security Layer | Protection Provided | Configuration |
|---|---|---|
| Cloudflare Edge | DDoS protection, traffic filtering | Cloudflare Dashboard |
| Cloudflare Tunnel | Encrypted transport, no inbound ports | CLOUDFLARE_TUNNEL_TOKEN |
| Docker Network | Internal isolation between containers | docker-compose.yml |
| Mosquitto Authentication | None - anonymous access enabled | allow_anonymous: true |
Sources: mosquitto.conf, README.md
Threat Model Considerations
What Anonymous Access Protects Against:
- Direct internet exposure (mitigated by Cloudflare Tunnel)
- Port scanning and direct connection attempts (no inbound firewall rules required)
- DDoS attacks at the network level (handled by Cloudflare Edge)
What Anonymous Access Does NOT Protect Against:
- Unauthorized message publication by any client that reaches the broker
- Eavesdropping on topics by any connected client
- Malicious clients subscribing to all topics using
#wildcard - Topic flooding or message spam from authenticated clients
- Multi-tenant isolation (all clients can access all topics)
Sources: README.md
Appropriate Use Cases
When Anonymous Access Is Suitable
Anonymous access is appropriate in the following scenarios:
| Scenario | Rationale |
|---|---|
| Single-User Systems | One person controls all MQTT clients and traffic |
| Private/Home Networks | Network-level security already restricts access |
| Trusted Development Environments | Quick setup for testing without credential management |
| Internal IoT Networks | All devices within a trusted network boundary |
| Proof-of-Concept Deployments | Rapid prototyping without authentication complexity |
When Authentication Is Required
Authentication should be implemented when:
- Multiple users need isolated topic spaces
- Different clients require different permission levels
- Production deployments with untrusted client access
- Compliance or audit requirements mandate access control
- Topic-level security policies need enforcement
- Multi-tenant systems require user isolation
For these scenarios, see the alternative implementation in the [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) which provides username/password authentication and ACL-based topic restrictions. Detailed documentation is available at Protected Branch Features.
Sources: README.md
Configuration File Structure
The complete Mosquitto configuration from mosquitto.conf:1-5:
listener 1883
allow_anonymous true
listener 9001
protocol websockets
Configuration Analysis
| Line | Directive | Effect | Scope |
|---|---|---|---|
| 1 | listener 1883 | Defines standard MQTT listener | Port 1883 |
| 2 | allow_anonymous true | Enables anonymous access | Global (all listeners) |
| 3 | (blank) | Line separator | - |
| 4 | listener 9001 | Defines WebSocket listener | Port 9001 |
| 5 | protocol websockets | Specifies WebSocket protocol | Port 9001 only |
Key Observations
- The
allow_anonymous truedirective applies globally to both listeners - No
password_filedirective is present - No
acl_filedirective is present - No
allow_anonymousoverride on individual listeners - This creates a uniform anonymous access policy across MQTT and WebSocket protocols
Sources: mosquitto.conf
graph TB
ComposeFile["docker-compose.yml"]
MosqConf["mosquitto.conf"]
MosqContainer["mosquitto Container\n(eclipse-mosquitto:latest)"]
subgraph "Configuration Flow"
ComposeFile -->|Defines service| MosqContainer
MosqConf -->|Volume mount ./mosquitto.conf:/mosquitto/config/mosquitto.conf| MosqContainer
end
subgraph "Anonymous Access Config"
Line2["Line 2: allow_anonymous true"]
NoACL["No aclfile directive"]
NoPwdFile["No password_file directive"]
end
MosqConf --> Line2
MosqConf --> NoACL
MosqConf --> NoPwdFile
subgraph "Runtime Behavior"
AllowAll["All connections accepted"]
NoCredCheck["No credential validation"]
FullTopicAccess["Full topic access for all clients"]
end
Line2 --> AllowAll
NoACL --> FullTopicAccess
NoPwdFile --> NoCredCheck
Relationship to System Components
Container Configuration
The mosquitto service in docker-compose.yml mounts the configuration file as a volume:
This volume mount makes the allow_anonymous true setting effective when the container starts. The Mosquitto process reads this configuration file on startup and applies the anonymous access policy to all listeners.
Sources: mosquitto.conf, docker-compose.yml (referenced in diagrams)
Alternative Authentication Configurations
Username/Password Authentication
To enable credential-based authentication, modify mosquitto.conf to include:
allow_anonymous false
password_file /mosquitto/config/passwordfile
Create the password file using the mosquitto_passwd utility:
Authentication Flow with Credentials
| Configuration | Client Connect Behavior | Result |
|---|---|---|
allow_anonymous false | ||
password_file set | CONNECT without credentials | Connection rejected (CONNACK with return code 5) |
allow_anonymous false | ||
password_file set | CONNECT with valid credentials | Connection accepted |
allow_anonymous false | ||
password_file set | CONNECT with invalid credentials | Connection rejected (CONNACK with return code 4) |
Access Control Lists (ACLs)
ACLs provide topic-level permission control based on usernames. Add to mosquitto.conf:
acl_file /mosquitto/config/aclfile
ACL File Example
# User 'alice' can read/write to alice/* topics
user alice
topic readwrite alice/#
# User 'bob' can only read bob/* topics
user bob
topic read bob/#
Protected Branch Example
The [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) demonstrates a complete authentication implementation:
| Feature | Main Branch | Protected Branch |
|---|---|---|
| Authentication | Anonymous | Username/password required |
| Topic Access | Unrestricted | ACL-based restrictions |
| Wildcard Subscriptions | Allowed | Restricted to user’s namespace |
| Configuration Files | mosquitto.conf only | mosquitto.conf + ACL file + password file |
| Retained Messages | Unencrypted | Encrypted with gocryptfs |
For implementation details, see Protected Branch Features.
Sources: README.md
Testing Anonymous Access
The CI/CD pipeline validates that the Mosquitto broker starts successfully with anonymous access enabled. The health check in .github/workflows/ci.yml verifies the container reaches a running state but does not test authentication behavior since none is required.
Testing Approach
To manually verify anonymous access behavior:
- Start the services:
docker compose up - Connect any MQTT client to the public hostname configured in Cloudflare
- Attempt to connect without providing credentials
- Verify successful connection and message publication
No username or password should be required for successful connection and full MQTT operation.
Sources: .github/workflows/ci.yml (referenced), README.md
Migration Path to Authentication
If anonymous access proves insufficient for security requirements, the system can be migrated to authenticated access by:
- Creating a password file with
mosquitto_passwdcommand - Adding
password_file /path/to/password_filedirective to mosquitto.conf - Removing or changing
allow_anonymous truetoallow_anonymous false - Optionally adding an ACL file for topic-level restrictions
- Updating client code to provide credentials in CONNECT packets
The [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) provides a complete reference implementation. View the [configuration differences](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/configuration differences) to understand required changes.
For production deployments requiring authentication, see Production Considerations and Topic Access Control (ACL).
Sources: README.md
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Mosquitto MQTT Broker
Loading…
Mosquitto MQTT Broker
Relevant source files
Purpose and Scope
This document provides in-depth technical documentation of the mosquitto service within the Docker MQTT Mosquitto Cloudflare Tunnel system. It covers the Docker service configuration, container image specification, volume mount strategy, network integration, and runtime characteristics of the MQTT broker component.
For detailed information about the specific MQTT listener configurations (ports 1883 and 9001), see MQTT Listeners. For security implications and configuration of anonymous client access, see Anonymous Access. For the cloudflared service that proxies external traffic to this broker, see Cloudflared Tunnel Service.
Sources: README.md15 docker-compose.yml:4-9
Overview
The mosquitto service is the core MQTT message broker in this system, responsible for accepting MQTT client connections, routing published messages to subscribers, and maintaining message state. It runs as a containerized instance of Eclipse Mosquitto, an open-source implementation of the MQTT protocol versions 5.0, 3.1.1, and 3.1.
Within the system architecture, the broker receives traffic exclusively through the cloudflared container, which proxies external MQTT connections from Cloudflare’s tunnel to the broker’s WebSocket listener on port 9001. The broker is never directly exposed to the internet, relying on Cloudflare Tunnel for secure external access.
Sources: README.md15 docker-compose.yml:4-9 Diagram 1 from system overview
Docker Service Configuration
Service Definition
The mosquitto service is defined in docker-compose.yml:4-9 as follows:
| Property | Value | Purpose |
|---|---|---|
image | eclipse-mosquitto:latest | Container image specification |
container_name | mosquitto | Fixed container name for DNS resolution |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | Configuration file mount |
restart | unless-stopped | Automatic restart policy |
The service definition is minimal by design, relying on external configuration through the volume-mounted mosquitto.conf file rather than environment variables or command-line arguments.
Sources: docker-compose.yml:4-9
Container Image Strategy
The service uses the eclipse-mosquitto:latest image tag from Docker Hub’s official Eclipse Mosquitto repository. This tag automatically pulls the most recent stable version of Mosquitto when the container is created or updated. The latest tag strategy means:
- New deployments receive the current stable version
- Existing deployments remain on their pulled version until explicitly updated
- No version pinning provides automatic access to bug fixes and features
- Manual intervention required to pull updated images (
docker compose pull)
For production deployments where version stability is critical, consider pinning to a specific version tag (e.g., eclipse-mosquitto:2.0.18) instead of latest.
Sources: docker-compose.yml5
Configuration File Management
Volume Mount Architecture
Diagram: Configuration File Volume Mount Flow
The configuration file is mounted from the host filesystem into the container at /mosquitto/config/mosquitto.conf. The Mosquitto process reads this file during container startup to determine listener configurations, authentication settings, and protocol options.
Sources: docker-compose.yml:7-8 mosquitto.conf:1-6
Configuration File Contents
The mosquitto.conf:1-6 file defines the broker’s operational parameters:
listener 1883
allow_anonymous true
listener 9001
protocol websockets
This configuration establishes two distinct listeners with different protocols and purposes. The configuration is intentionally minimal, containing only the essential settings for listener setup and anonymous access control. The file is version-controlled and identical across all deployments, with no host-specific or secret values.
Changes to mosquitto.conf require a container restart to take effect, as Mosquitto reads the configuration only during startup.
Sources: mosquitto.conf:1-6 docker-compose.yml:7-8
Network Architecture
Docker Network Integration
Diagram: Service Network Connectivity
The mosquitto service participates in Docker Compose’s default bridge network, which provides automatic DNS resolution. The fixed container_name: mosquitto enables the cloudflared container to reference it by hostname when configuring the tunnel’s backend service (configured in Cloudflare Dashboard as mosquitto:9001).
No ports are exposed to the host machine in docker-compose.yml:4-9 meaning:
- Port 1883 is accessible only within the Docker network
- Port 9001 is accessible only within the Docker network
- External access occurs exclusively through the cloudflared tunnel proxy
- No inbound firewall rules are required on the host
Sources: docker-compose.yml:4-9 README.md62
Service-to-Service Communication
Diagram: Internal Service Communication Flow
The cloudflared container resolves the mosquitto hostname using Docker’s internal DNS, which maps the container_name to the container’s IP address on the bridge network. This DNS-based service discovery eliminates the need for hardcoded IP addresses or external service registries.
Sources: README.md62 docker-compose.yml:4-9
Runtime Characteristics
Container Lifecycle
The restart: unless-stopped policy in docker-compose.yml9 configures automatic container restart behavior:
| Event | Container Behavior |
|---|---|
| Container crash | Automatically restarts |
| Docker daemon restart | Automatically restarts |
Manual docker stop | Remains stopped |
Manual docker compose down | Removed (not restarted) |
| System reboot | Automatically restarts if Docker daemon starts |
This policy ensures high availability while allowing manual intervention when needed. The broker restarts automatically after transient failures but respects explicit stop commands from administrators.
Sources: docker-compose.yml9
Data Persistence
The main branch configuration does not define any volume mounts for persistent data storage. This means:
- Message persistence is disabled (all messages are in-memory only)
- Retained messages are lost on container restart
- Client session state is not persisted
- QoS 1 and QoS 2 message queues are cleared on restart
For persistent message storage, the [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) implements encrypted retained message storage. See Encrypted Retained Messages for details on that advanced configuration.
Sources: docker-compose.yml:4-9 README.md:5-11
Process Architecture
Diagram: Container Process Architecture
The Mosquitto broker runs as PID 1 within its container, reading configuration from the mounted mosquitto.conf file and establishing both listener sockets. The process handles all MQTT client connections, message routing, and protocol state management as a single-threaded event loop.
Sources: docker-compose.yml:4-9 mosquitto.conf:1-6
Integration with System Components
Relationship to Cloudflared Service
The mosquitto service operates in coordination with the cloudflared service defined in docker-compose.yml:11-17 The cloudflared tunnel configuration (managed in the Cloudflare Dashboard) specifies mosquitto:9001 as the backend service URL, establishing the proxy relationship:
- Cloudflared container resolves
mosquittohostname via Docker DNS - Cloudflared establishes HTTP connection to mosquitto container port 9001
- Cloudflared proxies WebSocket upgrade requests to mosquitto
- Mosquitto accepts WebSocket connection and initiates MQTT protocol
This architecture keeps the mosquitto container completely isolated from direct internet access while maintaining full MQTT functionality through the tunnel proxy.
Sources: README.md62 docker-compose.yml:11-17
CI/CD Testing Integration
The GitHub Actions workflow in .github/workflows/ci.yml tests the mosquitto service health during continuous integration:
The CI pipeline verifies that the mosquitto container reaches a running state, validating that:
- The
eclipse-mosquitto:latestimage can be pulled - The container starts without configuration errors
- The mosquitto process initializes successfully
- The mounted configuration file is valid
For complete CI/CD pipeline documentation, see CI/CD Pipeline.
Sources: .github/workflows/ci.yml docker-compose.yml:4-9
Configuration Reference Summary
| Configuration Aspect | Location | Value |
|---|---|---|
| Service name | docker-compose.yml:4 | mosquitto |
| Container name | docker-compose.yml:6 | mosquitto |
| Image | docker-compose.yml:5 | eclipse-mosquitto:latest |
| Configuration file (host) | docker-compose.yml:8 | ./mosquitto.conf |
| Configuration file (container) | docker-compose.yml:8 | /mosquitto/config/mosquitto.conf |
| Restart policy | docker-compose.yml:9 | unless-stopped |
| Standard MQTT listener | mosquitto.conf:1 | Port 1883 |
| WebSocket listener | mosquitto.conf:4 | Port 9001 |
| Anonymous access | mosquitto.conf:2 | true |
| Data persistence | N/A | None (in-memory only) |
Sources: docker-compose.yml:4-9 mosquitto.conf:1-6
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Cloudflared Tunnel Connector
Loading…
Cloudflared Tunnel Connector
Relevant source files
Purpose and Scope
This document details the cloudflared container component, which acts as the secure tunnel connector between the Cloudflare network and the local Mosquitto MQTT broker. It explains the container’s configuration, how it authenticates with Cloudflare’s infrastructure, how it establishes and maintains the tunnel connection, and how it routes incoming traffic to the Mosquitto service.
For broader system architecture context, see System Architecture. For Docker Compose orchestration details, see Docker Compose Orchestration. For environment variable configuration, see Environment Variables.
Container Overview
The cloudflared service is defined in docker-compose.yml:11-17 and runs the official Cloudflare Tunnel connector image. This container establishes an outbound-only persistent connection to Cloudflare’s network, eliminating the need for open inbound ports on the host system.
Container Specification
| Property | Value | Purpose |
|---|---|---|
| Service Name | cloudflared | Docker Compose service identifier |
| Container Name | cloudflared | Runtime container name |
| Image | cloudflare/cloudflared:latest | Official Cloudflare tunnel connector |
| Command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Tunnel execution with token authentication |
| Restart Policy | unless-stopped | Automatic restart except on manual stop |
| Environment Variables | CLOUDFLARE_TUNNEL_TOKEN | Tunnel authentication credential |
Sources: docker-compose.yml:11-17
Container Configuration Architecture
The following diagram maps the Docker Compose configuration elements to their runtime purposes:
Sources: docker-compose.yml:11-17
graph TB
subgraph "docker-compose.yml Definition"
SERVICE["services.cloudflared"]
IMAGE["image: cloudflare/cloudflared:latest"]
CONTAINER["container_name: cloudflared"]
COMMAND["command: tunnel --no-autoupdate run --token"]
RESTART["restart: unless-stopped"]
ENV_SECTION["environment:\n- CLOUDFLARE_TUNNEL_TOKEN"]
end
subgraph "Runtime Configuration"
TOKEN_VAR["${CLOUDFLARE_TUNNEL_TOKEN}\nfrom .env file"]
CMD_EXEC["Executed Command:\ntunnel --no-autoupdate run --token <actual_token>"]
end
subgraph "Operational Behavior"
CONTAINER_PROC["cloudflared Process"]
AUTO_RESTART["Automatic Restart\non Failure"]
NO_UPDATE["Update Mechanism\nDisabled"]
end
SERVICE --> IMAGE
SERVICE --> CONTAINER
SERVICE --> COMMAND
SERVICE --> RESTART
SERVICE --> ENV_SECTION
ENV_SECTION --> TOKEN_VAR
COMMAND --> TOKEN_VAR
TOKEN_VAR --> CMD_EXEC
CMD_EXEC --> CONTAINER_PROC
RESTART --> AUTO_RESTART
COMMAND --> NO_UPDATE
CONTAINER_PROC --> NO_UPDATE
CONTAINER_PROC --> AUTO_RESTART
Tunnel Authentication
The cloudflared connector authenticates with Cloudflare’s infrastructure using a cryptographically secure tunnel token. This token is generated during the Cloudflare Tunnel creation process and links the local connector to a specific tunnel configuration on Cloudflare’s network.
sequenceDiagram
participant User as "Administrator"
participant CF_UI as "Cloudflare Dashboard"
participant Env as ".env File"
participant Compose as "docker-compose"
participant CFD as "cloudflared Container"
participant CF_Net as "Cloudflare Network"
User->>CF_UI: Create tunnel via Zero Trust dashboard
CF_UI->>CF_UI: Generate CLOUDFLARE_TUNNEL_TOKEN
CF_UI-->>User: Display token in connector setup
User->>Env: Create .env file with token
Note over Env: CLOUDFLARE_TUNNEL_TOKEN=eyJh...
User->>Compose: docker compose up
Compose->>Env: Read CLOUDFLARE_TUNNEL_TOKEN
Compose->>CFD: Start container with token in command
Note over CFD: tunnel run --token <token>
CFD->>CF_Net: Initiate tunnel connection with token
CF_Net->>CF_Net: Validate token signature
CF_Net->>CF_Net: Retrieve tunnel configuration
CF_Net-->>CFD: Tunnel established
Note over CFD,CF_Net: Persistent connection maintained
Token Lifecycle
Token Security Properties
- Single-Use Binding: Each token is bound to a specific tunnel UUID in Cloudflare’s infrastructure
- Environment Injection: Token is injected via environment variable substitution at container start
- Never Logged: The
--tokenflag ensures the token does not appear in process listings - Git Exclusion: The
.envfile containing the token is excluded via.gitignore
Sources: docker-compose.yml14 docker-compose.yml:16-17 .env.sample1 README.md53
Connection Establishment
The cloudflared container establishes an outbound-only connection to Cloudflare’s edge network. This inverted architecture eliminates the need for inbound firewall rules or public IP addresses.
Tunnel Command Structure
The command executed by the container is:
tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}
| Flag | Purpose |
|---|---|
tunnel | Primary cloudflared operation mode |
--no-autoupdate | Disables automatic binary updates to ensure container image control |
run | Executes the tunnel connector |
--token | Provides authentication credential inline |
Sources: docker-compose.yml14
Connection Flow
Sources: docker-compose.yml14 README.md64 README.md67
Traffic Routing Mechanism
The cloudflared container acts as a local reverse proxy, receiving traffic from the Cloudflare tunnel and forwarding it to the mosquitto service using Docker’s internal DNS resolution.
graph LR
subgraph "Cloudflare Configuration"
CF_HOST["Public Hostname:\nmqtt.example.com"]
CF_SERVICE["Service Type: HTTP"]
CF_URL["URL: mosquitto:9001"]
end
subgraph "Docker Network"
DNS["Docker Internal DNS"]
MOSQ_NAME["Container Name:\nmosquitto"]
MOSQ_IP["Container IP:\n172.x.x.x"]
end
subgraph "cloudflared Container"
CFD_PROC["cloudflared Process"]
RESOLVER["DNS Resolver"]
HTTP_CLIENT["HTTP Client"]
end
CF_HOST --> CF_SERVICE
CF_SERVICE --> CF_URL
CF_URL -.->|'mosquitto' hostname configured in tunnel| CFD_PROC
CFD_PROC --> RESOLVER
RESOLVER --> DNS
DNS --> MOSQ_NAME
MOSQ_NAME --> MOSQ_IP
MOSQ_IP -.-> HTTP_CLIENT
HTTP_CLIENT -->|HTTP to 172.x.x.x:9001| MOSQ_IP
Internal DNS Resolution
Routing Configuration
The routing is configured in the Cloudflare Dashboard during tunnel setup:
- Public Hostname: User-defined subdomain (e.g.,
mqtt.example.com) - Service Type:
HTTP(internal protocol between cloudflared and mosquitto) - Service URL:
mosquitto:9001(Docker service name and port)
The mosquitto hostname in the service URL is resolved by Docker’s internal DNS to the IP address of the container named mosquitto as defined in docker-compose.yml6
Sources: README.md:62-67 docker-compose.yml:4-6
Protocol Translation
The cloudflared container performs protocol translation between external HTTPS/WSS connections and internal HTTP/WebSocket connections.
External vs Internal Protocols
| Direction | Protocol | Port | Encryption |
|---|---|---|---|
| Client → Cloudflare Edge | HTTPS/WSS | 443 | TLS (Cloudflare-managed) |
| Cloudflare Edge → cloudflared | Tunnel Protocol | N/A | Encrypted tunnel |
| cloudflared → mosquitto | HTTP/WebSocket | 9001 | Unencrypted (internal network) |
The external connection is encrypted end-to-end from client to Cloudflare’s edge, while the internal connection between cloudflared and mosquitto is unencrypted but isolated within Docker’s private bridge network.
Sources: README.md67 README.md82
Operational Characteristics
No Exposed Ports
The cloudflared container exposes no ports to the host system. All communication occurs via:
- Outbound tunnel connection to Cloudflare’s network
- Internal Docker network for communication with the mosquitto container
This design eliminates attack surface by ensuring no listening ports on the host machine.
Sources: docker-compose.yml:11-17
Restart Behavior
The container uses the unless-stopped restart policy defined in docker-compose.yml15 This ensures:
- Automatic recovery: Container restarts on crashes or Docker daemon restarts
- Persistent operation: Container continues running across reboots
- Manual control: Container only stops when explicitly commanded via
docker compose downordocker stop
Update Management
The --no-autoupdate flag in docker-compose.yml14 disables cloudflared’s built-in automatic update mechanism. This ensures:
- Version pinning: Updates are controlled via Docker image version
- Predictable behavior: No unexpected binary changes during operation
- Image-based updates: Updates occur through
docker compose pulland container recreation
Sources: docker-compose.yml:14-15
Connection State Diagram
Sources: docker-compose.yml:14-15
Environment Variable Configuration
The cloudflared container requires exactly one environment variable:
CLOUDFLARE_TUNNEL_TOKEN
- Source: Defined in
.envfile (see .env.sample1) - Injection Method: Environment variable substitution in docker-compose.yml14
- Format: Base64-encoded JSON containing tunnel credentials
- Scope: Container-level environment variable
- Security: Must not be committed to version control
The token is referenced twice in the configuration:
- In the
commandfield via shell substitution:${CLOUDFLARE_TUNNEL_TOKEN} - In the
environmentsection for explicit passing to the container
Sources: docker-compose.yml14 docker-compose.yml:16-17 .env.sample1
Security Model
Zero Inbound Exposure
The cloudflared container implements a security model where:
- No listening ports: Container binds no ports to the host interface
- Outbound-only connections: All connections are initiated from inside the network
- Tunnel-based access: External access is only possible through the authenticated tunnel
- No direct routes: No firewall rules or port forwarding required
Authentication Flow
Sources: docker-compose.yml14 docker-compose.yml:16-17 README.md53
Common Configuration Reference
Minimal Configuration
The cloudflared service requires minimal configuration:
Configuration Dependencies
| Dependency | Location | Purpose |
|---|---|---|
| Tunnel Token | .env file | Authentication credential |
| Tunnel Configuration | Cloudflare Dashboard | Routing and hostname mapping |
| Docker Network | Implicit (default bridge) | Container-to-container communication |
| mosquitto Service | docker-compose.yml:4-9 | Target service for traffic forwarding |
Sources: docker-compose.yml:11-17
Integration with Mosquitto
The cloudflared container integrates with the mosquitto container through Docker’s internal networking:
Service Discovery
- DNS Name:
mosquitto(from docker-compose.yml4) - Container Name:
mosquitto(from docker-compose.yml6) - Target Port:
9001(WebSocket listener) - Resolution: Automatic via Docker’s embedded DNS server
graph TB
subgraph "Host System"
HOST["No Exposed Ports"]
end
subgraph "Docker Bridge Network"
CFD_CONTAINER["cloudflared Container\nNo EXPOSE directive"]
MOSQ_CONTAINER["mosquitto Container\nNo EXPOSE directive"]
DOCKER_DNS["Docker DNS Server"]
end
subgraph "External Network"
CF_EDGE["Cloudflare Edge\nPublic HTTPS:443"]
end
CF_EDGE <-->|Outbound tunnel connection| CFD_CONTAINER
CFD_CONTAINER <-->|DNS query: mosquitto| DOCKER_DNS
DOCKER_DNS -->|Resolves to container IP| MOSQ_CONTAINER
CFD_CONTAINER -->|HTTP:9001| MOSQ_CONTAINER
HOST -.->|No direct connection| CFD_CONTAINER
HOST -.->|No direct connection| MOSQ_CONTAINER
Network Isolation
Both containers run on the same Docker bridge network without exposing ports to the host:
Sources: docker-compose.yml:4-6 docker-compose.yml:11-17 README.md64
Troubleshooting Connection Issues
Token Validation Failures
If the cloudflared container fails to establish a tunnel:
- Verify token format: Token should be a long base64-encoded string
- Check
.envfile: EnsureCLOUDFLARE_TUNNEL_TOKEN=<token>with no extra spaces - Confirm tunnel exists: Verify the tunnel is active in Cloudflare Dashboard
- Review logs:
docker logs cloudflaredwill show authentication errors
DNS Resolution Failures
If cloudflared cannot reach the mosquitto container:
- Verify container names: Both containers must use the names defined in docker-compose.yml
- Check network: Both containers must be on the same Docker network (default bridge)
- Confirm mosquitto is running:
docker psshould show both containers as “Up” - Test DNS resolution:
docker exec cloudflared nslookup mosquittoshould resolve
Service URL Configuration
The service URL in Cloudflare Dashboard must match the Docker configuration:
- Correct:
mosquitto:9001orhttp://mosquitto:9001 - Incorrect:
localhost:9001,127.0.0.1:9001,mqtt:9001
Sources: docker-compose.yml:4-6 README.md64 README.md84
Dismiss
Refresh this wiki
Enter email to refresh
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
Loading…
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
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Configuration Reference
Loading…
Configuration Reference
Relevant source files
This page provides comprehensive reference documentation for all configuration files and settings in the Docker MQTT Mosquitto Cloudflare Tunnel system. Each configuration file is documented with complete syntax specifications, available directives, default values, and behavioral implications.
The system uses three configuration files:
docker-compose.yml- service orchestration and container definitionsmosquitto.conf- MQTT broker listener and access control configuration.env- runtime environment variables containing sensitive credentials
For architectural context, see page 1.1. For security implications of configuration choices, see page 1.2. For setup instructions, see page 2.
Configuration Files Overview
The system uses three primary configuration files:
| File | Purpose | Version Controlled | Contains Secrets | Format |
|---|---|---|---|---|
docker-compose.yml | Service orchestration and container definitions | Yes | No | YAML (Compose v3.8) |
mosquitto.conf | MQTT broker configuration (listeners, protocols, access control) | Yes | No | Mosquitto INI-style |
.env | Runtime secrets and environment variables | No (.gitignore) | Yes | KEY=VALUE pairs |
.env.sample | Template for .env file | Yes | No | KEY=VALUE pairs |
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3 .gitignore1
Configuration File Relationships
Configuration Directive Mapping to Runtime Behavior
The following diagram maps natural language concepts to specific configuration directives and code symbols used in the system:
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
graph TB
subgraph "docker-compose.yml Directives"
Version["version: '3.8'"]
MosqService["services.mosquitto"]
MosqImage["image: eclipse-mosquitto:latest"]
MosqContainer["container_name: mosquitto"]
MosqVolume["volumes:\n./mosquitto.conf:/mosquitto/config/mosquitto.conf"]
MosqRestart["restart: unless-stopped"]
CFService["services.cloudflared"]
CFImage["image: cloudflare/cloudflared:latest"]
CFContainer["container_name: cloudflared"]
CFCommand["command: tunnel --no-autoupdate run --token"]
CFEnv["environment:\nCLOUDFLARE_TUNNEL_TOKEN"]
CFRestart["restart: unless-stopped"]
end
subgraph "mosquitto.conf Directives"
Listener1883["listener 1883"]
AllowAnon["allow_anonymous true"]
Listener9001["listener 9001"]
ProtocolWS["protocol websockets"]
end
subgraph ".env Variables"
TunnelToken["CLOUDFLARE_TUNNEL_TOKEN=<value>"]
end
subgraph "Runtime Behavior"
TCPPort["TCP MQTT on port 1883"]
WSPort["WebSocket MQTT on port 9001"]
NoAuth["No authentication required"]
TunnelAuth["Tunnel authentication to Cloudflare"]
end
Listener1883 --> TCPPort
Listener9001 --> WSPort
ProtocolWS --> WSPort
AllowAnon --> NoAuth
TunnelToken --> CFEnv
CFEnv --> TunnelAuth
MosqVolume -.->|mounts| Listener1883
MosqVolume -.->|mounts| AllowAnon
MosqVolume -.->|mounts| Listener9001
MosqVolume -.->|mounts| ProtocolWS
docker-compose.yml Reference
The docker-compose.yml file defines the multi-container application using Docker Compose specification version 3.8. It orchestrates two services: mosquitto (MQTT broker) and cloudflared (tunnel connector).
graph TB
Root["docker-compose.yml"]
Version["version: '3.8'\n(line 1)"]
Services["services:\n(line 3)"]
MosqService["mosquitto:\n(line 4)"]
MosqImage["image: eclipse-mosquitto:latest\n(line 5)"]
MosqContainer["container_name: mosquitto\n(line 6)"]
MosqVolumes["volumes:\n(line 7)"]
MosqVolMount["./mosquitto.conf:/mosquitto/config/mosquitto.conf\n(line 8)"]
MosqRestart["restart: unless-stopped\n(line 9)"]
CFService["cloudflared:\n(line 11)"]
CFImage["image: cloudflare/cloudflared:latest\n(line 12)"]
CFContainer["container_name: cloudflared\n(line 13)"]
CFCommand["command: tunnel --no-autoupdate run --token $CLOUDFLARE_TUNNEL_TOKEN\n(line 14)"]
CFRestart["restart: unless-stopped\n(line 15)"]
CFEnv["environment:\n(line 16)"]
CFEnvToken["- CLOUDFLARE_TUNNEL_TOKEN\n(line 17)"]
Root --> Version
Root --> Services
Services --> MosqService
Services --> CFService
MosqService --> MosqImage
MosqService --> MosqContainer
MosqService --> MosqVolumes
MosqService --> MosqRestart
MosqVolumes --> MosqVolMount
CFService --> CFImage
CFService --> CFContainer
CFService --> CFCommand
CFService --> CFRestart
CFService --> CFEnv
CFEnv --> CFEnvToken
File Structure
Sources: docker-compose.yml:1-18
Service: mosquitto
Directives
| Directive | Value | Line | Description |
|---|---|---|---|
image | eclipse-mosquitto:latest | 5 | Docker Hub image for Eclipse Mosquitto broker. Uses the latest tag, which tracks the most recent stable release. |
container_name | mosquitto | 6 | Static container name. Used by cloudflared service for DNS resolution via Docker’s internal DNS server. |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | 7-8 | Bind mount that injects custom configuration. Host path is relative to docker-compose.yml location. Container path is the default Mosquitto config location. |
restart | unless-stopped | 9 | Restart policy. Container automatically restarts on failure or daemon restart, but not if manually stopped. |
Sources: docker-compose.yml:4-9
Volume Mount Behavior
The volume directive at docker-compose.yml8 creates a bind mount from the host filesystem:
- Host path:
./mosquitto.conf(relative to the directory containingdocker-compose.yml) - Container path:
/mosquitto/config/mosquitto.conf - Mount type: Bind mount (read-only by default in this configuration)
- Effect: Overrides the default Mosquitto configuration with custom listener definitions
The Mosquitto process reads this file at startup. Changes to the host file require container restart to take effect.
Sources: docker-compose.yml:7-8
Service: cloudflared
Directives
| Directive | Value | Line | Description |
|---|---|---|---|
image | cloudflare/cloudflared:latest | 12 | Official Cloudflare tunnel connector image. The latest tag should be pinned to specific versions in production. |
container_name | cloudflared | 13 | Static container name for identification and logging. |
command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | 14 | Overrides the default container entrypoint. Runs the tunnel in persistent mode with auto-update disabled. The ${CLOUDFLARE_TUNNEL_TOKEN} is substituted from the environment. |
restart | unless-stopped | 15 | Matches the restart policy of the mosquitto service for consistency. |
environment | CLOUDFLARE_TUNNEL_TOKEN | 16-17 | Declares that the CLOUDFLARE_TUNNEL_TOKEN environment variable should be passed from the host environment to the container. Value is sourced from the .env file. |
Sources: docker-compose.yml:11-18
Command Breakdown
The command directive at docker-compose.yml14 constructs the following runtime command:
tunnel --no-autoupdate run --token <token_value>
Command components:
tunnel- The cloudflared subcommand for tunnel operations--no-autoupdate- Disables automatic updates of the cloudflared binary (recommended in containerized environments)run- Starts the tunnel in persistent connection mode--token- Authentication token for the tunnel (required for cloudflared tunnel connectivity)${CLOUDFLARE_TUNNEL_TOKEN}- Environment variable substitution performed by Docker Compose
Sources: docker-compose.yml14
Network Configuration
Docker Compose automatically creates a bridge network for inter-service communication. The default network name follows the pattern <project-name>_default.
Service Discovery:
- The
mosquittoservice is resolvable via DNS at hostnamemosquitto - The
cloudflaredservice references this hostname when configured in the Cloudflare dashboard - No explicit
networkssection is required; services communicate on the default bridge network
Port Exposure:
- Neither service exposes ports to the host (
portsdirective not used) - All external access routes through the Cloudflare Tunnel
- Internal communication occurs on Docker’s isolated network
Sources: docker-compose.yml:1-18
graph TB
Conf["mosquitto.conf"]
Block1["Listener Configuration 1"]
L1Port["listener 1883\n(line 1)"]
L1Anon["allow_anonymous true\n(line 2)"]
Block2["Listener Configuration 2"]
L2Port["listener 9001\n(line 4)"]
L2Proto["protocol websockets\n(line 5)"]
Conf --> Block1
Conf --> Block2
Block1 --> L1Port
Block1 --> L1Anon
Block2 --> L2Port
Block2 --> L2Proto
Runtime1["Runtime Effect:\nTCP MQTT on 0.0.0.0:1883\nNo authentication required"]
Runtime2["Runtime Effect:\nWebSocket MQTT on 0.0.0.0:9001\nUses same allow_anonymous setting"]
L1Port --> Runtime1
L1Anon --> Runtime1
L2Port --> Runtime2
L2Proto --> Runtime2
mosquitto.conf Reference
The mosquitto.conf file configures the Eclipse Mosquitto MQTT broker. It defines two listeners with different protocols.
File Structure
Sources: mosquitto.conf:1-6
Listener Directives
Listener 1: TCP MQTT (Port 1883)
| Directive | Value | Line | Description |
|---|---|---|---|
listener | 1883 | 1 | Defines a listener on port 1883 (standard MQTT port). Binds to all interfaces (0.0.0.0) by default. |
allow_anonymous | true | 2 | Permits connections without username/password authentication. Applies to this listener and subsequent listeners unless overridden. |
Protocol: Native MQTT (TCP-based)
Bind address: 0.0.0.0 (all interfaces, default when not specified)
Authentication: None (anonymous access enabled)
Encryption: None (plaintext). Encryption is handled by Cloudflare Tunnel at the edge.
Sources: mosquitto.conf:1-2
Listener 2: WebSocket MQTT (Port 9001)
| Directive | Value | Line | Description |
|---|---|---|---|
listener | 9001 | 4 | Defines a listener on port 9001. This port is referenced by the Cloudflare Tunnel configuration. |
protocol | websockets | 5 | Enables WebSocket protocol for this listener. Allows browser-based MQTT clients to connect. |
Protocol: MQTT over WebSockets
Bind address: 0.0.0.0 (default)
Authentication: Inherits allow_anonymous true from line 2
Encryption: None at broker level (handled by Cloudflare Tunnel)
Sources: mosquitto.conf:4-5
Directive Scope and Inheritance
The allow_anonymous true directive at mosquitto.conf2 has global scope and applies to all subsequent listeners. The configuration does not explicitly set allow_anonymous for the second listener, so it inherits the global setting.
Scope hierarchy:
- Global directives apply to all listeners
- Per-listener directives override global settings
- Subsequent global directives override previous global directives
In this configuration:
- Line 2 sets global anonymous access to
true - Both listeners (lines 1 and 4) permit anonymous connections
Sources: mosquitto.conf:1-5
graph LR
Client["MQTT Client\n(Browser or Native)"]
CFEdge["Cloudflare Edge\n(TLS termination)"]
Tunnel["Cloudflare Tunnel\n(cloudflared container)"]
Mosq9001["mosquitto:9001\n(WebSocket listener)"]
Mosq1883["mosquitto:1883\n(TCP listener)"]
Client -->|WSS/HTTPS| CFEdge
CFEdge -->|Encrypted tunnel| Tunnel
Tunnel -->|HTTP to mosquitto:9001| Mosq9001
Mosq9001 -.->|Not directly accessible| Mosq1883
Note1["listener 9001\nprotocol websockets\n(mosquitto.conf:4-5)"]
Note2["listener 1883\n(mosquitto.conf:1)"]
Mosq9001 --- Note1
Mosq1883 --- Note2
Protocol Mapping to Cloudflare Tunnel
The Cloudflare Tunnel must be configured to route traffic to http://mosquitto:9001:
Critical configuration requirement: The Cloudflare Tunnel public hostname must route to http://mosquitto:9001 (the WebSocket listener), not port 1883. Port 1883 is the native MQTT protocol and cannot be proxied through HTTP-based Cloudflare Tunnels.
Sources: mosquitto.conf:1-5 docker-compose.yml:6-13
Security Implications
The allow_anonymous true directive at mosquitto.conf2 disables authentication:
| Security Property | Status | Implication |
|---|---|---|
| Username/password authentication | Disabled | Any client can connect without credentials |
| Access control lists (ACLs) | Not configured | All topics are readable and writable by all clients |
| TLS/SSL at broker level | Not configured | Plaintext MQTT within Docker network (acceptable because traffic is encrypted by Cloudflare Tunnel before reaching the internet) |
Recommended for: Development, testing, or trusted environments where the Cloudflare Tunnel provides sufficient access control.
Not recommended for: Production environments with multiple tenants or sensitive data without additional access controls.
For authentication and ACL configuration, see page 6.1.
Sources: mosquitto.conf2
Environment Variables Reference
Environment variables provide runtime configuration, particularly for sensitive credentials that must not be committed to version control.
CLOUDFLARE_TUNNEL_TOKEN
Variable name: CLOUDFLARE_TUNNEL_TOKEN
Defined in: .env (user-created, not version controlled)
Template in: .env.sample:1
Referenced in: docker-compose.yml:14,17
Used by: cloudflared service
Purpose
This variable contains the authentication token for the Cloudflare Tunnel. The token is a long-lived credential that authorizes the cloudflared container to establish an outbound connection to Cloudflare’s network and receive inbound traffic routed through the tunnel.
Token format: Base64-encoded JSON Web Token (JWT) with tunnel metadata and credentials.
Sources: .env.sample1 docker-compose.yml:14-17
Token Lifecycle
Sources: .env.sample1 docker-compose.yml:14-17
Configuration Method
The token is injected into the cloudflared container via two mechanisms:
- Environment variable declaration at docker-compose.yml:16-17:
This instructs Docker Compose to pass the variable from the host environment to the container.
- Command substitution at docker-compose.yml14:
Docker Compose performs variable substitution before starting the container, replacing ${CLOUDFLARE_TUNNEL_TOKEN} with the actual token value.
Sources: docker-compose.yml:14-17
Security Best Practices
| Practice | Implementation | Purpose |
|---|---|---|
| Never commit to version control | .gitignore contains .env at line 1 | Prevents accidental token exposure in Git history |
Use .env.sample as template | Contains placeholder your_token | Provides documentation without exposing real credentials |
| Restrict file permissions | chmod 600 .env (not enforced by repository) | Limits token access to file owner on Unix systems |
| Rotate tokens periodically | Regenerate in Cloudflare dashboard | Limits exposure window if token is compromised |
| Use separate tokens per environment | Create distinct tunnels for dev/staging/prod | Limits blast radius of token compromise |
Sources: .env.sample1 .gitignore1
Validation and Troubleshooting
Token validation:
- Token is validated when
cloudflaredattempts to connect to Cloudflare - Invalid tokens result in authentication failures logged in container output
- No local validation occurs before container startup
Common issues:
| Issue | Symptom | Resolution |
|---|---|---|
| Token not set | docker-compose warning about undefined variable | Create .env file with CLOUDFLARE_TUNNEL_TOKEN=<value> |
| Token invalid | cloudflared logs authentication errors | Regenerate token in Cloudflare dashboard |
| Token expired | Tunnel disconnects after previously working | Check Cloudflare dashboard for token status |
| Wrong token | Tunnel connects to different configuration | Verify token matches intended tunnel in dashboard |
Sources: docker-compose.yml:14-17 .env.sample1
sequenceDiagram
participant DC as docker-compose
participant Env as .env file
participant DCFile as docker-compose.yml
participant MConf as mosquitto.conf
participant MosqC as mosquitto container
participant CFC as cloudflared container
Note over DC,CFC: Configuration Loading Phase
DC->>Env: Read CLOUDFLARE_TUNNEL_TOKEN
DC->>DCFile: Parse service definitions
Note over DCFile: services.mosquitto (lines 4-9)
Note over DCFile: services.cloudflared (lines 11-17)
DC->>MosqC: Create container from eclipse-mosquitto:latest
DC->>MosqC: Mount ./mosquitto.conf to /mosquitto/config/mosquitto.conf
DC->>CFC: Create container from cloudflare/cloudflared:latest
DC->>CFC: Set environment variable CLOUDFLARE_TUNNEL_TOKEN
DC->>CFC: Set command with --token flag
Note over MosqC,CFC: Container Startup Phase
MosqC->>MConf: Read configuration file
Note over MConf: listener 1883 (line 1)
Note over MConf: allow_anonymous true (line 2)
Note over MConf: listener 9001 (line 4)
Note over MConf: protocol websockets (line 5)
MosqC->>MosqC: Initialize listeners 1883 and 9001
CFC->>CFC: Execute tunnel run with token
CFC->>CFC: Establish connection to Cloudflare
Configuration Loading Sequence
The following diagram shows the order in which configuration is loaded and applied during system startup:
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
Service Configuration Matrix
The following table maps configuration sources to their effects on each service:
| Configuration Aspect | Source File | Line(s) | Applies To | Effect |
|---|---|---|---|---|
| MQTT TCP listener | mosquitto.conf | 1 | mosquitto service | Opens port 1883 for MQTT TCP connections |
| Anonymous access | mosquitto.conf | 2 | mosquitto service | Allows connections without authentication |
| WebSocket listener | mosquitto.conf | 4-5 | mosquitto service | Opens port 9001 for WebSocket MQTT connections |
| Mosquitto image | docker-compose.yml | 5 | mosquitto service | Uses eclipse-mosquitto:latest |
| Mosquitto container name | docker-compose.yml | 6 | mosquitto service | Container named mosquitto |
| Configuration mount | docker-compose.yml | 8 | mosquitto service | Mounts mosquitto.conf to /mosquitto/config/mosquitto.conf |
| Mosquitto restart policy | docker-compose.yml | 9 | mosquitto service | Restarts unless explicitly stopped |
| Cloudflared image | docker-compose.yml | 12 | cloudflared service | Uses cloudflare/cloudflared:latest |
| Cloudflared container name | docker-compose.yml | 13 | cloudflared service | Container named cloudflared |
| Tunnel command | docker-compose.yml | 14 | cloudflared service | Runs tunnel --no-autoupdate run --token |
| Cloudflared restart policy | docker-compose.yml | 15 | cloudflared service | Restarts unless explicitly stopped |
| Tunnel token | .env | 1 | cloudflared service | Authenticates tunnel with Cloudflare |
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
graph TB
Root["docker-compose.yml root"]
Version["version: '3.8'"]
Services["services:] MosqService[mosquitto:] MosqProps[Properties:\n- image\n- container_name\n- volumes\n- restart"]
CFService["cloudflared:] CFProps[Properties:\n- image\n- container_name\n- command\n- restart\n- environment"]
Root --> Version
Root --> Services
Services --> MosqService
Services --> CFService
MosqService --> MosqProps
CFService --> CFProps
Configuration File Syntax
docker-compose.yml Syntax
The docker-compose.yml file follows Docker Compose version 3.8 specification. Key structural elements:
Sources: docker-compose.yml:1-18
graph LR
Conf["mosquitto.conf"]
Block1["Listener Block 1:\nlistener 1883\nallow_anonymous true"]
Block2["Listener Block 2:\nlistener 9001\nprotocol websockets"]
Conf --> Block1
Conf --> Block2
Block1 --> Port1["Port: 1883"]
Block1 --> Anon["Anonymous: true"]
Block2 --> Port2["Port: 9001"]
Block2 --> Proto["Protocol: websockets"]
mosquitto.conf Syntax
The mosquitto.conf file uses the Mosquitto broker configuration format. Each directive appears on its own line:
Sources: mosquitto.conf:1-6
.env File Syntax
The .env file uses simple KEY=value syntax, one variable per line:
CLOUDFLARE_TUNNEL_TOKEN=your_token_here
Sources: .env.sample:1-3
Configuration Validation
The system validates configuration at multiple stages:
| Validation Stage | Component | What Is Validated | Failure Behavior |
|---|---|---|---|
| Compose file parse | docker-compose CLI | YAML syntax of docker-compose.yml | Exit with parse error |
| Environment variable resolution | docker-compose CLI | Presence of ${CLOUDFLARE_TUNNEL_TOKEN} in .env | Warning or substitution with empty string |
| Mosquitto config parse | mosquitto process | Syntax of mosquitto.conf directives | Container logs error and may exit |
| Tunnel authentication | cloudflared process | Validity of CLOUDFLARE_TUNNEL_TOKEN | Connection fails, logged in container output |
| Port binding | Docker Engine | Availability of specified ports | Container fails to start if ports in use |
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
Configuration Override Hierarchy
When configuration values can be specified in multiple locations, the following precedence applies:
- Command-line arguments to
docker-compose(highest precedence) - Environment variables in the shell running
docker-compose .envfile in the project directory- Default values in
docker-compose.yml - Container defaults from images (lowest precedence)
For the mosquitto service, the configuration file mosquitto.conf:1-6 is mounted as a volume and overrides all default Mosquitto configurations.
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
Cross-Reference Index
The following table provides a quick reference for locating configuration settings:
| Setting Name | Configuration File | Line Number(s) |
|---|---|---|
version | docker-compose.yml | 1 |
services.mosquitto.image | docker-compose.yml | 5 |
services.mosquitto.container_name | docker-compose.yml | 6 |
services.mosquitto.volumes | docker-compose.yml | 7-8 |
services.mosquitto.restart | docker-compose.yml | 9 |
services.cloudflared.image | docker-compose.yml | 12 |
services.cloudflared.container_name | docker-compose.yml | 13 |
services.cloudflared.command | docker-compose.yml | 14 |
services.cloudflared.restart | docker-compose.yml | 15 |
services.cloudflared.environment | docker-compose.yml | 16-17 |
listener (port 1883) | mosquitto.conf | 1 |
allow_anonymous | mosquitto.conf | 2 |
listener (port 9001) | mosquitto.conf | 4 |
protocol | mosquitto.conf | 5 |
CLOUDFLARE_TUNNEL_TOKEN | .env.sample (template) | 1 |
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample:1-3
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
docker-compose.yml
Loading…
docker-compose.yml Reference
Relevant source files
Purpose and Scope
This document provides a complete technical reference for the docker-compose.yml file, which orchestrates both the mosquitto MQTT broker and cloudflared tunnel services. It covers all configuration properties, service definitions, volume mounts, environment variables, and restart policies used in the system.
For conceptual information about how Docker Compose orchestrates the services, see page 3.3. For deployment procedures, see page 2.4. For environment variable setup, see page 4.3.
Sources: docker-compose.yml:1-18
File Location and Structure
The docker-compose.yml file is located in the repository root directory and uses Docker Compose file format version 3.8. The file defines a multi-container application with two services that work together to provide secure, externally-accessible MQTT brokering.
graph TB
subgraph "docker-compose.yml Structure"
Root["version: '3.8'"]
Services["services:]
subgraph Service Definitions"
Mosquitto["mosquitto:]
Cloudflared[cloudflared:]
end
subgraph Mosquitto Properties"
MQ_Image["image: eclipse-mosquitto:latest"]
MQ_Container["container_name: mosquitto"]
MQ_Volumes["volumes: [./mosquitto.conf mount]"]
MQ_Restart["restart: unless-stopped"]
end
subgraph "Cloudflared Properties"
CF_Image["image: cloudflare/cloudflared:latest"]
CF_Container["container_name: cloudflared"]
CF_Command["command: tunnel ... run --token"]
CF_Restart["restart: unless-stopped"]
CF_Env["environment: [CLOUDFLARE_TUNNEL_TOKEN]"]
end
end
Root --> Services
Services --> Mosquitto
Services --> Cloudflared
Mosquitto --> MQ_Image
Mosquitto --> MQ_Container
Mosquitto --> MQ_Volumes
Mosquitto --> MQ_Restart
Cloudflared --> CF_Image
Cloudflared --> CF_Container
Cloudflared --> CF_Command
Cloudflared --> CF_Restart
Cloudflared --> CF_Env
High-Level Structure
Sources: docker-compose.yml:1-18
Version Specification
| Property | Value | Line |
|---|---|---|
version | '3.8' | docker-compose.yml1 |
The file uses Docker Compose file format version 3.8, which supports all features required by this system including service definitions, volume mounts, environment variables, and restart policies. Version 3.8 is compatible with Docker Engine 19.03.0+ and provides the necessary capabilities for container orchestration.
Sources: docker-compose.yml1
Services Overview
The services section defines two containerized services that comprise the complete system. Both services run continuously and restart automatically on failure.
graph LR
subgraph "Docker Compose Services"
subgraph "mosquitto Service"
MQ_Runtime["Container: mosquitto"]
MQ_Base["Base Image: eclipse-mosquitto:latest"]
MQ_Config["Config Volume Mount"]
MQ_Policy["Restart: unless-stopped"]
end
subgraph "cloudflared Service"
CF_Runtime["Container: cloudflared"]
CF_Base["Base Image: cloudflare/cloudflared:latest"]
CF_Token["Environment: CLOUDFLARE_TUNNEL_TOKEN"]
CF_Cmd["Command: tunnel run"]
CF_Policy["Restart: unless-stopped"]
end
end
subgraph "External Resources"
MosqConf["./mosquitto.conf"]
EnvFile[".env File"]
end
MosqConf -.-> MQ_Config
EnvFile -.-> CF_Token
CF_Runtime -->|Proxies to| MQ_Runtime
Service Composition Diagram
Sources: docker-compose.yml:3-18
Mosquitto Service Configuration
The mosquitto service runs the Eclipse Mosquitto MQTT broker. It is defined starting at line 4 and includes image specification, container naming, volume configuration, and restart policy.
Service Properties
| Property | Value | Line | Description |
|---|---|---|---|
image | eclipse-mosquitto:latest | docker-compose.yml5 | Official Eclipse Mosquitto Docker image, latest version |
container_name | mosquitto | docker-compose.yml6 | Fixed container name for internal DNS resolution |
volumes | ./mosquitto.conf:/mosquitto/config/mosquitto.conf | docker-compose.yml:7-8 | Mounts local config file into container |
restart | unless-stopped | docker-compose.yml9 | Automatic restart policy |
Image Specification
docker-compose.yml5 specifies eclipse-mosquitto:latest as the base image. This pulls the official Mosquitto image from Docker Hub. The latest tag ensures the most recent stable version is used, though this can be pinned to a specific version for production deployments.
Container Name
docker-compose.yml6 sets a fixed container name mosquitto. This serves two purposes:
- Provides predictable DNS resolution within Docker’s internal network
- Enables the
cloudflaredservice to reference the broker atmosquitto:9001
Volume Mount Configuration
docker-compose.yml:7-8 configures a bind mount that makes the broker configuration available to the container:
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
| Component | Value | Description |
|---|---|---|
| Host Path | ./mosquitto.conf | Relative path from docker-compose.yml location |
| Container Path | /mosquitto/config/mosquitto.conf | Standard Mosquitto config location |
| Mount Type | Bind mount | Direct filesystem mapping |
graph LR
HostFS["Host Filesystem\n./mosquitto.conf"]
Mount["Volume Mount\nBind Type"]
ContainerFS["Container Filesystem\n/mosquitto/config/mosquitto.conf"]
Process["mosquitto Process\nReads Config"]
HostFS -->|Mounted by Docker| Mount
Mount -->|Accessible at| ContainerFS
ContainerFS -->|Read by| Process
This mount allows configuration changes without rebuilding the container image. The mosquitto process reads this configuration on startup.
Volume Mount Flow
Sources: docker-compose.yml:4-9
Cloudflared Service Configuration
The cloudflared service establishes a secure tunnel to Cloudflare’s network. It is defined starting at line 11 and includes image specification, container naming, command configuration, environment variables, and restart policy.
Service Properties
| Property | Value | Line | Description |
|---|---|---|---|
image | cloudflare/cloudflared:latest | docker-compose.yml12 | Official Cloudflare Tunnel client image |
container_name | cloudflared | docker-compose.yml13 | Fixed container name |
command | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | docker-compose.yml14 | Command with token interpolation |
restart | unless-stopped | docker-compose.yml15 | Automatic restart policy |
environment | CLOUDFLARE_TUNNEL_TOKEN | docker-compose.yml:16-17 | Environment variable declaration |
Image Specification
docker-compose.yml12 specifies cloudflare/cloudflared:latest as the base image. This is the official Cloudflare Tunnel client maintained by Cloudflare.
Command Override
docker-compose.yml14 overrides the default container command with:
tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}
| Argument | Purpose |
|---|---|
tunnel | Main cloudflared subcommand for tunnel operations |
--no-autoupdate | Disables automatic updates within container (container restart handles updates) |
run | Starts the tunnel using token authentication |
--token | Specifies authentication token (value from environment variable) |
${CLOUDFLARE_TUNNEL_TOKEN} | Environment variable interpolation by Docker Compose |
Environment Variables
docker-compose.yml:16-17 declares the environment variable section:
graph TB
EnvFile[".env File\nCLOUDFLARE_TUNNEL_TOKEN=xxx"]
Compose["docker-compose.yml\nEnvironment Declaration"]
Runtime["Container Runtime Environment\nCLOUDFLARE_TUNNEL_TOKEN=xxx"]
Command["Command Execution\ntunnel run --token xxx"]
EnvFile -->|Read by docker-compose| Compose
Compose -->|Passes to container| Runtime
Runtime -->|Interpolated in| Command
environment:
- CLOUDFLARE_TUNNEL_TOKEN
This configuration tells Docker Compose to pass the CLOUDFLARE_TUNNEL_TOKEN environment variable from the host (typically loaded from .env file) into the container. The variable’s value is then used in the command string via ${CLOUDFLARE_TUNNEL_TOKEN} interpolation.
Environment Variable Flow
Sources: docker-compose.yml:11-17
Restart Policy Configuration
Both services use restart: unless-stopped docker-compose.yml:9-15 This restart policy ensures containers automatically restart under most failure conditions but respect explicit stop commands.
Restart Policy Behavior
| Scenario | Behavior |
|---|---|
| Container exits with error | Automatically restarts |
| Container exits successfully (exit code 0) | Automatically restarts |
| Docker daemon restarts | Container restarts |
Manual docker stop | Container remains stopped |
Manual docker-compose down | Container remains stopped |
| System reboot | Container does NOT restart (requires Docker to start stopped containers) |
Alternative Restart Policies
While this system uses unless-stopped, Docker Compose supports other policies:
| Policy | Description | Use Case |
|---|---|---|
no | Never restart (default) | Development, debugging |
always | Always restart, even after manual stop | Maximum availability |
unless-stopped | Restart unless explicitly stopped | Recommended for most services |
on-failure[:max-retries] | Restart only on non-zero exit | Services with proper shutdown |
Sources: docker-compose.yml:9-15
graph TB
subgraph "Docker Default Bridge Network"
subgraph "cloudflared Container"
CF_Process["cloudflared process"]
CF_DNS["DNS Resolution:\nmosquitto resolves to container IP"]
end
subgraph "mosquitto Container"
MQ_Process["mosquitto process"]
MQ_Listener["Listener: 0.0.0.0:9001"]
end
CF_Process -->|HTTP Proxy to mosquitto:9001| CF_DNS
CF_DNS -->|Resolves via Docker DNS| MQ_Listener
end
External["External Traffic\nfrom Cloudflare"]
External -.->|Via Tunnel| CF_Process
Service-to-Service Communication
Although not explicitly defined in the compose file, Docker Compose creates a default network for all services, enabling internal communication.
Internal Networking Model
The cloudflared service proxies traffic to mosquitto:9001, where mosquitto is resolved via Docker’s internal DNS to the mosquitto container’s IP address. This is made possible by the fixed container_name properties.
Sources: docker-compose.yml:6-13
Complete Property Reference
Top-Level Properties
| Property | Type | Required | Value in File |
|---|---|---|---|
version | string | No (but recommended) | '3.8' |
services | object | Yes | Contains mosquitto and cloudflared |
Mosquitto Service Properties
| Property Path | Type | Required | Value | Default if Omitted |
|---|---|---|---|---|
services.mosquitto.image | string | Yes | eclipse-mosquitto:latest | N/A |
services.mosquitto.container_name | string | No | mosquitto | Auto-generated |
services.mosquitto.volumes | array | No | ["./mosquitto.conf:/mosquitto/config/mosquitto.conf"] | No volumes |
services.mosquitto.restart | string | No | unless-stopped | no |
Cloudflared Service Properties
| Property Path | Type | Required | Value | Default if Omitted |
|---|---|---|---|---|
services.cloudflared.image | string | Yes | cloudflare/cloudflared:latest | N/A |
services.cloudflared.container_name | string | No | cloudflared | Auto-generated |
services.cloudflared.command | string/array | No | tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} | Image default |
services.cloudflared.restart | string | No | unless-stopped | no |
services.cloudflared.environment | array/object | No | ["CLOUDFLARE_TUNNEL_TOKEN"] | No environment vars |
Sources: docker-compose.yml:1-18
Deployment Commands
The following commands operate on this docker-compose.yml file:
| Command | Purpose |
|---|---|
docker-compose up -d | Start all services in detached mode |
docker-compose down | Stop and remove all containers |
docker-compose ps | Show service status |
docker-compose logs | View combined logs |
docker-compose logs mosquitto | View mosquitto logs only |
docker-compose logs cloudflared | View cloudflared logs only |
docker-compose restart | Restart all services |
docker-compose pull | Pull latest images |
Sources: docker-compose.yml:1-18
File Modification Considerations
When modifying docker-compose.yml, consider the following:
Image Version Pinning
The current configuration uses latest tags docker-compose.yml:5-12 For production environments, consider pinning specific versions:
Network Isolation
To add explicit network configuration:
Port Exposure
The current configuration does not expose ports to the host, which is intentional (all traffic goes through Cloudflare Tunnel). To expose ports for testing:
⚠️ Security Warning: Exposing ports bypasses the Cloudflare Tunnel security layer and should only be done in controlled environments.
Sources: docker-compose.yml:1-18
Configuration Validation
To validate the docker-compose.yml syntax:
This command parses the file, interpolates environment variables, and displays the resolved configuration. It will report syntax errors and missing environment variables.
Sources: docker-compose.yml:1-18
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
mosquitto.conf
Loading…
mosquitto.conf Reference
Relevant source files
Purpose and Scope
This page provides complete technical reference documentation for the mosquitto.conf configuration file used in this system. The file defines the MQTT broker’s runtime behavior, including network listeners, protocol support, and access control settings.
For conceptual information about Mosquitto configuration and setup procedures, see Mosquitto Configuration. For detailed documentation of the Mosquitto broker component itself, see Mosquitto MQTT Broker. For listener-specific implementation details, see MQTT Listeners and Anonymous Access.
Sources: mosquitto.conf:1-6
File Location and Integration
The mosquitto.conf file resides in the project root directory and is mounted as a read-only volume into the mosquitto container at runtime. The file is version-controlled and contains no secrets.
graph TB
subgraph "Version Control"
MosqConf["mosquitto.conf\n(Project Root)"]
end
subgraph "Docker Compose"
ComposeYML["docker-compose.yml"]
VolumeMount["Volume Mount Definition\n./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro"]
end
subgraph "Container Runtime"
MosqContainer["mosquitto Container\n(eclipse-mosquitto:latest)"]
MosqProcess["mosquitto Process\nReads Config at Startup"]
end
MosqConf --> ComposeYML
ComposeYML --> VolumeMount
VolumeMount --> MosqContainer
MosqContainer --> MosqProcess
Docker Compose Integration
Diagram: mosquitto.conf Mount and Load Path
The configuration is mounted read-only (:ro flag) to prevent the container from modifying the configuration file. The mosquitto process reads this configuration file during container initialization.
Sources: mosquitto.conf:1-6 docker-compose.yml
Configuration Overview
The current mosquitto.conf implements a minimal configuration optimized for accessibility and simplicity. It defines two network listeners with different protocols and enables anonymous client connections.
graph TB
subgraph "mosquitto.conf"
RootConfig["Configuration Root"]
subgraph "First Listener Block"
Listener1["listener 1883"]
Anonymous["allow_anonymous true"]
end
subgraph "Second Listener Block"
Listener2["listener 9001"]
Protocol["protocol websockets"]
end
RootConfig --> Listener1
Listener1 --> Anonymous
RootConfig --> Listener2
Listener2 --> Protocol
end
subgraph "Runtime Behavior"
Port1883["TCP Port 1883\nStandard MQTT Protocol"]
Port9001["TCP Port 9001\nMQTT over WebSockets"]
AnonymousAccess["All Clients Accepted\nNo Authentication Required"]
end
Listener1 --> Port1883
Listener2 --> Port9001
Anonymous --> AnonymousAccess
Active Configuration Structure
Diagram: Configuration Structure and Runtime Mapping
Sources: mosquitto.conf:1-6
Directive Reference
listener
Syntax: listener <port> [<bind_address>]
Purpose: Defines a network listener that accepts incoming MQTT client connections.
Parameters:
| Parameter | Required | Description |
|---|---|---|
port | Yes | TCP port number (1-65535) on which to listen for connections |
bind_address | No | IP address or hostname to bind to (default: all interfaces) |
Behavior:
- Multiple
listenerdirectives create multiple independent listeners - Each listener can be configured with different protocols and security settings
- Configuration directives following a
listenerapply to that specific listener until the nextlistenerdirective - When no bind address is specified, the listener binds to all available network interfaces (0.0.0.0)
Current Usage:
mosquitto.conf1 defines the first listener:
listener 1883
This creates a listener on port 1883 (the IANA-registered standard MQTT port) bound to all interfaces.
mosquitto.conf4 defines the second listener:
listener 9001
This creates a listener on port 9001 (commonly used for MQTT WebSockets) bound to all interfaces.
Sources: mosquitto.conf1 mosquitto.conf4
allow_anonymous
Syntax: allow_anonymous <true|false>
Purpose: Controls whether clients can connect without providing authentication credentials.
Parameters:
| Parameter | Required | Valid Values | Description |
|---|---|---|---|
| Setting | Yes | true or false | Enable or disable anonymous connections |
Behavior:
- When set to
true: Clients may connect without providing a username or password - When set to
false: All clients must authenticate using username/password or certificate-based authentication - This setting applies to all listeners unless overridden by listener-specific configuration
- Default value:
false(as of Mosquitto 2.0+)
Security Implications:
- Anonymous access is appropriate for trusted networks or development environments
- Production deployments should typically disable anonymous access and implement authentication
- When anonymous access is enabled, no per-client authorization checks occur unless ACLs are configured
Current Usage:
mosquitto.conf2 enables anonymous access:
allow_anonymous true
This configuration allows any client to connect to either listener (port 1883 or port 9001) without authentication.
Sources: mosquitto.conf2
protocol
Syntax: protocol <protocol_type>
Purpose: Specifies the MQTT protocol variant for a specific listener.
Parameters:
| Parameter | Required | Valid Values | Description |
|---|---|---|---|
protocol_type | Yes | mqtt, websockets | Protocol encapsulation method |
Behavior:
- Must be specified within a listener block (after a
listenerdirective) - Applies only to the immediately preceding listener
- When set to
mqtt: Standard MQTT protocol over TCP (default behavior) - When set to
websockets: MQTT protocol encapsulated in WebSocket frames
Protocol Variants:
| Value | Description | Use Case |
|---|---|---|
mqtt | Standard MQTT over TCP | Traditional MQTT clients, IoT devices, backend services |
websockets | MQTT over WebSocket protocol | Web browsers, JavaScript clients, environments with HTTP-only connectivity |
WebSocket Protocol Details:
- Enables MQTT communication from browser-based JavaScript clients
- WebSocket frames provide HTTP-compatible upgrade mechanism
- Allows MQTT traffic to traverse HTTP proxies and firewalls
- Implements RFC 6455 (WebSocket Protocol)
Current Usage:
mosquitto.conf5 configures the 9001 listener for WebSockets:
protocol websockets
This enables MQTT-over-WebSocket support on port 9001, while port 1883 uses standard MQTT (default when protocol is not specified).
Sources: mosquitto.conf5
graph LR
subgraph "mosquitto.conf Directives"
Line1["listener 1883\n(Line 1)"]
Line2["allow_anonymous true\n(Line 2)"]
Line4["listener 9001\n(Line 4)"]
Line5["protocol websockets\n(Line 5)"]
end
subgraph "Listener 1 Runtime Config"
L1Port["Bind Port: 1883"]
L1Protocol["Protocol: mqtt (default)"]
L1Auth["Auth: Anonymous Allowed"]
end
subgraph "Listener 2 Runtime Config"
L2Port["Bind Port: 9001"]
L2Protocol["Protocol: websockets"]
L2Auth["Auth: Anonymous Allowed (inherited)"]
end
Line1 --> L1Port
Line1 --> L1Protocol
Line2 --> L1Auth
Line2 --> L2Auth
Line4 --> L2Port
Line5 --> L2Protocol
Complete Configuration Map
The following diagram shows how each directive in mosquitto.conf maps to runtime broker behavior:
Diagram: Directive-to-Runtime Configuration Mapping
Sources: mosquitto.conf:1-6
Configuration Directives Not Present
The current configuration uses only three directives. Mosquitto supports numerous additional directives for advanced functionality. Common directives not present in this configuration include:
Authentication and Authorization
| Directive | Purpose |
|---|---|
password_file | Path to file containing username/password credentials |
acl_file | Path to file defining topic-based access control rules |
allow_zero_length_clientid | Control client ID requirements |
TLS/SSL Configuration
| Directive | Purpose |
|---|---|
cafile | CA certificate for client certificate validation |
certfile | Server certificate for TLS |
keyfile | Server private key for TLS |
require_certificate | Require client certificates |
Persistence and Logging
| Directive | Purpose |
|---|---|
persistence | Enable message persistence to disk |
persistence_location | Directory for persistence database |
log_dest | Logging destination (file, stdout, syslog) |
log_type | Types of messages to log |
Connection Limits
| Directive | Purpose |
|---|---|
max_connections | Maximum concurrent client connections |
max_keepalive | Maximum keepalive interval |
max_packet_size | Maximum MQTT packet size |
For advanced configuration including ACL-based access control, see the protected-no-wildcard branch documentation at Topic Access Control (ACL).
Sources: mosquitto.conf:1-6
graph TB
subgraph "Configuration Layer"
MosqConf["mosquitto.conf"]
ComposeYML["docker-compose.yml"]
EnvFile[".env"]
end
subgraph "Container Layer"
MosqContainer["mosquitto container\n(eclipse-mosquitto:latest)"]
CFDContainer["cloudflared container"]
end
subgraph "Mosquitto Configuration Application"
Listener1883["Listener: Port 1883\nProtocol: mqtt\nFrom: mosquitto.conf:1"]
Listener9001["Listener: Port 9001\nProtocol: websockets\nFrom: mosquitto.conf:4-5"]
AuthPolicy["Anonymous Access: Enabled\nFrom: mosquitto.conf:2"]
end
subgraph "Traffic Flow"
CFProxy["cloudflared proxies to\nmosquitto:9001"]
ExternalClients["External MQTT Clients\n(via Cloudflare Tunnel)"]
InternalClients["Internal MQTT Clients\n(direct to port 1883)"]
end
MosqConf -->|Volume Mount| MosqContainer
ComposeYML -->|Orchestrates| MosqContainer
ComposeYML -->|Orchestrates| CFDContainer
EnvFile -->|Provides Token to| CFDContainer
MosqContainer --> Listener1883
MosqContainer --> Listener9001
MosqContainer --> AuthPolicy
CFProxy -->|WebSocket Connection| Listener9001
ExternalClients -->|Through Cloudflare| CFProxy
InternalClients -->|Direct TCP| Listener1883
AuthPolicy -.->|Applies to| Listener1883
AuthPolicy -.->|Applies to| Listener9001
Relationship to System Architecture
The mosquitto.conf file configures the MQTT broker component within the larger system architecture:
Diagram: mosquitto.conf in System Context
The configuration file directly controls broker behavior, while Docker Compose handles orchestration and the Cloudflare tunnel handles external routing. Note that cloudflared proxies specifically to port 9001 (WebSocket listener), making this the primary entry point for external clients.
Sources: mosquitto.conf:1-6 docker-compose.yml
Configuration Modification Guidelines
Modifying the Configuration
-
Edit
mosquitto.confin the project root directory -
Restart the mosquitto container to apply changes:
-
Verify configuration by checking container logs:
Common Configuration Changes
Disabling Anonymous Access:
allow_anonymous false
password_file /mosquitto/config/password.txt
Enabling TLS on WebSocket Listener:
listener 9001
protocol websockets
cafile /mosquitto/config/ca.crt
certfile /mosquitto/config/server.crt
keyfile /mosquitto/config/server.key
Adding Standard MQTT over TLS:
listener 8883
cafile /mosquitto/config/ca.crt
certfile /mosquitto/config/server.crt
keyfile /mosquitto/config/server.key
Note that any additional files (passwords, certificates, ACLs) must be mounted into the container via additional volume mounts in docker-compose.yml.
Sources: mosquitto.conf:1-6 docker-compose.yml
Validation and Testing
After modifying mosquitto.conf, validate the configuration before deployment:
Syntax Validation
The mosquitto process validates configuration on startup. Invalid configurations will prevent container startup. Check logs for validation errors:
Configuration errors appear in startup logs with the format:
Error: <description of error>
Testing Listener Configuration
Test each configured listener independently:
Port 1883 (Standard MQTT):
Port 9001 (WebSockets): Requires a WebSocket-capable MQTT client. Browser-based clients can connect to:
ws://localhost:9001
For testing WebSocket connectivity through the Cloudflare tunnel, use the public hostname configured in the Cloudflare dashboard.
Sources: mosquitto.conf:1-6
File Format Specifications
Syntax Rules
| Rule | Description |
|---|---|
| Line-based | Each directive appears on a separate line |
| Comments | Lines starting with # are ignored |
| Whitespace | Leading and trailing whitespace is ignored |
| Blank lines | Empty lines are ignored |
| Case sensitivity | Directive names are case-sensitive |
| Continuation | No multi-line continuation support |
Directive Scope
Configuration directives fall into two categories:
Global Directives:
- Apply to the entire broker instance
- Examples:
allow_anonymous,persistence,log_dest - Typically appear before any
listenerdirectives
Listener-Specific Directives:
- Apply only to the immediately preceding
listener - Examples:
protocol,cafile,certfile - Must follow a
listenerdirective
In mosquitto.conf:1-6 allow_anonymous is a global directive (line 2) that applies to all listeners, while protocol is listener-specific (line 5) and applies only to the port 9001 listener.
Sources: mosquitto.conf:1-6
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Environment Variables
Loading…
Environment Variables
Relevant source files
Purpose and Scope
This document provides a comprehensive reference for all environment variables used in the Docker MQTT Mosquitto with Cloudflare Tunnel system. It covers the .env file structure, the .env.sample template, and security practices for managing sensitive credentials.
For information about the Docker Compose configuration that consumes these variables, see docker-compose.yml. For the initial setup process of configuring environment variables, see Local Configuration. For version control practices related to secret management, see Version Control Best Practices.
Environment Variable System Overview
The system uses a file-based environment variable pattern where secrets are stored in a .env file that is explicitly excluded from version control. A template file, .env.sample, is committed to the repository to document the required variables without exposing actual values.
File Structure
project-root/
├── .env # Actual secrets (gitignored)
├── .env.sample # Template (version controlled)
├── .gitignore # Excludes .env
└── docker-compose.yml # Consumes environment variables
Sources : .env.sample1 .gitignore1
Environment Variable File Relationship
Analysis : This diagram shows the critical separation between template and actual secrets. The .env.sample file serves as documentation in version control, while .env contains actual credentials and is explicitly excluded by .gitignore. Developers copy the template and populate it with real values obtained from the Cloudflare dashboard.
Sources : .env.sample1 .gitignore1
CLOUDFLARE_TUNNEL_TOKEN
The system uses a single environment variable for authentication and configuration.
Variable Specification
| Variable Name | Required | Type | Description |
|---|---|---|---|
CLOUDFLARE_TUNNEL_TOKEN | Yes | String (JWT) | Authentication token generated by Cloudflare Zero Trust dashboard for tunnel access |
Sources : .env.sample1
Purpose and Function
The CLOUDFLARE_TUNNEL_TOKEN is a JSON Web Token (JWT) that contains:
- Tunnel identification information
- Authentication credentials
- Routing configuration
- Cryptographic signatures
This token authorizes the cloudflared container to establish an outbound connection to Cloudflare’s network and route traffic to the local Mosquitto broker.
Token Format
The token is a base64-encoded JWT string with the following characteristics:
- Length: Approximately 500-800 characters
- Format:
eyJ...(standard JWT prefix) - Components: Header, payload, and signature concatenated with dots
Example from template :
CLOUDFLARE_TUNNEL_TOKEN=your_token
Sources : .env.sample1
Obtaining the Token
The token is generated through the Cloudflare Zero Trust dashboard during tunnel creation. See Cloudflare Tunnel Setup for detailed instructions. The token should be copied from the dashboard and placed in the .env file.
sequenceDiagram
participant User as "User"
participant Dashboard as "Cloudflare Dashboard"
participant EnvSample as ".env.sample"
participant EnvFile as ".env (local)"
participant Compose as "docker-compose.yml"
participant Container as "cloudflared container"
participant Tunnel as "Cloudflare Tunnel Service"
User->>Dashboard: 1. Create tunnel
Dashboard-->>User: 2. Generate CLOUDFLARE_TUNNEL_TOKEN
User->>EnvSample: 3. Read template
EnvSample-->>User: CLOUDFLARE_TUNNEL_TOKEN=your_token
User->>EnvFile: 4. Create .env file
User->>EnvFile: 5. Paste actual token
Note over Compose: docker-compose up
Compose->>EnvFile: 6. Read environment variables
EnvFile-->>Compose: CLOUDFLARE_TUNNEL_TOKEN=eyJh...
Compose->>Container: 7. Start container with env var
Container->>Container: 8. cloudflared tunnel run
Container->>Tunnel: 9. Authenticate with token
Tunnel-->>Container: 10. Tunnel established
Note over Container,Tunnel: Persistent outbound connection\nmaintained for traffic routing
Token Lifecycle and Usage
Analysis : The token is generated once during tunnel creation and remains valid until the tunnel is deleted or the token is rotated. The token is read from .env at container startup and used by the cloudflared tunnel run command to authenticate with Cloudflare’s network.
Sources : .env.sample1
Security Architecture
graph LR
subgraph "Files That Can Be Committed"
SAMPLE[".env.sample\nTemplate only"]
README["README.md"]
COMPOSE["docker-compose.yml"]
MOSQUITTO["mosquitto.conf"]
GITIGNORE_FILE[".gitignore"]
end
subgraph "Files That Must Not Be Committed"
ENV[".env\nContains actual token"]
DATA["data/*\nMosquitto persistence"]
end
subgraph ".gitignore Rules"
RULE1["Line 1: .env"]
RULE2["Line 2: data/*"]
end
RULE1 -.->|excludes| ENV
RULE2 -.->|excludes| DATA
SAMPLE -.->|safe to commit| README
ENV -.->|blocked by gitignore| SAMPLE
The .gitignore Boundary
The .gitignore file establishes a security boundary that prevents accidental exposure of secrets to version control.
Analysis : The .gitignore file contains two critical rules. Line 1 excludes .env to prevent token exposure. Line 2 excludes data/* to prevent committing Mosquitto’s persistence data, which may contain message history or subscriber information.
Sources : .gitignore:1-2
File Contents and Security Model
.env.sample (Template File)
Located at .env.sample1 this file provides:
-
Documentation of required variables
-
Example structure (not functional values)
-
Onboarding guide for new developers
CLOUDFLARE_TUNNEL_TOKEN=your_token
The value your_token is a placeholder and will not work for authentication. Developers must replace it with an actual token from the Cloudflare dashboard.
Security Properties :
- Safe to commit to public repositories
- Contains no sensitive information
- Serves as self-documenting configuration reference
.env (Actual Secrets File)
Not present in the repository; created locally by developers. Contains:
- Real
CLOUDFLARE_TUNNEL_TOKENvalue - Full JWT string with authentication credentials
Security Properties :
- Excluded from version control via .gitignore1
- Should have restrictive file permissions (
chmod 600 .env) - Must never be shared publicly or committed to Git
Sources : .env.sample1 .gitignore1
graph TB
subgraph "Filesystem"
ENV_FILE[".env file"]
end
subgraph "Docker Compose Process"
COMPOSE_CMD["docker-compose up"]
COMPOSE_PARSER["YAML Parser"]
COMPOSE_ENGINE["Docker Engine API"]
end
subgraph "docker-compose.yml"
SERVICE_DEF["cloudflared service definition"]
ENV_REF["environment:\n - CLOUDFLARE_TUNNEL_TOKEN"]
end
subgraph "Container Runtime"
CONTAINER["cloudflared container"]
PROCESS["cloudflared tunnel run"]
ENV_VAR["CLOUDFLARE_TUNNEL_TOKEN\nenvironment variable"]
end
ENV_FILE -->|1. read automatically| COMPOSE_CMD
COMPOSE_CMD --> COMPOSE_PARSER
COMPOSE_PARSER -->|2. parse service config| SERVICE_DEF
SERVICE_DEF --> ENV_REF
ENV_REF -->|3. resolve variable| ENV_FILE
ENV_FILE -->|4. substitute value| COMPOSE_ENGINE
COMPOSE_ENGINE -->|5. create container with env| CONTAINER
CONTAINER -->|6. start process| PROCESS
PROCESS -->|7. read env var| ENV_VAR
Integration with Docker Compose
The docker-compose.yml file consumes environment variables from the .env file automatically through Docker Compose’s built-in environment file support.
Environment Variable Injection Flow
Analysis : Docker Compose automatically loads the .env file from the project root directory. When it encounters environment variable references in docker-compose.yml, it substitutes values from the .env file before passing them to the Docker Engine. The cloudflared process can then access CLOUDFLARE_TUNNEL_TOKEN as a standard environment variable.
Sources : .env.sample1
graph LR
subgraph "Environment Variables"
TOKEN["CLOUDFLARE_TUNNEL_TOKEN"]
end
subgraph "Containers"
CFD["cloudflared\n✓ Has access"]
MOSQ["mosquitto\n✗ No access"]
end
TOKEN -->|injected via environment| CFD
TOKEN -.->|not available| MOSQ
Variable Scoping
The CLOUDFLARE_TUNNEL_TOKEN is only available to the cloudflared container. The mosquitto container does not have access to this variable, demonstrating principle of least privilege.
Sources : .env.sample1
Best Practices
Local Development
-
Initial Setup :
- Copy .env.sample1 to
.env - Obtain token from Cloudflare dashboard (see Cloudflare Tunnel Setup)
- Replace
your_tokenwith actual JWT token - Verify
.envis listed in .gitignore1
- Copy .env.sample1 to
-
File Permissions :
Restricts read/write access to the file owner only.
- Verification : Before starting containers, verify the token is populated:
If this returns empty, the token has not been configured.
Production Deployment
For production environments, consider alternatives to file-based secrets:
| Method | Security | Rotation | Audit |
|---|---|---|---|
.env file | Low | Manual | No |
| Docker secrets | Medium | Manual | Limited |
| HashiCorp Vault | High | Automated | Yes |
| AWS Secrets Manager | High | Automated | Yes |
| Azure Key Vault | High | Automated | Yes |
Production deployments should:
- Use secrets management systems instead of
.envfiles - Implement automatic token rotation
- Enable audit logging for secret access
- Use read-only access where possible
graph TB
subgraph "Common Mistakes (DO NOT DO)"
COMMIT["❌ Committing .env to Git"]
HARDCODE["❌ Hardcoding token in docker-compose.yml"]
PUBLIC["❌ Posting token in issues/forums"]
SHARE["❌ Sharing .env file via email/chat"]
PERMISSIONS["❌ World-readable .env file"]
end
subgraph "Correct Practices (DO THIS)"
GITIGNORE_CHECK["✓ Verify .env in .gitignore"]
ENV_PATTERN["✓ Use environment variable pattern"]
ROTATE["✓ Rotate tokens regularly"]
RESTRICT["✓ Restrict file permissions"]
SECRETS_MGR["✓ Use secrets manager in production"]
end
COMMIT -.->|leads to| TOKEN_LEAK["Token Exposure"]
HARDCODE -.->|leads to| TOKEN_LEAK
PUBLIC -.->|leads to| TOKEN_LEAK
SHARE -.->|leads to| TOKEN_LEAK
PERMISSIONS -.->|leads to| TOKEN_LEAK
TOKEN_LEAK -.->|enables| UNAUTHORIZED["Unauthorized Access"]
Token Rotation
The CLOUDFLARE_TUNNEL_TOKEN can be rotated by:
- Creating a new tunnel in the Cloudflare dashboard
- Updating the
.envfile with the new token - Restarting containers:
docker-compose restart cloudflared
Common Security Mistakes
Sources : .gitignore1
Troubleshooting
Token Not Found Error
Symptom : cloudflared container fails to start with error about missing token.
Diagnosis :
- Verify
.envfile exists in project root - Check token variable name is exactly
CLOUDFLARE_TUNNEL_TOKEN - Ensure no extra spaces or quotes around the token value
Solution : Create or correct the .env file using .env.sample1 as template.
Token Authentication Failed
Symptom : cloudflared container starts but fails to establish tunnel.
Diagnosis :
- Token may be invalid or expired
- Token may be from a different tunnel
- Tunnel may have been deleted in Cloudflare dashboard
Solution : Generate new token from Cloudflare dashboard and update .env file.
.env File Accidentally Committed
Symptom : .env file appears in Git history.
Immediate Actions :
- Rotate token immediately in Cloudflare dashboard
- Remove file from Git history using
git filter-branchor BFG Repo-Cleaner - Update
.envwith new token - Verify .gitignore1 contains
.env
Environment Variable Reference Table
| Variable | Type | Required | Default | Container | Usage |
|---|---|---|---|---|---|
CLOUDFLARE_TUNNEL_TOKEN | String (JWT) | Yes | None | cloudflared | Authenticates tunnel connection to Cloudflare network |
Sources : .env.sample1
Related Documentation
- docker-compose.yml - How environment variables are consumed in Docker Compose
- Cloudflared Tunnel Connector - How the cloudflared container uses the token
- Local Configuration - Setup instructions for creating
.envfile - Security Model - Overall security architecture and secret management
- Version Control Best Practices - Git workflows for secrets
Sources : .env.sample1 .gitignore:1-2
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
CI/CD and Automation
Loading…
CI/CD and Automation
Relevant source files
Purpose and Scope
This document describes the automated testing and documentation build systems that ensure code quality and maintain up-to-date documentation for the Docker MQTT Mosquitto Cloudflare Tunnel system.
The system implements two distinct automation pipelines:
- Continuous Integration Pipeline - Validates that the Mosquitto service can successfully start on every commit
- Documentation Build Pipeline - Generates and deploys wiki documentation to GitHub Pages
This page is organized into three main sections:
- Continuous Integration (Section 5.1) - Details the automated testing workflow
- Documentation Pipeline (Section 5.2) - Explains the documentation generation and deployment process
- Version Control Best Practices (Section 5.3) - Covers Git workflow and secret management in the context of CI/CD
For general system setup, see page 2 (Getting Started). For component details, see page 3 (Components).
Sources: .github/workflows/ci.yml, .github/workflows/build-docs.yml, .gitignore
Automation Pipeline Overview
The system uses GitHub Actions to automate quality assurance and documentation maintenance. Both pipelines execute in isolated GitHub-hosted runners and operate independently.
Pipeline Comparison
| Aspect | CI Pipeline | Documentation Pipeline |
|---|---|---|
| Workflow File | .github/workflows/ci.yml | .github/workflows/build-docs.yml |
| Trigger | Push or PR to main | Weekly schedule + manual dispatch |
| Runner | ubuntu-latest | ubuntu-latest |
| Primary Action | Test service startup | Generate and deploy docs |
| Duration | ~2 minutes | ~5 minutes |
| External Dependencies | Docker Compose | DeepWiki, GitHub Pages |
| Failure Impact | Blocks merge | Does not block development |
Sources: .github/workflows/ci.yml, .github/workflows/build-docs.yml
Automation Architecture
Diagram: GitHub Actions Pipeline Architecture
This diagram illustrates the two independent automation pipelines. The CI pipeline .github/workflows/ci.yml:1-43 executes on code changes, while the documentation pipeline .github/workflows/build-docs.yml:1-81 runs on a weekly schedule. Both use GitHub-hosted runners but have different trigger mechanisms and purposes.
Sources: .github/workflows/ci.yml, .github/workflows/build-docs.yml
5.1 Continuous Integration
The CI pipeline validates that the Mosquitto MQTT broker can successfully start in a Docker environment. This automated test runs on every push to main and on all pull requests targeting main.
Workflow Configuration
The workflow is defined in .github/workflows/ci.yml:1-43 with the following key elements:
Trigger Configuration .github/workflows/ci.yml:3-9:
Job Definition .github/workflows/ci.yml:11-13:
- Job Name:
test-mosquitto - Runner:
ubuntu-latest - Purpose: Validate Mosquitto container can start and reach running state
CI Execution Steps
| Step | Action | Purpose | Exit Condition |
|---|---|---|---|
| 1. Checkout | actions/checkout@v2 | Clone repository | Always succeeds |
| 2. Setup | apt-get install docker-compose | Install Docker Compose | Package installation complete |
| 3. Start Service | docker-compose up -d mosquitto | Launch Mosquitto container | Container created |
| 4. Health Check | Poll container status | Verify running state | Running or timeout (100s) |
| 5. Teardown | docker-compose down | Clean up resources | Always executes |
Health Check Mechanism
Diagram: Health Check Loop Logic
The health check loop .github/workflows/ci.yml:28-39 implements a polling strategy with bounded retry:
This bash loop:
- Executes up to 10 iterations
- Uses
docker inspectto query container state - Checks if status contains “running”
- Waits 10 seconds between attempts
- Fails after 100 seconds total (10 × 10s)
Sources: .github/workflows/ci.yml:28-39
Why Only Mosquitto Is Tested
The CI pipeline starts only the mosquitto service, not the cloudflared service .github/workflows/ci.yml25:
Rationale:
- The
cloudflaredcontainer requiresCLOUDFLARE_TUNNEL_TOKENenvironment variable - This token is a secret that cannot be exposed in public CI logs
- The
mosquittoservice has no external dependencies and can run in isolation - Starting Mosquitto validates the core MQTT broker functionality
The docker-compose.yml file docker-compose.yml:4-9 defines mosquitto as an independent service that doesn’t depend on cloudflared, enabling this isolated testing approach.
Sources: .github/workflows/ci.yml:25, docker-compose.yml:4-9
Failure Scenarios
| Failure Type | Symptom | Likely Cause | CI Outcome |
|---|---|---|---|
| Container won’t start | Exits before 100s | Invalid mosquitto.conf syntax | Exit 1, workflow fails |
| Container starts then crashes | Status changes from running | Configuration error (port conflict, permissions) | Exit 1, workflow fails |
| Timeout | Never reaches running state | Resource constraints, slow pull | Exit 1, workflow fails |
| Teardown failure | docker-compose down fails | Docker daemon issue | Step fails, workflow fails |
When the CI workflow fails, it blocks pull request merges, preventing broken configurations from entering the main branch.
Sources: .github/workflows/ci.yml:28-42
5.2 Documentation Pipeline
The documentation pipeline automatically generates and deploys technical documentation to GitHub Pages. It uses the DeepWiki documentation generation system to create an mdBook-formatted static site from the repository’s codebase.
Workflow Configuration
The workflow is defined in .github/workflows/build-docs.yml:1-81 with distinct build and deploy jobs.
Trigger Configuration .github/workflows/build-docs.yml:3-7:
The pipeline executes in two scenarios:
- Scheduled: Every Sunday at 00:00 UTC via cron schedule
- Manual: Triggered through GitHub UI via
workflow_dispatch
Permissions .github/workflows/build-docs.yml:9-12:
These permissions enable:
- Reading repository contents
- Writing to GitHub Pages deployment
- Generating OIDC tokens for secure deployment
Build Job
The build job .github/workflows/build-docs.yml:19-69 performs documentation generation:
Diagram: Documentation Build Job Flow
Step 1: Repository Resolution
.github/workflows/build-docs.yml:25-52 determines which repository to document:
This logic allows manual workflow dispatch to specify a different repository, defaulting to the current repository (github.repository context variable).
Step 2: Title Generation
.github/workflows/build-docs.yml:37-47 generates the documentation book title:
For this repository, the generated title would be: "docker-mqtt-mosquitto-cloudflare-tunnel Documentation"
Step 3: DeepWiki Generation
.github/workflows/build-docs.yml:59-64 invokes the DeepWiki documentation generator:
The jzombie/deepwiki-to-mdbook action:
- Analyzes the repository structure
- Generates markdown documentation pages
- Creates an mdBook configuration
- Outputs static HTML to
./output/book
Step 4: Artifact Upload
.github/workflows/build-docs.yml:66-69 uploads the generated documentation:
This creates a GitHub Actions artifact containing the compiled mdBook site, which is consumed by the deploy job.
Sources: .github/workflows/build-docs.yml:19-69
Deploy Job
The deploy job .github/workflows/build-docs.yml:71-81 publishes documentation to GitHub Pages:
Diagram: Documentation Deploy Job Flow
The deploy job configuration .github/workflows/build-docs.yml:71-80:
Key aspects:
- Dependency:
needs: buildensures build completes first - Environment: Publishes to
github-pagesenvironment - Output:
page_urlprovides the live documentation URL - Idempotency: Overwrites existing GitHub Pages deployment
Sources: .github/workflows/build-docs.yml:71-81
Concurrency Control
.github/workflows/build-docs.yml:14-16 prevents concurrent documentation builds:
This configuration:
- Groups all Pages deployments under single concurrency group
- Queues new runs instead of canceling in-progress deployments
- Prevents race conditions in GitHub Pages publishing
Sources: .github/workflows/build-docs.yml:14-16
Documentation Update Frequency
The weekly schedule ensures documentation stays current without excessive CI usage:
| Update Trigger | Frequency | Purpose |
|---|---|---|
| Scheduled | Weekly (Sunday 00:00 UTC) | Regular refresh |
| Manual | On-demand via UI | Immediate update after major changes |
| Automatic | Never on push | Prevents CI overload |
Manual triggering is accessible via: Actions → Build and Deploy Documentation → Run workflow
Sources: .github/workflows/build-docs.yml:3-7
5.3 Version Control Best Practices
The repository implements strict separation between public configuration and sensitive secrets to prevent credential leakage in version control.
Gitignore Configuration
The .gitignore file .gitignore:1-2 enforces this separation:
.env
data/*
Rationale:
| Excluded Path | Type | Reason | Alternative |
|---|---|---|---|
.env | Secret file | Contains CLOUDFLARE_TUNNEL_TOKEN | .env.sample provides template |
data/* | Runtime directory | Mosquitto persistent storage | Created automatically by container |
Secret Management Pattern
Diagram: Secret Management Flow in Version Control
This diagram shows how secrets flow from Cloudflare Dashboard to Docker containers without entering version control. The .gitignore file .gitignore1 acts as a security boundary.
Sources: .gitignore:1-2
Tracked vs Untracked Files
| File Path | Version Controlled | Contains Secrets | Purpose |
|---|---|---|---|
.env.sample | ✓ Yes | ✗ No | Documents required variables |
.env | ✗ No | ✓ Yes | Stores CLOUDFLARE_TUNNEL_TOKEN |
docker-compose.yml | ✓ Yes | ✗ No | References ${CLOUDFLARE_TUNNEL_TOKEN} variable |
mosquitto.conf | ✓ Yes | ✗ No | Public broker configuration |
.github/workflows/ci.yml | ✓ Yes | ✗ No | CI pipeline definition |
.github/workflows/build-docs.yml | ✓ Yes | ✗ No | Documentation pipeline |
data/* | ✗ No | ⚠ Maybe | Runtime MQTT persistence (may contain messages) |
Git Workflow Integration
The CI pipeline .github/workflows/ci.yml:3-9 enforces version control best practices:
Protected Branch Pattern:
- All changes to
mainrequire pull request - CI must pass before merge
- Prevents broken configuration from entering main branch
Pull Request Flow:
- Developer creates feature branch
- Pushes changes to remote
- Opens pull request to
main - CI workflow executes automatically .github/workflows/ci.yml:7-9
- If CI passes, pull request can be merged
- If CI fails, developer must fix issues
Environment Variable Security
The docker-compose.yml file docker-compose.yml:16-17 references the token without exposing it:
This pattern:
- Uses variable substitution instead of hardcoded values
- Reads from
.envfile at runtime (excluded by.gitignore) - Never logs the token value in CI output
- Follows twelve-factor app methodology
Sources: docker-compose.yml:16-17, .gitignore:1
Data Directory Management
The data/* directory .gitignore2 is excluded for several reasons:
- Size: Mosquitto may accumulate significant message data
- Privacy: MQTT messages might contain sensitive IoT data
- State: Runtime state should not be in version control
- Portability: Each deployment should have independent state
The directory is automatically created by the Mosquitto container when it starts.
Sources: .gitignore:2
Commit Hygiene for CI/CD
DO:
- Commit workflow YAML files .github/workflows/
- Commit public configuration mosquitto.conf
- Update
.env.samplewhen adding new required variables - Include descriptive commit messages for CI changes
DON’T:
- Commit
.envfiles with actual tokens - Commit
data/*runtime directories - Hardcode secrets in workflow files
- Bypass CI by pushing directly to
main
Security Checklist
Before committing changes related to CI/CD:
- Verify
.gitignoreexcludes.env - Confirm workflow files contain no hardcoded secrets
- Check that
.env.sampleis up-to-date - Ensure CI workflow would catch your changes
- Review
git statusfor untracked secret files - Use
git diff --cachedto review staged changes
Sources: .gitignore:1-2, .github/workflows/ci.yml, .github/workflows/build-docs.yml
Testing Strategy
The automated testing focuses on validating successful service startup rather than functional MQTT testing:
| Test Aspect | Implementation | Location |
|---|---|---|
| Trigger | Push or PR to main branch | .github/workflows/ci.yml:3-9 |
| Environment | ubuntu-latest runner | .github/workflows/ci.yml13 |
| Service Under Test | mosquitto only | .github/workflows/ci.yml25 |
| Health Check | Container status polling | .github/workflows/ci.yml:28-39 |
| Timeout | 100 seconds (10 × 10s) | .github/workflows/ci.yml29 |
| Cleanup | Always runs teardown | .github/workflows/ci.yml:41-42 |
Note that the cloudflared service is not tested in CI because it requires a valid CLOUDFLARE_TUNNEL_TOKEN, which is not available in the test environment. The CI validates only that the mosquitto service can successfully start in isolation.
Sources: .github/workflows/ci.yml
Development Commands
Developers use the following docker-compose commands during development:
| Command | Purpose | Effect |
|---|---|---|
docker-compose up -d | Start all services in background | Starts both mosquitto and cloudflared |
docker-compose up -d mosquitto | Start only mosquitto | Starts mosquitto without cloudflared |
docker-compose ps | Check service status | Shows running containers and their state |
docker-compose logs -f mosquitto | View mosquitto logs | Streams log output in real-time |
docker-compose down | Stop and remove services | Stops containers and removes them |
docker-compose restart mosquitto | Restart mosquitto | Stops and starts the service |
These commands interact with the service definitions in docker-compose.yml:3-17
Sources: docker-compose.yml, .github/workflows/ci.yml
Configuration Management
Environment Variables
The system uses environment variable substitution in docker-compose.yml docker-compose.yml:14-17:
The CLOUDFLARE_TUNNEL_TOKEN variable must be defined in the .env file for the cloudflared service to authenticate with Cloudflare’s network.
Volume Mounts
The mosquitto configuration is provided via volume mount docker-compose.yml:7-8:
This allows developers to modify mosquitto.conf and restart the service without rebuilding any Docker images.
Sources: docker-compose.yml
Next Steps for Developers
After understanding this overview:
- Setup Local Environment: Follow the detailed instructions in Local Development Setup to configure your workstation
- Understand CI Pipeline: Review CI/CD Pipeline for details on how automated tests validate changes
- Learn Version Control: Read Version Control Practices for guidelines on managing secrets and contributions
For advanced configuration topics such as ACLs and encryption, see Advanced Topics.
Sources: All files in this repository
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Continuous Integration
Loading…
Continuous Integration
Relevant source files
Purpose and Scope
This page documents the automated testing workflow defined in .github/workflows/ci.yml:1-43 that validates the mosquitto service can be successfully deployed. The workflow triggers on push and pull request events to the main branch and verifies the Mosquitto MQTT broker container starts and reaches a running state.
Scope : Tests the mosquitto service only. The cloudflared service is excluded because it requires CLOUDFLARE_TUNNEL_TOKEN which cannot be securely provided in CI environments.
Workflow Overview
The CI workflow (.github/workflows/ci.yml1) defines a single job test-mosquitto (.github/workflows/ci.yml12) that runs on ubuntu-latest (.github/workflows/ci.yml13):
| Attribute | Value |
|---|---|
| Workflow Name | CI |
| Job Name | test-mosquitto |
| Runner | ubuntu-latest |
| Service Tested | mosquitto from docker-compose.yml:4-9 |
| Triggers | push to main, pull_request to main |
| Validation | Container state inspection with 10-attempt retry loop |
| Timeout | 100 seconds (10 × 10s) |
Sources: .github/workflows/ci.yml:1-43 docker-compose.yml:4-9
Workflow Configuration
Trigger Configuration
The workflow executes on two GitHub event types:
Sources: .github/workflows/ci.yml:3-9
Job Architecture
test-mosquitto Job Flow
Sources: .github/workflows/ci.yml:1-43 docker-compose.yml:4-9
Execution Sequence
Sources: .github/workflows/ci.yml:1-43
Workflow Steps
Step 1: Checkout repository
Uses actions/checkout@v2 (.github/workflows/ci.yml:16-17) to clone the repository, making docker-compose.yml:1-18 and mosquitto.conf:1-5 available to subsequent steps.
Sources: .github/workflows/ci.yml:16-17
Step 2: Set up Docker Compose
Installs docker-compose via apt-get (.github/workflows/ci.yml:19-22). Docker Engine is pre-installed on ubuntu-latest, but Docker Compose requires explicit installation.
Sources: .github/workflows/ci.yml:19-22
Step 3: Start Mosquitto service using Docker Compose
Executes docker-compose up -d mosquitto (.github/workflows/ci.yml:24-25), starting only the mosquitto service from docker-compose.yml:4-9 The -d flag runs the container in detached mode. The cloudflared service (docker-compose.yml:11-17) is excluded because it requires CLOUDFLARE_TUNNEL_TOKEN.
Sources: .github/workflows/ci.yml:24-25 docker-compose.yml:4-9
Step 4: Wait for Mosquitto to be healthy
Implements a bounded retry loop (.github/workflows/ci.yml:27-39):
| Parameter | Value |
|---|---|
| Max Attempts | 10 |
| Sleep Interval | 10 seconds |
| Total Timeout | 100 seconds |
| Inspection Command | docker inspect --format='{{.State.Status}}' mosquitto |
| Success Condition | Status equals running |
Health Check Logic Flow
Sources: .github/workflows/ci.yml:27-39
Step 5: Stop Mosquitto service using Docker Compose
Executes docker-compose down (.github/workflows/ci.yml:41-42) to stop and remove the mosquitto container. This step always runs regardless of health check outcome.
Sources: .github/workflows/ci.yml:41-42
Test Coverage
The workflow validates:
| Aspect | Validation Mechanism |
|---|---|
eclipse-mosquitto:latest image availability | docker-compose up pulls image from Docker Hub |
| docker-compose.yml:4-9 service definition | Container instantiation succeeds |
| mosquitto.conf:1-5 syntax validity | Container starts without error |
Volume mount ./mosquitto.conf:/mosquitto/config/mosquitto.conf | Implicit validation during startup |
| Mosquitto broker process initialization | Container reaches running state |
Sources: .github/workflows/ci.yml:1-43 docker-compose.yml:4-9 mosquitto.conf:1-5
Excluded from Testing
cloudflared Service
The cloudflared service (docker-compose.yml:11-17) is not tested because:
- Requires
CLOUDFLARE_TUNNEL_TOKEN(docker-compose.yml17) - Token is environment-specific and authenticates with Cloudflare’s network
- Exposing a real token in CI logs would create a security vulnerability
The workflow explicitly specifies docker-compose up -d mosquitto (.github/workflows/ci.yml25), omitting the cloudflared service.
Sources: .github/workflows/ci.yml:24-25 docker-compose.yml:11-17
Protocol and Functional Testing
Not tested:
| Excluded Test | Reason |
|---|---|
| MQTT protocol connectivity (ports 1883, 9001) | No MQTT client invoked |
| Message publish/subscribe operations | Requires mosquitto_pub/mosquitto_sub clients |
| WebSocket protocol | No WebSocket connection attempted |
| Authentication/authorization | Configuration uses allow_anonymous true |
| Performance/load testing | Outside scope of health check |
Sources: .github/workflows/ci.yml:1-43 mosquitto.conf:1-5
Success and Failure Conditions
Success
The workflow exits with code 0 (.github/workflows/ci.yml32) when:
Returns true within 10 attempts, indicating:
- Container started with configuration from mosquitto.conf:1-5
- Volume mount
./mosquitto.conf:/mosquitto/config/mosquitto.confloaded successfully - Mosquitto broker process initialized
- Container state is
running
Sources: .github/workflows/ci.yml:29-32 mosquitto.conf:1-5 docker-compose.yml8
Failure
Health Check Timeout
Exits with code 1 (.github/workflows/ci.yml39) after 10 failed attempts:
Waiting for Mosquitto to be healthy...
(10 iterations)
Mosquitto did not become healthy in time
Potential causes:
- Invalid mosquitto.conf:1-5 syntax
- Missing
./mosquitto.conffile - Resource constraints on runner
- Docker Hub rate limiting
Sources: .github/workflows/ci.yml:38-39
Docker Compose Failures
Failures during docker-compose up -d mosquitto (.github/workflows/ci.yml25):
| Failure | Cause |
|---|---|
| Image pull error | Docker Hub connectivity issue |
| YAML parse error | Invalid docker-compose.yml:1-18 syntax |
| Volume mount error | ./mosquitto.conf not found |
Sources: .github/workflows/ci.yml:24-25 docker-compose.yml:1-18
Local Test Replication
Replicate the CI workflow locally:
Sources: .github/workflows/ci.yml:24-42
Debugging Workflow Failures
Failure Diagnosis
Sources: .github/workflows/ci.yml:1-43
Adding Container Log Output
To capture container logs on failure, add this step after .github/workflows/ci.yml39:
Sources: .github/workflows/ci.yml:27-42
Limitations
Test Scope
| Limitation | Impact |
|---|---|
No cloudflared testing | Tunnel connectivity not validated |
| No protocol testing | MQTT/WebSocket functionality not verified |
| No persistence testing | Data retention not checked (no volumes configured) |
| No security testing | Anonymous access not explicitly validated |
Sources: .github/workflows/ci.yml:1-43 mosquitto.conf:1-5
Runner Environment
ubuntu-latest (.github/workflows/ci.yml13) may differ from production:
- Architecture: typically
x86_64 - Network: isolated runner environment
- Resources: GitHub-managed limits
- Docker version: may lag behind latest releases
Sources: .github/workflows/ci.yml13
Timeout Consideration
The 100-second timeout (.github/workflows/ci.yml:29-39) accommodates:
- Docker Hub rate limiting
- Runner load variations
- Network latency
Typical startup time: 5-10 seconds in production.
Sources: .github/workflows/ci.yml:27-39
Potential Enhancements
Extended Coverage
Possible additions to .github/workflows/ci.yml:1-43:
| Enhancement | Implementation |
|---|---|
| MQTT protocol test | Add mosquitto_pub/mosquitto_sub client steps |
| WebSocket test | Add MQTT.js connection step |
Mock cloudflared | Stub tunnel with local proxy |
| Config validation | Run mosquitto -t -c mosquitto.conf |
| ACL testing | Test access control (requires password file) |
Sources: .github/workflows/ci.yml:1-43
Summary
The CI/CD pipeline provides basic validation that the Mosquitto MQTT broker can be successfully deployed using the repository’s Docker Compose configuration. The pipeline:
- Executes on every push and pull request to
main - Tests only the
mosquittoservice (excludescloudflared) - Validates container startup and health within 100 seconds
- Ensures configuration files are syntactically valid
- Provides immediate feedback to developers on infrastructure changes
While limited in scope, this pipeline prevents basic configuration errors from reaching production and provides a foundation for more comprehensive testing in the future.
Sources: .github/workflows/ci.yml:1-43 docker-compose.yml:1-18 mosquitto.conf:1-7
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Documentation Pipeline
Loading…
Documentation Pipeline
Relevant source files
Purpose and Scope
This document describes the automated documentation generation and deployment system that converts repository content into a browsable static site hosted on GitHub Pages. The pipeline uses the DeepWiki documentation generation tool to analyze the codebase and produce structured technical documentation.
For information about the continuous integration testing pipeline, see Continuous Integration. For Git workflow and secret management practices, see Version Control Best Practices.
Overview
The documentation pipeline is implemented as a GitHub Actions workflow that automatically generates and deploys documentation to GitHub Pages. The workflow is defined in .github/workflows/build-docs.yml:1-81 and operates independently of the main CI pipeline. It uses the deepwiki-to-mdbook action to analyze the repository structure and generate mdBook-formatted documentation.
Key Characteristics
| Property | Value |
|---|---|
| Workflow File | .github/workflows/build-docs.yml |
| Trigger Schedule | Weekly (Sundays at midnight UTC) |
| Manual Trigger | Available via workflow_dispatch |
| Documentation Tool | deepwiki-to-mdbook |
| Output Format | mdBook static site |
| Deployment Target | GitHub Pages |
| Permissions Required | contents: read, pages: write, id-token: write |
Sources: .github/workflows/build-docs.yml:1-12
Workflow Triggers
The pipeline supports two activation mechanisms configured in the on section of the workflow:
Scheduled Execution
The workflow executes automatically every Sunday at midnight UTC. This weekly cadence ensures documentation stays reasonably current without excessive build frequency. The cron expression "0 0 * * 0" specifies minute 0, hour 0, any day of month, any month, day 0 (Sunday).
Manual Dispatch
The workflow_dispatch trigger enables manual workflow execution through the GitHub Actions UI. This allows documentation regeneration on-demand when significant repository changes occur or when immediate documentation updates are required.
Sources: .github/workflows/build-docs.yml:3-7
Permissions and Concurrency
Permission Model
The workflow requires three specific permissions defined at the workflow level:
| Permission | Scope | Purpose |
|---|---|---|
contents: read | Repository content | Access source files for documentation generation |
pages: write | GitHub Pages | Deploy generated documentation artifacts |
id-token: write | OIDC token | Authenticate with GitHub Pages deployment service |
Concurrency Control
The pages concurrency group ensures only one documentation deployment runs at a time. The cancel-in-progress: false setting prevents cancellation of in-flight deployments when new builds are triggered, ensuring documentation builds complete even if overlapping triggers occur.
Sources: .github/workflows/build-docs.yml:9-16
Build Job Architecture
The build job executes on ubuntu-latest and consists of five sequential steps that prepare, generate, and package the documentation.
flowchart TD
Trigger["Trigger Event\n(schedule or workflow_dispatch)"]
subgraph BuildJob["build job (ubuntu-latest)"]
CheckoutStep["actions/checkout@v4\nClone repository"]
ResolveStep["Resolve repository and book title\nShell script logic"]
PrepareStep["Prepare output directory\nrm -rf output && mkdir"]
GenerateStep["jzombie/deepwiki-to-mdbook@main\nGenerate documentation"]
UploadStep["actions/upload-pages-artifact@v3\nPackage for Pages"]
end
subgraph DeployJob["deploy job (ubuntu-latest)"]
DeployStep["actions/deploy-pages@v4\nPublish to GitHub Pages"]
end
Trigger --> CheckoutStep
CheckoutStep --> ResolveStep
ResolveStep --> PrepareStep
PrepareStep --> GenerateStep
GenerateStep --> UploadStep
UploadStep --> DeployStep
ResolveStep -.->|repo_value title_value| GenerateStep
Workflow Execution Diagram
Sources: .github/workflows/build-docs.yml:18-69
Repository and Title Resolution
The “Resolve repository and book title” step implements dynamic configuration logic that determines which repository to document and what title to use.
Resolution Logic
The step defined in .github/workflows/build-docs.yml:25-52 executes a shell script with the following logic:
Output Variables
The resolution step produces two output variables accessible to subsequent steps:
| Variable | Source Priority | Example Value |
|---|---|---|
repo_value | 1. Manual input | |
| 2. Current repository | jzombie/docker-mqtt-mosquitto-cloudflare-tunnel | |
title_value | 1. Manual input | |
| 2. Derived from repo name | ||
| 3. Default “Documentation” | docker-mqtt-mosquitto-cloudflare-tunnel Documentation |
Sources: .github/workflows/build-docs.yml:25-52
Documentation Generation
The core documentation generation step uses the jzombie/deepwiki-to-mdbook@main action to analyze the repository and produce mdBook output.
Generation Step Configuration
Input Parameters
| Parameter | Value Source | Purpose |
|---|---|---|
repo | steps.resolve.outputs.repo_value | Repository identifier for DeepWiki analysis |
book_title | steps.resolve.outputs.title_value | Title displayed in generated documentation |
output_dir | ./output | Directory where mdBook site is generated |
Output Directory Structure
The generation step produces the following directory structure:
output/
└── book/
├── index.html
├── *.html (generated pages)
└── (mdBook assets)
The output/book/ directory contains the complete static site ready for deployment.
Sources: .github/workflows/build-docs.yml:59-64
GitHub Pages Deployment
The deployment process uses GitHub’s standard Pages deployment actions to publish the generated documentation.
Artifact Upload
The actions/upload-pages-artifact@v3 action packages the ./output/book directory as a GitHub Actions artifact specifically formatted for Pages deployment. This artifact is stored temporarily and consumed by the deployment job.
Deployment Job
The deploy job:
- Depends on successful completion of the
buildjob vianeeds: build - Targets the
github-pagesenvironment - Exposes the deployment URL via
steps.deployment.outputs.page_url - Uses
actions/deploy-pages@v4to publish the artifact
Sources: .github/workflows/build-docs.yml:66-80
Pipeline Data Flow
Sources: .github/workflows/build-docs.yml:1-81
Pipeline Behavior and Characteristics
Idempotency
The pipeline is fully idempotent. Multiple executions with the same repository state produce identical documentation output. The preparation step .github/workflows/build-docs.yml:54-57 explicitly removes and recreates the output directory to ensure clean builds.
Failure Modes
| Failure Point | Effect | Recovery |
|---|---|---|
| Repository checkout fails | Build job fails, no deployment | Retry workflow |
| Resolution step fails | Build job fails, outputs undefined | Check step logic |
| DeepWiki generation fails | Build job fails, no artifact | Review repository structure |
| Artifact upload fails | Deployment job cannot start | Retry workflow |
| Pages deployment fails | Old documentation remains live | Check Pages configuration |
Build Duration
Typical pipeline execution times:
- Repository checkout: 5-10 seconds
- Documentation generation: 2-5 minutes (varies with repository size)
- Artifact upload: 10-30 seconds
- Pages deployment: 30-60 seconds
- Total pipeline duration: ~3-7 minutes
Sources: .github/workflows/build-docs.yml:18-80
Integration with Repository
The documentation pipeline operates independently but maintains integration with the repository through several mechanisms:
Repository Settings Requirements
For the pipeline to function, the repository must have GitHub Pages enabled:
- Navigate to Settings → Pages
- Set Source to “GitHub Actions”
- No branch selection required (Actions-based deployment)
Permission Configuration
The workflow uses the default GITHUB_TOKEN with elevated permissions defined in .github/workflows/build-docs.yml:9-12 No additional secrets or tokens are required.
Workflow Dependencies
Sources: .github/workflows/build-docs.yml:9-16
Customization and Extension
Modifying Schedule
To change the build frequency, edit the cron expression in .github/workflows/build-docs.yml:5-6:
Customizing Book Title
The book title can be overridden by:
- Manual workflow dispatch with custom
book_titleinput - Modifying the derived title logic in .github/workflows/build-docs.yml:41-46
Output Directory
The output directory is hardcoded as ./output in multiple steps:
- Preparation: .github/workflows/build-docs.yml56
- Generation: .github/workflows/build-docs.yml64
- Upload: .github/workflows/build-docs.yml69
Changing the directory requires updating all three references.
Sources: .github/workflows/build-docs.yml:4-69
Monitoring and Verification
Workflow Execution Status
View workflow runs at: https://github.com/<owner>/<repo>/actions/workflows/build-docs.yml
Each run shows:
- Trigger type (scheduled or manual)
- Execution time
- Build job status
- Deploy job status
- Deployed Pages URL
Deployment Verification
After successful deployment:
- Navigate to repository Settings → Pages
- Verify the displayed URL matches expected documentation site
- Access the URL to confirm documentation content
Troubleshooting Failed Builds
Check the workflow logs for common issues:
- Repository checkout fails : Verify repository permissions
- DeepWiki generation fails : Check repository structure and content
- Artifact upload fails : Verify GitHub Actions storage limits
- Pages deployment fails : Confirm Pages is enabled and configured for Actions
Sources: .github/workflows/build-docs.yml:1-81
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Version Control Best Practices
Loading…
Version Control Best Practices
Relevant source files
Purpose and Scope
This document explains the version control practices and configurations implemented in this repository to ensure secure handling of secrets and maintain a clean Git history. It focuses on the .gitignore configuration, the template-based approach for sensitive configuration, and recommended workflows for committing changes safely.
For information about the automated CI/CD workflows themselves, see Continuous Integration and Documentation Pipeline. For general configuration management, see Configuration Reference.
File Classification Strategy
The repository implements a strict separation between code/documentation (version controlled) and secrets/runtime data (explicitly excluded). This classification prevents accidental exposure of sensitive information.
graph TB
subgraph VersionControlled["Version Controlled (Committed to Git)"]
DockerCompose["docker-compose.yml\nService orchestration"]
MosquittoConf["mosquitto.conf\nBroker configuration"]
EnvSample[".env.sample\nSecret template"]
Workflows["GitHub Actions workflows\n.github/workflows/*"]
README["README.md\nDocumentation"]
Gitignore[".gitignore\nExclusion rules"]
end
subgraph Ignored["Explicitly Ignored (Never Committed)"]
EnvFile[".env\nContains CLOUDFLARE_TUNNEL_TOKEN"]
DataDir["data/*\nRuntime broker data"]
end
subgraph Runtime["Runtime Environment"]
DockerEngine["Docker Engine"]
CloudflaredContainer["cloudflared container"]
MosquittoContainer["mosquitto container"]
end
DockerCompose -->|orchestrates| DockerEngine
MosquittoConf -->|mounted into| MosquittoContainer
EnvSample -->|developer copies to| EnvFile
Gitignore -->|excludes| EnvFile
Gitignore -->|excludes| DataDir
EnvFile -->|loaded by docker-compose| CloudflaredContainer
DataDir -->|mounted into| MosquittoContainer
File Classification Diagram
Sources : .gitignore:1-2 .env.sample1 docker-compose.yml
The .gitignore Configuration
The .gitignore file serves as the primary security boundary, preventing sensitive files from being committed to version control.
Current Exclusions
| Pattern | Purpose | Risk if Committed |
|---|---|---|
.env | Environment variables containing CLOUDFLARE_TUNNEL_TOKEN | Exposes tunnel access credentials, allowing unauthorized traffic routing |
data/* | Mosquitto runtime data (messages, persistence files) | May contain sensitive message content, creates unnecessary repository bloat |
Sources : .gitignore:1-2
Security Implications
The .env file exclusion is critical because it contains the CLOUDFLARE_TUNNEL_TOKEN value. This token provides authentication to route traffic through your Cloudflare Tunnel. If committed to a public repository (or even a private one with multiple collaborators), the token could be:
- Extracted from Git history - Even if deleted in a later commit, the token remains in Git history
- Used to route unauthorized traffic - An attacker with the token can send traffic through your tunnel
- Difficult to rotate - Revoking and regenerating the token requires reconfiguring the entire Cloudflare Tunnel
Sources : .env.sample1 .gitignore1
Template-Based Secret Management
The repository uses a template file pattern to document required secrets without exposing actual values.
sequenceDiagram
participant Dev as "Developer"
participant Repo as "Git Repository"
participant Local as "Local Filesystem"
participant Docker as "Docker Compose"
participant CF as "Cloudflare API"
Note over Repo: .env.sample checked into Git\nNote over Repo: .gitignore excludes .env
Dev->>Repo: git clone repository
Repo->>Local: .env.sample available
Repo->>Local: .gitignore rules applied
Dev->>CF: Create tunnel via dashboard
CF-->>Dev: Receive CLOUDFLARE_TUNNEL_TOKEN
Dev->>Local: cp .env.sample .env
Dev->>Local: Edit .env with actual token
Note over Local: .env now contains secret
Dev->>Docker: docker-compose up
Docker->>Local: Read .env file
Local-->>Docker: CLOUDFLARE_TUNNEL_TOKEN value
Docker->>Docker: Inject into cloudflared container
Note over Dev,Local: .env never committed due to .gitignore
Template File Flow
Sources : .env.sample1 .gitignore1
The .env.sample Template
The .env.sample file serves three purposes:
- Documentation - Shows developers what environment variables are required
- Template - Provides copy-paste structure for creating the actual
.envfile - Placeholder Values - Uses obvious placeholder text (
your_token) that will fail if not replaced
Content:
CLOUDFLARE_TUNNEL_TOKEN=your_token
This template is intentionally minimal. The placeholder value your_token will cause the tunnel connection to fail, forcing developers to replace it with a real token from the Cloudflare dashboard.
Sources : .env.sample1
Protected Files and Their Purposes
Files That Are Safe to Commit
| File | Contains Secrets? | Why It’s Safe |
|---|---|---|
docker-compose.yml | No | References environment variable by name (${CLOUDFLARE_TUNNEL_TOKEN}), not value |
mosquitto.conf | No | Only contains port numbers and protocol settings |
.env.sample | No | Contains placeholder text, not real secrets |
README.md | No | Documentation only |
Sources : .env.sample1 docker-compose.yml mosquitto.conf
Files That Must Never Be Committed
| File/Directory | Contains Secrets? | Contents |
|---|---|---|
.env | YES | Real CLOUDFLARE_TUNNEL_TOKEN value |
data/* | Potentially | MQTT message persistence, retained messages, subscription state |
Sources : .gitignore:1-2
Git Workflow Best Practices
Pre-Commit Verification Process
Sources : .gitignore:1-2
Recommended Workflow Steps
-
Initial Setup (One-time per developer)
- Clone repository:
git clone <repository-url> - Verify
.gitignoreexists:cat .gitignore - Copy template:
cp .env.sample .env - Populate
.envwith real token from Cloudflare dashboard - Verify exclusion:
git statusshould not show.env
- Clone repository:
-
Before Each Commit
- Review staged files:
git status - Confirm
.envis NOT in the staged files list - Confirm
data/*is NOT in the staged files list - If either appears, investigate why (usually means
.gitignoreis missing or misconfigured)
- Review staged files:
-
After Cloning on a New Machine
- The
.envfile will not exist (this is correct) - Must manually create
.envfrom.env.sampletemplate - Must obtain new token from Cloudflare or securely transfer existing token
- The
Sources : .gitignore:1-2 .env.sample1
Verification Commands
Checking Gitignore Effectiveness
Sources : .gitignore1
Staged Files Safety Check
Before committing, run this command to list all files that will be committed:
This output should never include:
.env- Any file under
data/
If either appears, do not commit. Instead:
Sources : .gitignore:1-2
sequenceDiagram
participant Admin as "Administrator"
participant CF as "Cloudflare Dashboard"
participant Local as "Local .env File"
participant Docker as "Docker Environment"
participant Git as "Git Repository"
Note over Admin,Git: Token Rotation Process
Admin->>CF: Regenerate tunnel token
CF-->>Admin: New CLOUDFLARE_TUNNEL_TOKEN
Admin->>Local: Update .env with new token
Note over Local: Old token still in file
Admin->>Docker: docker-compose down
Admin->>Docker: docker-compose up
Docker->>Local: Read updated .env
Note over Docker: New token now active
Note over Admin,Git: Verification
Admin->>Git: git status
Git-->>Admin: .env not in changes
Note over Admin: Confirm .gitignore still protecting .env
Admin->>CF: Verify tunnel connected
CF-->>Admin: Tunnel active with new token
Secret Rotation Procedure
When the CLOUDFLARE_TUNNEL_TOKEN needs to be rotated (compromised token, security policy, or regular rotation):
Rotation Flow Diagram
Sources : .env.sample1 .gitignore1
Step-by-Step Rotation
- Generate new token in Cloudflare Dashboard (Networks → Tunnels → Select tunnel → Configure)
- Update local
.envfile with newCLOUDFLARE_TUNNEL_TOKENvalue - Restart services :
docker-compose down && docker-compose up -d - Verify exclusion : Run
git statusto confirm.envis not staged - Test connection : Verify tunnel connects with new token
- Revoke old token (optional, if Cloudflare UI supports it)
Important : The .env file is never committed during this process. The token change remains local to each deployment environment.
Sources : .env.sample1 .gitignore1
Common Mistakes to Avoid
Mistake 1: Committing .env Before Adding .gitignore
Scenario : Developer commits .env file before creating .gitignore.
Result : Token is now in Git history permanently.
Prevention :
- Ensure
.gitignoreexists before first commit - This repository includes
.gitignorefrom initial commit
Recovery (if it happens):
Sources : .gitignore1
Mistake 2: Using .env.sample Directly
Scenario : Developer renames .env.sample to .env instead of copying.
Result : The template file disappears from version control.
Prevention :
- Always use
cp .env.sample .env(copy, not move) - Never run
git rm .env.sample
Sources : .env.sample1
Mistake 3: Accidentally Staging with git add -A
Scenario : Running git add -A or git add . without reviewing files.
Result : Might stage .env if .gitignore is misconfigured or missing.
Prevention :
- Always run
git statusbeforegit add - Use
git add <specific-files>for granular control - Review
git diff --cachedbefore committing
Sources : .gitignore1
Integration with CI/CD
The version control practices integrate with the automated CI/CD pipelines:
CI Pipeline Behavior
The GitHub Actions CI workflow (.github/workflows/ci.yml) does not require the .env file because:
- It only tests the
mosquittoservice, notcloudflared - The Mosquitto broker does not require the
CLOUDFLARE_TUNNEL_TOKEN - Secrets would be injected via GitHub Secrets for full-stack testing (not implemented in this repository)
Sources : .github/workflows/ci.yml docker-compose.yml
Documentation Pipeline
The documentation build workflow (.github/workflows/build-docs.yml) operates on the committed files only:
- Reads
.env.sample(committed) to document the template format - Never accesses
.env(ignored) as it’s not present in the repository - Builds static documentation without requiring runtime secrets
Sources : .github/workflows/build-docs.yml .env.sample1
Production Environment Considerations
For production deployments, additional secret management practices should be considered:
Production Secret Management Table
| Environment | Secret Storage | Injection Method | Rotation Strategy |
|---|---|---|---|
| Local Development | .env file | docker-compose reads file | Manual edit + restart |
| CI/CD | GitHub Secrets | Environment variables in workflow | Update in GitHub UI |
| Production | Secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) | Environment variables via orchestration | Automated rotation with zero-downtime |
For production deployments beyond local development, consider:
- Secrets Manager Integration - Replace
.envfile with calls to a secrets management service - Automated Rotation - Implement automatic token rotation with zero-downtime restarts
- Audit Logging - Track when secrets are accessed and by whom
- Encryption at Rest - Ensure secrets are encrypted when stored on disk
Sources : .env.sample1
Summary of Key Practices
- Never commit
.env- The.gitignoreconfiguration prevents this - Always use
.env.sampleas template - Copy, don’t move - Verify before each commit - Run
git statusand check for excluded files - Rotate tokens out-of-band - Token updates never touch Git
- Audit Git history - Ensure secrets never entered version control
These practices implement the principle of “separation of code and configuration,” where sensitive credentials are injected at runtime rather than embedded in version-controlled artifacts.
Sources : .gitignore:1-2 .env.sample1
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Advanced Topics
Loading…
Advanced Topics
Relevant source files
This page covers advanced configuration options and production-ready features available in the protected-no-wildcard branch of the repository. While the main branch provides a straightforward MQTT broker setup with anonymous access and no encryption, the advanced branch introduces ACL-based topic restrictions, encrypted retained message storage, and additional safeguards for multi-tenant environments.
For step-by-step deployment instructions, see Getting Started. For basic component documentation, see Components. This page focuses on enhanced security features and production considerations not present in the base configuration.
Sources : README.md:5-11
Advanced Branch Overview
The repository maintains two primary branches with different security and feature profiles:
| Feature | main Branch | protected-no-wildcard Branch |
|---|---|---|
| Anonymous Access | Enabled (mosquitto.conf2) | User authentication required |
| Topic Access Control | None | ACL file with user-based restrictions |
| Wildcard Topic Queries | Unrestricted | Restricted to user’s own topics |
| Retained Message Storage | Plaintext | Encrypted with gocryptfs |
| Message Persistence | Manual save | Auto-save after every message |
| Use Case | Development, trusted environments | Production, multi-tenant systems |
The protected-no-wildcard branch addresses security concerns in scenarios where:
- Multiple users share a single MQTT broker
- Topic privacy between users must be enforced
- Retained messages contain sensitive data requiring encryption at rest
- Filesystem-level message persistence protection is needed
Sources : README.md:5-11
Architecture Comparison
The following diagram illustrates the architectural differences between the two branches:
Diagram: Main vs Protected Branch Architecture
graph TB
subgraph "main Branch Architecture"
direction TB
MQ_Main["mosquitto Container\n(main branch)"]
Conf_Main["mosquitto.conf\nallow_anonymous: true"]
Data_Main["data/\nPlaintext Storage"]
Conf_Main -->|Configures| MQ_Main
MQ_Main -->|Writes Retained Messages| Data_Main
end
subgraph "protected-no-wildcard Branch Architecture"
direction TB
MQ_Protected["mosquitto Container\n(protected-no-wildcard)"]
Conf_Protected["mosquitto.conf\nallow_anonymous: false\nacl_file /mosquitto/config/aclfile"]
ACL["aclfile\nUser-based Topic Rules"]
Gocryptfs["gocryptfs Container\nEncryption Layer"]
Data_Encrypted["data-encrypted/\nEncrypted Filesystem"]
Data_Decrypted["data/\nDecrypted Mount Point"]
Conf_Protected -->|Configures| MQ_Protected
ACL -->|Restricts Topics| MQ_Protected
MQ_Protected -->|Writes to| Data_Decrypted
Gocryptfs -->|Mounts| Data_Decrypted
Gocryptfs -->|Encrypts to| Data_Encrypted
end
Client_Main["MQTT Client\n(Any)"]
Client_Protected["MQTT Client\n(Authenticated)"]
Client_Main -->|Anonymous Connect| MQ_Main
Client_Protected -->|Username/Password| MQ_Protected
The protected branch introduces two key architectural components not present in the main branch:
- ACL File : Located at mosquitto/aclfile in the protected branch, this file defines user-specific topic access patterns
- Gocryptfs Layer : A separate container that provides transparent encryption for the
data/directory
Sources : README.md:5-11 mosquitto.conf:1-6
Topic Access Control (ACL)
The protected-no-wildcard branch implements an ACL (Access Control List) system that restricts MQTT topic access based on username. The implementation follows a naive but effective pattern where:
- The first level of each topic must match the authenticated username
- Users can only publish and subscribe to topics under their own namespace
- Wildcard subscriptions (
#,+) are restricted to prevent cross-user topic enumeration
Example ACL Pattern :
# User "alice" can only access topics starting with "alice/"
user alice
topic readwrite alice/#
# User "bob" can only access topics starting with "bob/"
user bob
topic readwrite bob/#
This approach prevents a scenario where user alice subscribes to bob/# or # to discover or intercept messages from other users.
For detailed ACL configuration and examples, see Topic Access Control (ACL).
Sources : README.md7
Encrypted Retained Messages
MQTT retained messages persist on the broker’s filesystem and are delivered to new subscribers. In the main branch, these messages are stored in plaintext within the data/ directory. The protected-no-wildcard branch adds filesystem-level encryption using gocryptfs.
Data Flow with Encryption :
The encryption operates transparently to Mosquitto. The broker reads and writes to data/ as if it were a normal directory, while gocryptfs handles encryption/decryption automatically. The actual encrypted data resides in data-encrypted/, which is stored on the Docker host filesystem.
Key characteristics:
- Algorithm : AES-256-GCM authenticated encryption
- Scope : Encrypts retained messages only (not in-flight messages)
- Performance : Minimal overhead for typical MQTT workloads
- Recovery : Requires the encryption password to mount the filesystem
For implementation details and configuration, see Encrypted Retained Messages.
Sources : README.md:8-9
Auto-Save Retained Messages
The protected-no-wildcard branch includes functionality to automatically persist retained messages to disk after every message write. This contrasts with Mosquitto’s default behavior where:
- In-memory messages may be lost on unclean shutdown
- Persistence is typically triggered by shutdown signals or intervals
The auto-save feature ensures that:
- Each retained message write triggers a disk synchronization
- Retained messages survive unexpected container restarts
- Message persistence guarantees are stronger in high-availability scenarios
Trade-offs :
- Pros : Better data durability, reduced risk of message loss
- Cons : Increased I/O operations, potential performance impact for high-volume retained message workloads
Sources : README.md9
Branch Comparison and Migration
The following table summarizes when to use each branch:
| Scenario | Recommended Branch | Rationale |
|---|---|---|
| Local development | main | Simpler configuration, no authentication overhead |
| Single trusted user | main | Anonymous access is sufficient for trusted environments |
| IoT prototyping | main | Rapid iteration without user management |
| Multi-tenant deployment | protected-no-wildcard | Topic isolation prevents cross-user access |
| Production with sensitive data | protected-no-wildcard | Encryption protects retained messages at rest |
| High-availability requirements | protected-no-wildcard | Auto-save ensures message persistence |
| Public-facing broker | protected-no-wildcard | Authentication and ACLs prevent abuse |
Migration Path : Moving from main to protected-no-wildcard requires:
- Adding user authentication credentials to Mosquitto configuration
- Creating an ACL file with appropriate topic rules
- Configuring and initializing the
gocryptfsencrypted filesystem - Updating client applications to provide authentication credentials
- Restructuring topic hierarchies to follow the username-first pattern
Sources : README.md:5-11
Accessing Advanced Features
To explore or deploy the advanced features:
-
View the diff : Compare the branches to understand specific changes
-
Checkout the branch :
-
Review documentation : Each subsection below provides detailed configuration instructions:
- Topic Access Control (ACL) - User authentication and ACL file configuration
- Encrypted Retained Messages - Gocryptfs setup and encryption key management
- Production Considerations - Scaling, monitoring, and hardening recommendations
Sources : README.md11
Configuration File Locations
The following files are specific to the protected-no-wildcard branch and do not exist in main:
| File Path | Purpose | Branch |
|---|---|---|
mosquitto/aclfile | Defines user-based topic access rules | protected-no-wildcard only |
mosquitto/passwordfile | Stores hashed user credentials | protected-no-wildcard only |
docker-compose.yml (modified) | Includes gocryptfs service definition | protected-no-wildcard only |
data-encrypted/ | Encrypted filesystem storage directory | protected-no-wildcard only |
Files that exist in both branches but have different configurations:
| File Path | Main Branch | Protected Branch |
|---|---|---|
| mosquitto.conf2 | allow_anonymous true | allow_anonymous false |
| mosquitto.conf | No acl_file directive | Includes acl_file /mosquitto/config/aclfile |
| mosquitto.conf | No password_file directive | Includes password_file /mosquitto/config/passwordfile |
Sources : README.md7 mosquitto.conf:1-6
Next Steps : For detailed implementation guides for each advanced feature, proceed to the subsections:
- Topic Access Control (ACL) for multi-user topic isolation
- Encrypted Retained Messages for filesystem encryption setup
- Production Considerations for deployment best practices
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Protected Branch Features
Loading…
Protected Branch Features
Relevant source files
Purpose and Scope
This document describes the advanced security features available in the protected-no-wildcard branch of this repository. These features provide enhanced access control, message encryption, and persistence capabilities beyond the base configuration in the main branch.
The protected branch implements three key enhancements:
- ACL-based wildcard topic restrictions
- Encrypted storage for retained messages
- Automatic persistence of retained messages
For basic deployment and configuration information, see Getting Started. For general production deployment considerations, see Production Deployment Considerations.
Sources : README.md:7-13
Overview of the Protected Branch
The protected-no-wildcard branch provides an alternative deployment configuration that addresses specific security and privacy requirements for multi-tenant or privacy-sensitive MQTT deployments. Unlike the main branch which provides anonymous access and no encryption, the protected branch implements defense-in-depth security measures.
Branch Access
| Resource | Location |
|---|---|
| Protected Branch | [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) |
| Diff from Main | [main…protected-no-wildcard comparison](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/main…protected-no-wildcard comparison) |
| ACL File | mosquitto/aclfile (in protected branch) |
The protected branch maintains the same container architecture and Cloudflare Tunnel integration as the main branch, adding security layers within the Mosquitto container and its configuration.
Sources : README.md:7-13
Wildcard Restriction via ACL File
Access Control List Implementation
The protected branch implements topic-level access control using Mosquitto’s ACL (Access Control List) system. The ACL file enforces a topic namespace convention where the first level of each topic path represents a username, preventing users from subscribing to wildcard patterns that cross user boundaries.
Topic Namespace Convention
<username>/<application>/<device>/<metric>
In this pattern:
<username>is the first-level topic segment- Users can only access topics beginning with their own username
- Wildcard subscriptions like
#or+/+/+are restricted to prevent cross-user data access
flowchart TD
Client["MQTT Client"] -->|SUBSCRIBE user1/sensors/#| CloudflareTunnel["Cloudflare Tunnel"]
CloudflareTunnel -->|Forward to mosquitto:9001| Cloudflared["cloudflared Container"]
Cloudflared --> MosquittoListener["Mosquitto WebSocket Listener\nPort 9001"]
MosquittoListener --> ACLCheck{"ACL Permission Check"}
ACLCheck -->|Load Rules| ACLFile["mosquitto/aclfile"]
ACLFile -->|Pattern Matching| ACLCheck
ACLCheck -->|Allowed| TopicTree["Topic Subscription Tree"]
ACLCheck -->|Denied| RejectSub["SUBACK with Failure Code"]
TopicTree --> MessageRoute["Route Messages to Client"]
RejectSub --> Client
MessageRoute --> Client
Publisher["Publishing Client"] -->|PUBLISH user2/data| CloudflareTunnel
CloudflareTunnel --> Cloudflared
Cloudflared --> MosquittoListener
MosquittoListener --> ACLCheckPub{"ACL Publish Check"}
ACLCheckPub -->|Read aclfile| ACLFile
ACLCheckPub -->|Allowed| TopicTree
ACLCheckPub -->|Denied| RejectPub["PUBACK with Error"]
RejectPub --> Publisher
ACL Enforcement Flow
Diagram : ACL enforcement flow showing how the aclfile mediates access to topics based on username-prefixed patterns.
Sources : README.md9
Naive Implementation Characteristics
The README describes this ACL implementation as “naive” because it relies on a simple convention rather than cryptographic authentication:
| Characteristic | Implementation Detail |
|---|---|
| Authentication | Users are not cryptographically authenticated; ACL assumes username is provided correctly |
| Namespace Convention | First-level topic segment must match username |
| Wildcard Blocking | Prevents # and multi-level + patterns that span users |
| Trust Model | Assumes clients accurately identify themselves |
This approach provides separation of concerns and data isolation in scenarios where clients are trusted or authenticated through external mechanisms (e.g., Cloudflare Access policies, API tokens in headers).
Sources : README.md9
Encrypted Retained Messages with gocryptfs
flowchart TB
subgraph "Docker Host Filesystem"
EncryptedVol["Encrypted Volume\ngocryptfs ciphertext"]
end
subgraph "mosquitto Container"
GocryptfsMount["gocryptfs FUSE Mount"]
PlaintextView["Plaintext View\n/mosquitto/data"]
MosquittoProcess["Mosquitto Process"]
PersistenceDir["persistence_location\nConfiguration Directive"]
end
subgraph "Message Lifecycle"
RetainedMsg["Retained Message\nMQTT PUBLISH with retain=true"]
WriteOperation["Write to Persistence Store"]
ReadOperation["Read from Persistence Store"]
DecryptedMsg["Decrypted Message\nDelivered to Subscriber"]
end
EncryptedVol <-->|FUSE System Calls| GocryptfsMount
GocryptfsMount -->|Transparent Encryption| PlaintextView
PlaintextView <--> MosquittoProcess
MosquittoProcess -->|References| PersistenceDir
RetainedMsg --> MosquittoProcess
MosquittoProcess --> WriteOperation
WriteOperation -->|Plaintext Write| PlaintextView
PlaintextView -->|Encrypted on Disk| GocryptfsMount
GocryptfsMount -->|Decrypt on Read| PlaintextView
PlaintextView --> ReadOperation
ReadOperation --> MosquittoProcess
MosquittoProcess --> DecryptedMsg
Overview
The protected branch integrates gocryptfs to encrypt Mosquitto’s retained message store at rest. This prevents unauthorized access to message content if the underlying storage volume is compromised or accessed outside the container.
gocryptfs Integration Architecture
Diagram : gocryptfs provides transparent encryption/decryption between Mosquitto’s plaintext operations and the encrypted on-disk storage.
Sources : README.md10
Encryption Properties
| Property | Value |
|---|---|
| Encryption Type | Filesystem-level encryption via FUSE |
| Cipher | Determined by gocryptfs (typically AES-256-GCM) |
| Key Storage | Configured at gocryptfs mount initialization |
| Performance | FUSE overhead; transparent to Mosquitto |
| Scope | Only retained messages in persistence_location |
Data Flow: Publishing a Retained Message
Diagram : Sequence showing how a retained message is transparently encrypted during the persistence operation.
Sources : README.md:10-11
Auto-Save Retained Messages
Persistence Trigger Mechanism
The protected branch configures Mosquitto to automatically save retained messages to persistent storage after every message operation. This differs from the default behavior where Mosquitto may defer writes to optimize performance.
Configuration Directive
The auto-save behavior is controlled by the autosave_interval directive in mosquitto.conf:
autosave_interval 1
Setting autosave_interval to 1 instructs Mosquitto to write the in-memory retained message database to disk after every message change (publish or removal of a retained message).
Sources : README.md11
Auto-Save Behavior Comparison
| Configuration | Behavior | Use Case |
|---|---|---|
| Main Branch | Default interval (1800 seconds) | Standard deployments with container restart tolerance |
| Protected Branch | Interval of 1 second | Data-critical deployments requiring immediate persistence |
Auto-Save State Machine
Diagram : State machine showing auto-save behavior triggered after each retained message operation.
Sources : README.md11
Performance Implications
| Aspect | Impact |
|---|---|
| Disk I/O | Increased write operations on every retained message |
| Latency | Minimal impact on PUBACK latency (async write) |
| Durability | Near-zero data loss on container crash |
| Throughput | May limit sustained retained message publish rate |
flowchart LR
subgraph "Main Branch Architecture"
MainMosq["mosquitto Container\n- Standard config\n- Anonymous access\n- No encryption"]
MainCF["cloudflared Container\n- Standard tunnel"]
MainVol["Volume: ./mosquitto.conf"]
MainVol --> MainMosq
MainCF --> MainMosq
end
subgraph "Protected Branch Architecture"
ProtMosq["mosquitto Container\n- ACL enabled\n- autosave_interval=1\n- gocryptfs mount"]
ProtCF["cloudflared Container\n- Standard tunnel"]
ProtACL["Volume: ./mosquitto/aclfile"]
ProtConf["Volume: ./mosquitto.conf\n+ ACL directives"]
ProtCrypt["Encrypted Volume\ngocryptfs"]
ProtACL --> ProtMosq
ProtConf --> ProtMosq
ProtCrypt <--> ProtMosq
ProtCF --> ProtMosq
end
For deployments with high-frequency retained message updates, the default autosave_interval may provide better throughput at the cost of potential data loss during abnormal termination.
Architectural Comparison: Main vs Protected Branch
Container Architecture Differences
Diagram : Comparison of container architectures between main and protected branches, highlighting additional components in the protected configuration.
Sources : README.md:7-13
Feature Matrix
| Feature | Main Branch | Protected Branch |
|---|---|---|
| Access Control | Anonymous, unrestricted | ACL-based, username-scoped |
| Wildcard Subscriptions | Allowed globally | Restricted to user namespace |
| Retained Message Encryption | None | gocryptfs transparent encryption |
| Persistence Interval | Default (30 minutes) | Immediate (1 second) |
| Additional Files | None | mosquitto/aclfile |
| Container Complexity | Minimal | Moderate (gocryptfs setup) |
| Suitable For | Development, trusted networks | Multi-tenant, privacy-sensitive |
flowchart TD
subgraph "User Isolation"
User1["User: alice"]
User2["User: bob"]
User3["User: charlie"]
end
subgraph "Topic Namespace"
T1["alice/home/temperature"]
T2["alice/home/humidity"]
T3["bob/sensors/motion"]
T4["bob/sensors/light"]
T5["charlie/devices/status"]
end
subgraph "ACL Enforcement"
ACL["mosquitto/aclfile\npattern: topic readwrite alice/#\npattern: topic readwrite bob/#\npattern: topic readwrite charlie/#"]
end
User1 -->|Allowed| T1
User1 -->|Allowed| T2
User1 -.->|Denied| T3
User1 -.->|Denied| T4
User2 -->|Allowed| T3
User2 -->|Allowed| T4
User2 -.->|Denied| T1
User3 -->|Allowed| T5
User3 -.->|Denied| T1
ACL -.->|Enforces| User1
ACL -.->|Enforces| User2
ACL -.->|Enforces| User3
Use Cases for Protected Branch Features
Multi-Tenant Deployments
In scenarios where multiple users or applications share a single MQTT broker, the ACL-based wildcard restrictions prevent data leakage across tenant boundaries:
Diagram : ACL enforcement creating isolated topic namespaces for multiple users.
Privacy-Sensitive Applications
For deployments handling sensitive data (healthcare, financial, personal information), the gocryptfs encryption ensures that retained messages stored on disk cannot be read without the encryption key:
Use Case: Healthcare IoT
- Medical devices publish patient vitals as retained messages
- Disk snapshots or backups contain only encrypted ciphertext
- Compromise of storage volume does not expose patient data
- Encryption key is managed separately from storage
Data Integrity Critical Systems
The auto-save mechanism ensures minimal data loss in crash scenarios:
| Scenario | Main Branch Impact | Protected Branch Impact |
|---|---|---|
| Container crash | Up to 30 minutes of retained messages lost | Maximum 1 second of messages lost |
| System power loss | Up to 30 minutes of retained messages lost | Maximum 1 second of messages lost |
| Normal shutdown | All messages persisted | All messages persisted |
Migration Between Branches
Switching from Main to Protected
To adopt the protected branch features:
- Review ACL requirements : Determine if your topic structure follows username-prefixed convention
- Configure gocryptfs : Initialize encrypted volume and obtain encryption key
- Update docker-compose.yml : Modify to mount gocryptfs volume and ACL file
- Test ACL rules : Verify wildcard restrictions work as expected
- Backup unencrypted data : Retained messages in main branch are plaintext
Switching from Protected to Main
To revert to the simpler main branch configuration:
- Decrypt retained messages : Use gocryptfs to access plaintext before migration
- Export critical data : Publish non-retained messages if needed for recovery
- Remove ACL restrictions : Understand that all topics become globally accessible
- Switch branch : Check out main branch and restart containers
Sources : README.md13
Configuration Files in Protected Branch
Additional Files
The protected branch introduces files not present in main:
| File | Purpose | Location |
|---|---|---|
mosquitto/aclfile | ACL pattern definitions | Mounted as volume in mosquitto container |
Modified mosquitto.conf | References ACL file, sets autosave_interval=1 | Replaces main branch version |
| gocryptfs initialization scripts | Set up encrypted filesystem | Container initialization |
Modified Configuration Directives
Expected changes to mosquitto.conf in protected branch:
# ACL Configuration
acl_file /mosquitto/config/aclfile
# Persistence Configuration
autosave_interval 1
persistence true
persistence_location /mosquitto/data/
Sources : README.md:9-11
Security Considerations
Threat Model
The protected branch defends against specific threats:
| Threat | Mitigation |
|---|---|
| Cross-tenant data access | ACL wildcard restrictions |
| Disk volume compromise | gocryptfs encryption at rest |
| Data loss on crash | Auto-save immediate persistence |
| Unauthorized topic subscription | ACL pattern matching |
Limitations
The protected branch does NOT protect against:
- Man-in-the-middle attacks (handled by Cloudflare Tunnel TLS)
- Compromised MQTT clients (they can still access their own namespace)
- Memory-resident message inspection (encryption only at rest)
- DoS attacks from authenticated users
For comprehensive security, combine protected branch features with:
- Cloudflare Access policies (see Security Model)
- MQTT authentication plugins
- Rate limiting and message size restrictions
- Network-level monitoring
Sources : README.md9
Summary
The protected-no-wildcard branch extends the base Docker MQTT Mosquitto deployment with three integrated security features:
- ACL-based wildcard restrictions isolate user topics using username-prefixed patterns
- gocryptfs encryption protects retained messages at rest with transparent filesystem encryption
- Auto-save persistence minimizes data loss by writing retained messages after every operation
These features are particularly valuable for multi-tenant deployments, privacy-sensitive applications, and systems requiring high data durability. The trade-offs include increased container complexity, potential performance impact from frequent disk writes, and operational overhead of managing ACL files and encryption keys.
For standard single-tenant or development deployments, the main branch provides a simpler configuration with anonymous access and no encryption. For production deployments requiring enhanced security and isolation, the protected branch implements defense-in-depth measures while maintaining the same Cloudflare Tunnel integration and container orchestration model.
Sources : README.md:7-13
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Production Deployment Considerations
Loading…
Production Deployment Considerations
Relevant source files
Purpose and Scope
This document provides technical guidance for deploying the Docker MQTT Mosquitto with Cloudflare Tunnel system in production environments. It covers security hardening, scalability, high availability, resource management, monitoring, and disaster recovery strategies that extend beyond the basic development setup.
For information about the advanced security features in the alternative branch, see Protected Branch Features. For monitoring implementation details, see Monitoring and Health Checks. For secret management during development, see Version Control Best Practices.
The development configuration provided in docker-compose.yml:1-18 is suitable for testing and proof-of-concept deployments, but requires several modifications for production use. This document identifies these gaps and provides implementation guidance.
Security Hardening
Authentication and Authorization
The default configuration in mosquitto.conf2 sets allow_anonymous true, which permits unauthenticated client connections. This is unacceptable for production deployments.
Production Configuration Requirements:
| Security Control | Development | Production Required |
|---|---|---|
| Anonymous Access | Enabled | Disabled |
| Password Authentication | None | Required |
| ACL (Access Control Lists) | None | Topic-level restrictions |
| TLS Encryption | Cloudflare-managed | End-to-end recommended |
| Connection Limits | Unlimited | Rate-limited |
Implementing Password Authentication
Modify mosquitto.conf:1-6 to include authentication:
listener 1883
allow_anonymous false
password_file /mosquitto/config/password_file
listener 9001
protocol websockets
allow_anonymous false
password_file /mosquitto/config/password_file
Create the password file using mosquitto_passwd:
Update docker-compose.yml:7-8 to mount the password file:
Implementing Topic-Based ACL
Create an ACL file to restrict topic access per user. The [protected-no-wildcard branch](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/protected-no-wildcard branch) demonstrates a username-based topic hierarchy where the first topic level represents the username.
Production ACL Pattern:
# Admin users - full access
user admin
topic readwrite #
# IoT devices - restricted to device-specific topics
user device001
topic readwrite devices/device001/#
user device002
topic readwrite devices/device002/#
# Read-only monitoring users
user monitor
topic read #
Sources: mosquitto.conf:1-6 README.md:9-10
Secret Management
The development approach using .env files is insufficient for production. The CLOUDFLARE_TUNNEL_TOKEN in docker-compose.yml17 grants tunnel routing access and must be protected.
Production Secret Management Flow
Kubernetes Secret Management Example:
Docker Swarm Secret Management Example:
Sources: docker-compose.yml:14-17 .env.sample1 .gitignore1
Network Security
The current configuration in docker-compose.yml:1-18 does not implement network policies. Production deployments should restrict inter-container communication.
graph TB
subgraph "External Network"
CF_EDGE["Cloudflare Edge Network"]
end
subgraph "Docker Host"
subgraph "frontend_network"
CFD["cloudflared\ncontainer_name: cloudflared\nimage: cloudflare/cloudflared:latest"]
end
subgraph "backend_network"
MOSQ["mosquitto\ncontainer_name: mosquitto\nimage: eclipse-mosquitto:latest"]
end
subgraph "Shared Network: tunnel_network"
CFD_BRIDGE["cloudflared bridge"]
MOSQ_BRIDGE["mosquitto bridge"]
end
end
CF_EDGE <-->|outbound tunnel| CFD
CFD_BRIDGE <-->|port 9001| MOSQ_BRIDGE
CFD -.->|connected to tunnel_network| CFD_BRIDGE
MOSQ -.->|connected to tunnel_network| MOSQ_BRIDGE
Docker Network Segmentation
Production docker-compose.yml Network Configuration:
Key Security Enhancements:
read_only: true: Container filesystems are read-onlysecurity_opt: no-new-privileges: Prevents privilege escalationtmpfs: /tmp: Writable temporary directory- Named volumes: Persistent data storage
- Static IP addresses: Predictable network topology
Sources: docker-compose.yml:1-18
Persistent Storage and Data Management
The development configuration in docker-compose.yml:4-9 does not provision persistent volumes for Mosquitto data. Message retention, subscriptions, and authentication data require persistent storage.
graph LR
subgraph "Host Filesystem"
COMPOSE["docker-compose.yml"]
MOSQ_CONF_FILE["mosquitto.conf"]
end
subgraph "Docker Volumes"
DATA_VOL["mosquitto_data\n(retained messages,\nsubscriptions)"]
LOG_VOL["mosquitto_log\n(broker logs)"]
CONFIG_VOL["config volume\n(mounted configs)"]
end
subgraph "mosquitto Container"
MOSQ_PROC["mosquitto process"]
DATA_MOUNT["/mosquitto/data"]
LOG_MOUNT["/mosquitto/log"]
CONF_MOUNT["/mosquitto/config"]
end
subgraph "Backup Systems"
BACKUP_CRON["cron job"]
BACKUP_STORAGE["Remote Storage\n(S3, NFS, etc.)"]
end
MOSQ_CONF_FILE -->|bind mount| CONFIG_VOL
CONFIG_VOL -->|mounted at| CONF_MOUNT
DATA_VOL -->|mounted at| DATA_MOUNT
LOG_VOL -->|mounted at| LOG_MOUNT
CONF_MOUNT --> MOSQ_PROC
DATA_MOUNT --> MOSQ_PROC
LOG_MOUNT --> MOSQ_PROC
DATA_VOL -.->|backup| BACKUP_CRON
LOG_VOL -.->|backup| BACKUP_CRON
BACKUP_CRON -->|archive| BACKUP_STORAGE
Storage Architecture
Volume Configuration for Production
Modify docker-compose.yml:7-8 to include persistent volumes:
Backup Strategy
Automated Backup Script:
Restoration Procedure:
Sources: docker-compose.yml:7-8
Resource Management and Scaling
Container Resource Limits
The current docker-compose.yml:4-18 does not specify resource constraints. Production deployments must prevent resource exhaustion.
Production Resource Configuration:
graph TB
subgraph "Host Resources"
CPU["CPU Cores"]
MEMORY["System Memory"]
DISK["Disk I/O"]
end
subgraph "Container Allocations"
MOSQ_CPU["mosquitto\nCPUs: 1.0\nMemory: 1GB\nreservations.memory: 512MB"]
CFD_CPU["cloudflared\nCPUs: 0.5\nMemory: 512MB\nreservations.memory: 256MB"]
end
subgraph "Monitoring Limits"
ALERTS["Resource Alerts\n(>80% usage)"]
METRICS["Prometheus Metrics\ncontainer_memory_usage_bytes\ncontainer_cpu_usage_seconds_total"]
end
CPU -->|allocated| MOSQ_CPU
CPU -->|allocated| CFD_CPU
MEMORY -->|allocated| MOSQ_CPU
MEMORY -->|allocated| CFD_CPU
MOSQ_CPU -.->|export metrics| METRICS
CFD_CPU -.->|export metrics| METRICS
METRICS -->|trigger| ALERTS
Mosquitto Performance Tuning
Extend mosquitto.conf:1-6 with production performance settings:
# Connection limits
max_connections 1000
max_queued_messages 1000
max_inflight_messages 20
# Persistence settings
persistence true
persistence_location /mosquitto/data/
autosave_interval 300
autosave_on_changes false
# Memory management
max_keepalive 60
message_size_limit 0
# Logging (reduced verbosity for production)
log_dest file /mosquitto/log/mosquitto.log
log_type error
log_type warning
log_timestamp true
# Listeners
listener 1883
allow_anonymous false
password_file /mosquitto/config/password_file
listener 9001
protocol websockets
allow_anonymous false
password_file /mosquitto/config/password_file
Key Configuration Parameters:
| Parameter | Development | Production | Purpose |
|---|---|---|---|
max_connections | Unlimited | 1000 | Prevent resource exhaustion |
persistence | False (default) | True | Retain messages across restarts |
autosave_interval | N/A | 300 | Save retained messages every 5 minutes |
max_keepalive | 65535 | 60 | Detect dead connections faster |
log_type | All | error, warning | Reduce log volume |
Sources: mosquitto.conf:1-6 docker-compose.yml:4-9
High Availability and Clustering
Multi-Instance Deployment Architecture
The single-instance architecture in docker-compose.yml:4-9 provides no redundancy. Production systems require high availability.
Cloudflare Load Balancer Configuration:
- Create multiple Cloudflare Tunnels (one per region/instance)
- Configure DNS load balancing in Cloudflare dashboard:
- Geographic routing: Route based on client location
- Health checks: Monitor tunnel availability
- Failover: Automatic failover to healthy tunnels
Multi-Tunnel docker-compose.yml:
Mosquitto Bridge Configuration
Configure message synchronization between instances by adding to mosquitto.conf:
# Bridge configuration for message replication
connection bridge-to-replica
address mosquitto-replica:1883
topic # both 0
cleansession false
try_private false
bridge_attempt_unsubscribe true
bridge_protocol_version mqttv311
Sources: docker-compose.yml:4-18 mosquitto.conf:1-6
Container Orchestration
Kubernetes Deployment
For production-scale deployments, replace docker-compose.yml:1-18 with Kubernetes manifests.
Kubernetes Deployment Manifest (mosquitto):
Kubernetes Deployment Manifest (cloudflared):
PersistentVolumeClaim:
Sources: docker-compose.yml:1-18
Monitoring and Logging
Logging Configuration
The default mosquitto.conf:1-6 does not configure logging. Production deployments require comprehensive logging.
Production Logging Configuration (mosquitto.conf):
graph LR
subgraph "Log Sources"
MOSQ_PROC["mosquitto process"]
CFD_PROC["cloudflared process"]
DOCKER_DAEMON["Docker Daemon"]
end
subgraph "Log Collection"
MOSQ_LOG_VOL["/mosquitto/log/mosquitto.log"]
DOCKER_JSON_LOG["container stdout/stderr\n(JSON driver)"]
end
subgraph "Log Aggregation"
FLUENTD["Fluentd\n(log shipper)"]
FILEBEAT["Filebeat\n(log shipper)"]
end
subgraph "Log Storage & Analysis"
ELK["Elasticsearch\n(log indexing)"]
KIBANA["Kibana\n(visualization)"]
LOKI["Grafana Loki\n(log aggregation)"]
SPLUNK["Splunk\n(SIEM)"]
end
subgraph "Alerting"
ALERT_MGR["AlertManager\n(alert routing)"]
PAGERDUTY["PagerDuty"]
SLACK["Slack"]
end
MOSQ_PROC -->|writes to| MOSQ_LOG_VOL
MOSQ_PROC -->|stdout| DOCKER_JSON_LOG
CFD_PROC -->|stdout| DOCKER_JSON_LOG
DOCKER_DAEMON -->|container logs| DOCKER_JSON_LOG
MOSQ_LOG_VOL -->|tail| FLUENTD
DOCKER_JSON_LOG -->|docker logs| FILEBEAT
FLUENTD --> ELK
FILEBEAT --> ELK
FLUENTD --> LOKI
ELK --> KIBANA
LOKI --> ALERT_MGR
ELK --> ALERT_MGR
ALERT_MGR --> PAGERDUTY
ALERT_MGR --> SLACK
# Comprehensive logging
log_dest file /mosquitto/log/mosquitto.log
log_dest stdout
log_type error
log_type warning
log_type notice
log_type information
log_type subscribe
log_type unsubscribe
log_timestamp true
log_timestamp_format %Y-%m-%dT%H:%M:%S
connection_messages true
Docker Logging Driver Configuration:
Metrics and Monitoring
Implement Prometheus-compatible metrics exporters:
docker-compose.yml with Monitoring:
Prometheus Configuration (prometheus.yml):
Key Metrics to Monitor:
| Metric | Description | Alert Threshold |
|---|---|---|
mosquitto_connected_clients | Active client connections | > 80% of max_connections |
mosquitto_messages_received_total | Total messages received | Rate < 1/min (potential downtime) |
mosquitto_messages_sent_total | Total messages sent | Rate < 1/min (potential issue) |
mosquitto_retained_messages | Number of retained messages | > 90% of storage capacity |
container_memory_usage_bytes | Container memory usage | > 80% of limit |
container_cpu_usage_seconds_total | Container CPU usage | > 80% of limit |
Sources: mosquitto.conf:1-6 docker-compose.yml:1-18
Disaster Recovery and Business Continuity
Backup Automation
Implement automated backup schedules using cron or Kubernetes CronJobs.
Kubernetes CronJob for Backups:
Recovery Time Objectives
| Failure Scenario | RTO (Recovery Time Objective) | RPO (Recovery Point Objective) | Recovery Procedure |
|---|---|---|---|
| Container crash | < 1 minute | 0 (no data loss) | Automatic restart via restart: unless-stopped |
| Node failure | < 5 minutes | 0 (no data loss) | Kubernetes pod rescheduling |
| Data corruption | < 30 minutes | 24 hours | Restore from latest backup |
| Regional outage | < 15 minutes | 0 (no data loss) | Cloudflare automatic failover |
| Complete disaster | < 2 hours | 24 hours | Deploy new infrastructure, restore backups |
Disaster Recovery Testing
Quarterly DR Test Procedure:
-
Simulate Container Failure:
-
Simulate Data Corruption:
-
Simulate Network Partition:
-
Verify Backup Integrity:
Sources: docker-compose.yml9 docker-compose.yml15
Security Compliance and Hardening
Container Image Security
The default images specified in docker-compose.yml5 and docker-compose.yml12 should be validated and scanned for vulnerabilities.
Image Security Checklist:
| Security Control | Implementation |
|---|---|
| Base Image Verification | Use official images with verified publishers |
| Vulnerability Scanning | Run docker scan or Trivy before deployment |
| Image Signing | Verify Docker Content Trust signatures |
| Version Pinning | Use specific version tags, not latest |
| Minimal Base | Prefer Alpine-based images |
| Read-Only Filesystem | Set read_only: true in docker-compose.yml |
Production Image References:
Security Scanning Integration
Add security scanning to CI/CD pipeline:
Compliance Requirements
GDPR/Privacy Compliance:
- Enable audit logging in mosquitto.conf:1-6
- Implement data retention policies
- Configure message encryption
- Document data flows
SOC 2 Compliance:
- Implement access controls via ACL
- Enable comprehensive logging
- Implement change management procedures
- Document disaster recovery procedures
PCI DSS (if handling payment data):
- Implement network segmentation
- Enable encryption in transit and at rest
- Implement strong authentication
- Regular security assessments
Sources: docker-compose.yml5 docker-compose.yml12 mosquitto.conf:1-6
Performance Optimization
Connection Pooling and Load Distribution
The docker-compose.yml:4-9 single-instance architecture does not scale for high-throughput scenarios.
Mosquitto Performance Benchmarks
Expected Performance Metrics:
| Metric | Development (Single Instance) | Production (Clustered) |
|---|---|---|
| Max Concurrent Connections | ~1,000 | ~10,000 |
| Messages/sec (publish) | ~5,000 | ~50,000 |
| Messages/sec (subscribe) | ~10,000 | ~100,000 |
| Latency (p99) | < 100ms | < 50ms |
| Memory per connection | ~2KB | ~2KB |
Performance Testing:
Tuning mosquitto.conf for High Throughput
# High-performance configuration
listener 1883
max_connections 10000
max_queued_messages 10000
max_inflight_messages 100
# Disable persistence for high-throughput, low-retention scenarios
# (Use only if message loss is acceptable)
persistence false
# Reduce keepalive overhead
max_keepalive 30
# Increase message size limit (default: 268435456 bytes)
message_size_limit 1048576
# Optimize memory usage
memory_limit 2147483648
# Queue settings
max_queued_bytes 0
queue_qos0_messages false
# Websocket settings
listener 9001
protocol websockets
websocket_timeout 300
Sources: mosquitto.conf:1-6 docker-compose.yml:4-9
Deployment Checklist
Pre-Production Validation
Infrastructure Readiness:
- Container orchestration platform configured (Kubernetes/Docker Swarm)
- Persistent storage provisioned with backup strategy
- Secrets management system configured
- Monitoring and logging infrastructure deployed
- Disaster recovery procedures documented and tested
Security Configuration:
- Authentication enabled in mosquitto.conf2
- ACL file configured with topic-level restrictions
- Anonymous access disabled
- TLS certificates provisioned (if not using Cloudflare Tunnel)
- Network policies implemented
- Container security contexts configured (
read_only,no-new-privileges) - Image vulnerability scanning completed
Cloudflare Configuration:
- Multiple tunnels created for high availability
- DNS load balancing configured
- Health checks enabled
- Rate limiting configured
- DDoS protection verified
- WAF rules configured
Performance Tuning:
- Resource limits configured in docker-compose.yml:4-18
- Connection limits set in mosquitto.conf:1-6
- Persistence settings optimized
- Load testing completed
- Performance benchmarks documented
Operational Readiness:
- Backup automation tested
- Restore procedures validated
- Monitoring dashboards created
- Alert rules configured
- On-call runbooks documented
- Incident response procedures defined
Compliance:
- Audit logging enabled
- Data retention policies implemented
- Privacy impact assessment completed
- Security assessment completed
- Compliance documentation prepared
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 README.md:1-93
Migration from Development to Production
Transition Strategy
Migration Steps
Phase 1: Staging Environment Setup
- Deploy staging environment with production-like configuration
- Migrate from
.envto secrets manager - Enable authentication and ACL
- Configure persistent volumes
- Deploy monitoring stack
- Run load tests
Phase 2: Production Infrastructure
- Provision Kubernetes cluster or production Docker hosts
- Configure external secrets management
- Set up persistent storage with replication
- Deploy monitoring and logging infrastructure
- Configure backup automation
- Implement network policies
Phase 3: Service Migration
- Create multiple Cloudflare Tunnels for production regions
- Deploy Mosquitto cluster with bridge configuration
- Configure load balancing and health checks
- Migrate client connections gradually
- Monitor performance and adjust resources
- Validate disaster recovery procedures
Phase 4: Optimization
- Tune Mosquitto configuration based on production metrics
- Adjust resource limits based on actual usage
- Optimize backup schedules
- Refine alerting thresholds
- Document operational procedures
Sources: docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample1
Conclusion
Production deployment of the Docker MQTT Mosquitto with Cloudflare Tunnel system requires significant enhancements beyond the development configuration in docker-compose.yml:1-18 and mosquitto.conf:1-6 Key production requirements include:
- Security: Disable anonymous access, implement authentication and ACL, use secrets managers
- Reliability: Deploy multiple instances, implement health checks, configure automated backups
- Scalability: Use container orchestration (Kubernetes), implement load balancing, tune resource limits
- Observability: Deploy comprehensive logging and monitoring, configure alerting, track SLIs/SLOs
- Operations: Document runbooks, test disaster recovery, implement change management
The transition from development to production should be gradual, with thorough testing in staging environments before production deployment. Regular security assessments, performance tuning, and disaster recovery testing ensure ongoing operational excellence.
Sources: README.md:1-93 docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample1 .gitignore:1-2
Dismiss
Refresh this wiki
Enter email to refresh
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Monitoring and Health Checks
Loading…
Monitoring and Health Checks
Relevant source files
Purpose and Scope
This document explains monitoring and health check strategies for the Docker MQTT Mosquitto with Cloudflare Tunnel system. It covers container status verification, automated health checks in the CI pipeline, logging approaches, and techniques for implementing custom monitoring solutions. For production deployment considerations, see Production Deployment Considerations. For troubleshooting specific issues, see Troubleshooting.
Container Status Monitoring
Docker Inspect Command Pattern
The system uses Docker’s native inspection capabilities to verify container health. The primary method queries container state using docker inspect with formatted output.
Container State Check Pattern:
This command returns the current container state, which can be one of: created, restarting, running, removing, paused, exited, or dead.
Container Status Fields
Docker provides multiple state fields that can be inspected for comprehensive health monitoring:
| Field | Path | Description |
|---|---|---|
| Status | .State.Status | Current container state |
| Running | .State.Running | Boolean indicating if container is running |
| ExitCode | .State.ExitCode | Exit code if container has stopped |
| StartedAt | .State.StartedAt | Timestamp when container started |
| Error | .State.Error | Error message if container failed |
Sources: .github/workflows/ci.yml30
CI Pipeline Health Verification
Automated Health Check Implementation
The GitHub Actions CI pipeline implements a bounded retry pattern to verify the mosquitto container reaches a healthy running state after startup. This pattern accounts for non-deterministic container initialization times.
flowchart TD
Start["Start Docker Compose\ndocker-compose up -d mosquitto"]
Init["Initialize retry counter\ni=1, max=10"]
Inspect["Execute docker inspect\n--format='{{.State.Status}}'"]
Check{"Status == 'running'?"}
Success["Log: Mosquitto is running\nExit 0"]
Wait["Log: Waiting for Mosquitto...\nSleep 10 seconds"]
Increment["Increment counter\ni++"]
CheckMax{"i > 10?"}
Timeout["Log: Did not become healthy\nExit 1"]
Start --> Init
Init --> Inspect
Inspect --> Check
Check -->|Yes| Success
Check -->|No| Wait
Wait --> Increment
Increment --> CheckMax
CheckMax -->|No| Inspect
CheckMax -->|Yes| Timeout
Health Check Flow Diagram
Sources: .github/workflows/ci.yml:27-39
Retry Loop Parameters
The health check uses the following parameters:
| Parameter | Value | Rationale |
|---|---|---|
| Max Attempts | 10 | Provides 100 seconds total wait time |
| Sleep Interval | 10 seconds | Balances responsiveness and system load |
| Total Timeout | 100 seconds | Sufficient for cold starts and image pulls |
| Check Method | docker inspect | Native Docker API, no external dependencies |
| Success Criteria | Status contains running | Container process is active |
The implementation uses a shell loop structure:
Sources: .github/workflows/ci.yml:28-39
Docker Native Healthcheck Configuration
Current Implementation
The system currently relies on Docker Compose’s restart: unless-stopped policy for automatic recovery but does not define explicit healthcheck directives in the compose configuration.
Current Restart Policy:
- Service:
mosquitto- restart policy defined at docker-compose.yml9 - Service:
cloudflared- restart policy defined at docker-compose.yml15
Adding Native Docker Healthchecks
Docker Compose supports native healthcheck definitions that provide more sophisticated monitoring than simple state checks. Below are example configurations for both services.
Mosquitto Healthcheck Example
This healthcheck:
- Subscribes to the system topic
$SYS/broker/uptime - Waits up to 3 seconds for a message
- Receives 1 message (
-C 1) to confirm broker is operational - Runs every 30 seconds
- Allows 10 seconds for the test to complete
- Requires 3 consecutive failures before marking unhealthy
Cloudflared Healthcheck Example
This healthcheck:
- Executes
cloudflared tunnel infoto verify tunnel connectivity - Runs every 60 seconds (tunnel state changes slowly)
- Allows 5 retries (tunnel reconnections may take time)
- Provides 30 seconds startup grace period
Sources: docker-compose.yml:4-17
System Monitoring Architecture
Container Monitoring Topology
Sources: docker-compose.yml:1-18 .github/workflows/ci.yml:27-39
Cloudflared Tunnel Monitoring
Tunnel Status Verification
The cloudflared container maintains an outbound connection to Cloudflare’s network. Monitoring tunnel health requires checking both container state and tunnel connectivity.
Log-Based Monitoring
The cloudflared process outputs connection status to stdout:
Tunnel Status Commands
Common Cloudflared Log Patterns
| Log Pattern | Meaning | Action Required |
|---|---|---|
Registered tunnel connection | Tunnel successfully connected | Normal operation |
unable to register connection | Authentication or network failure | Verify token and connectivity |
Retrying in | Temporary connection loss | Monitor for recovery |
SIGTERM received | Graceful shutdown initiated | Expected during restarts |
certificate error | TLS/SSL verification failed | Check system time and CA certificates |
Sources: docker-compose.yml:11-17
Mosquitto Broker Monitoring
MQTT System Topics
The Mosquitto broker publishes operational metrics to reserved $SYS topics. These provide real-time broker statistics without external dependencies.
Key System Topics
| Topic | Description | Type |
|---|---|---|
$SYS/broker/version | Broker version string | Static |
$SYS/broker/uptime | Seconds since broker start | Counter |
$SYS/broker/clients/connected | Current connected client count | Gauge |
$SYS/broker/clients/total | Total clients since start | Counter |
$SYS/broker/messages/received | Total messages received | Counter |
$SYS/broker/messages/sent | Total messages sent | Counter |
$SYS/broker/bytes/received | Total bytes received | Counter |
$SYS/broker/bytes/sent | Total bytes sent | Counter |
Monitoring via MQTT Client
Log-Based Monitoring
Sources: docker-compose.yml:4-9
Health Check Decision Tree
Sources: .github/workflows/ci.yml:27-42 docker-compose.yml:1-18
Custom Health Check Strategies
Script-Based Monitoring
Implement a shell script for comprehensive health verification:
External Monitoring Integration
Prometheus Exporter Pattern
For production deployments, integrate with monitoring systems using exporters:
- Docker Container Exporter : Exposes container metrics
- MQTT Exporter : Subscribes to
$SYStopics and exposes as Prometheus metrics - Cloudflare Tunnel Exporter : Monitors tunnel status via Cloudflare API
Example Prometheus Configuration
Sources: .github/workflows/ci.yml:27-39
Logging and Log Analysis
Container Log Access
Both containers output logs to stdout/stderr, which Docker captures:
Log Patterns and Indicators
Mosquitto Startup Success Indicators
Opening ipv4 listen socket on port 1883.
Opening ipv4 listen socket on port 9001.
mosquitto version X.X.X running
Cloudflared Connection Success Indicators
Registered tunnel connection
Connection established
Log Aggregation
For production deployments, consider implementing centralized logging:
Sources: docker-compose.yml:4-17
Restart Policies and Recovery
Current Configuration
Both services use the unless-stopped restart policy, which provides:
- Automatic restart on failure
- Respect for manual stops (
docker-compose stop) - Persistence across Docker daemon restarts
Configuration locations:
mosquittoservice: docker-compose.yml9cloudflaredservice: docker-compose.yml15
Monitoring Restart Behavior
Crash Loop Detection
Frequent restarts indicate underlying issues:
If restart count increases rapidly (>3 restarts in 1 minute), investigate:
- Check container logs for error messages
- Verify configuration file syntax
- Confirm environment variables are set
- Check resource constraints (memory/CPU limits)
Sources: docker-compose.yml9 docker-compose.yml15
CI Pipeline Integration
The GitHub Actions CI workflow serves as a reference implementation for automated health verification. The workflow demonstrates:
- Isolated Environment : Starts only the
mosquittoservice without dependencies - Bounded Wait : Implements timeout to prevent hanging builds
- State Verification : Uses Docker’s native state inspection
- Clean Teardown : Ensures resources are released after testing
CI Health Check Sequence
Sources: .github/workflows/ci.yml:24-42
Best Practices Summary
| Practice | Implementation | Benefit |
|---|---|---|
| Bounded retries | Max 10 attempts with 10s interval | Prevents infinite waits |
| Docker native checks | Use docker inspect for state | No external dependencies |
| Log monitoring | Regularly check container logs | Early problem detection |
| Restart tracking | Monitor RestartCount metric | Identify crash loops |
| System topics | Subscribe to $SYS/# for MQTT stats | Broker-native monitoring |
| Health scripts | Automate multi-component checks | Consistent verification |
| External integration | Export metrics to monitoring systems | Production observability |
Sources: .github/workflows/ci.yml:27-39 docker-compose.yml:1-18
Dismiss
Refresh this wiki
Enter email to refresh
On this page
- Monitoring and Health Checks
- Purpose and Scope
- Container Status Monitoring
- Docker Inspect Command Pattern
- Container Status Fields
- CI Pipeline Health Verification
- Automated Health Check Implementation
- Health Check Flow Diagram
- Retry Loop Parameters
- Docker Native Healthcheck Configuration
- Current Implementation
- Adding Native Docker Healthchecks
- Mosquitto Healthcheck Example
- Cloudflared Healthcheck Example
- System Monitoring Architecture
- Container Monitoring Topology
- Cloudflared Tunnel Monitoring
- Tunnel Status Verification
- Log-Based Monitoring
- Tunnel Status Commands
- Common Cloudflared Log Patterns
- Mosquitto Broker Monitoring
- MQTT System Topics
- Key System Topics
- Monitoring via MQTT Client
- Log-Based Monitoring
- Health Check Decision Tree
- Custom Health Check Strategies
- Script-Based Monitoring
- External Monitoring Integration
- Prometheus Exporter Pattern
- Example Prometheus Configuration
- Logging and Log Analysis
- Container Log Access
- Log Patterns and Indicators
- Mosquitto Startup Success Indicators
- Cloudflared Connection Success Indicators
- Log Aggregation
- Restart Policies and Recovery
- Current Configuration
- Monitoring Restart Behavior
- Crash Loop Detection
- CI Pipeline Integration
- CI Health Check Sequence
- Best Practices Summary
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Troubleshooting
Loading…
Troubleshooting
Relevant source files
Purpose and Scope
This page provides diagnostic procedures and solutions for common issues encountered when deploying and operating the Docker MQTT Mosquitto with Cloudflare Tunnel system. It covers container startup failures, connection problems, configuration errors, and debugging techniques.
For information about the CI/CD automated health checks, see Continuous Integration. For production monitoring strategies, see Monitoring and Health Checks. For initial setup procedures, see Getting Started.
Diagnostic Decision Tree
The following flowchart provides a systematic approach to diagnosing system issues:
Sources : README.md:74-84 docker-compose.yml:1-18 mosquitto.conf:1-6
flowchart TD
Start["Issue Reported"] --> CheckContainers{"docker compose ps\nAll containers running?"}
CheckContainers -->|No| IdentifyDown{"Which container\nis down?"}
CheckContainers -->|Yes| CheckTunnel["Check tunnel connectivity"]
IdentifyDown -->|mosquitto| MosquittoDown["Check mosquitto logs:\ndocker compose logs mosquitto"]
IdentifyDown -->|cloudflared| CloudflaredDown["Check cloudflared logs:\ndocker compose logs cloudflared"]
IdentifyDown -->|Both| BothDown["Check docker-compose.yml\nand .env configuration"]
MosquittoDown --> MosquittoErrors{"Error type?"}
MosquittoErrors -->|Config syntax| CheckMosquittoConf["Validate mosquitto.conf:1-5\nlistener directives"]
MosquittoErrors -->|Port binding| CheckPortConflict["Check port 1883/9001\nconflicts on host"]
MosquittoErrors -->|Volume mount| CheckVolumeMount["Verify ./mosquitto.conf\nfile exists and readable"]
CloudflaredDown --> CloudflaredErrors{"Error type?"}
CloudflaredErrors -->|Authentication failed| CheckToken["Verify CLOUDFLARE_TUNNEL_TOKEN\nin .env file"]
CloudflaredErrors -->|Tunnel not found| RecreateToken["Generate new token\nin Cloudflare dashboard"]
CloudflaredErrors -->|Connection refused| CheckNetwork["Verify internet connectivity\nand firewall rules"]
BothDown --> CheckDockerCompose["Validate docker-compose.yml\nsyntax and structure"]
CheckTunnel --> TunnelTest{"Can connect via\npublic hostname?"}
TunnelTest -->|No| CheckHostname["Verify hostname in\nCloudflare dashboard\npoints to mosquitto:9001"]
TunnelTest -->|Yes| CheckMQTT["Test MQTT connection\non port 443"]
CheckMQTT --> MQTTTest{"MQTT publish/\nsubscribe works?"}
MQTTTest -->|No| CheckProtocol["Verify protocol:\nWebSockets required\nfor tunnel connection"]
MQTTTest -->|Yes| Resolved["Issue resolved"]
CheckMosquittoConf --> Restart["docker compose restart mosquitto"]
CheckPortConflict --> StopConflicting["Stop conflicting service\nor change port mapping"]
CheckVolumeMount --> FixMount["Create/fix mosquitto.conf\nand verify path"]
CheckToken --> UpdateEnv["Update .env and\ndocker compose restart cloudflared"]
RecreateToken --> UpdateEnv
CheckNetwork --> FixNetwork["Check DNS/firewall/proxy"]
CheckDockerCompose --> FixCompose["Fix syntax errors\nand docker compose up"]
CheckHostname --> UpdateHostname["Update hostname config\nin Cloudflare dashboard"]
CheckProtocol --> UseWebSockets["Use wss:// protocol\non port 443"]
Restart --> Resolved
StopConflicting --> Resolved
FixMount --> Resolved
UpdateEnv --> Resolved
FixNetwork --> Resolved
FixCompose --> Resolved
UpdateHostname --> Resolved
UseWebSockets --> Resolved
Common Issues and Solutions
Container Startup Failures
| Issue | Symptoms | Diagnosis | Solution |
|---|---|---|---|
| Missing tunnel token | cloudflared container exits immediately | docker compose logs cloudflared shows authentication error | Create .env file from .env.sample template and populate CLOUDFLARE_TUNNEL_TOKEN |
| Invalid token | cloudflared shows “tunnel credentials invalid” | Token rejected by Cloudflare API | Generate new token in Cloudflare Zero Trust dashboard and update .env |
| Configuration syntax error | mosquitto container crashes on startup | docker compose logs mosquitto shows parse error | Validate mosquitto.conf:1-6 syntax, ensure listener directives are properly formatted |
| Port conflict | mosquitto fails to bind ports | Error: “Address already in use” | Identify conflicting service with sudo lsof -i :1883 and sudo lsof -i :9001, then stop conflicting service |
| Volume mount failure | mosquitto cannot read configuration | Permission denied or file not found error | Verify ./mosquitto.conf exists and has read permissions: ls -l mosquitto.conf |
| Image pull failure | Container fails to start with image error | Cannot pull eclipse-mosquitto:latest or cloudflare/cloudflared:latest | Check Docker Hub connectivity, verify registry access, or use explicit image versions |
Sources : docker-compose.yml:4-17 mosquitto.conf:1-6 .env.sample1
Connection and Tunnel Issues
The following diagram illustrates the connection verification process:
Connection Troubleshooting Table :
| Failure Point | Test Command | Expected Result | Fix |
|---|---|---|---|
| Tunnel not established | `docker compose logs cloudflared | grep “Connection established”` | Should see “Connection established” message |
| Mosquitto not listening | docker compose exec mosquitto netstat -tlnp | Should show :1883 and :9001 listeners | Check mosquitto.conf:1-5 configuration, restart container |
| DNS resolution failure | docker compose exec cloudflared nslookup mosquitto | Should resolve to container IP | Verify container_name: mosquitto in docker-compose.yml6 |
| Public hostname misconfigured | Check Cloudflare dashboard tunnel configuration | URL should point to mosquitto:9001 with HTTP service type | Update hostname configuration in Cloudflare dashboard |
| WebSocket protocol mismatch | Client connection logs show protocol error | Client must use wss:// protocol on port 443 | Update client to use WebSockets over SSL/TLS |
Sources : docker-compose.yml:11-17 mosquitto.conf:4-5 README.md:63-67
Health Check Verification
The CI pipeline implements a health check strategy that can be replicated for manual troubleshooting:
Manual Health Check Procedure :
sequenceDiagram
participant User as "User/CI System"
participant Docker as "docker-compose"
participant MosqContainer as "mosquitto container"
participant HealthCheck as "Health Check Loop"
User->>Docker: docker-compose up -d mosquitto
Docker->>MosqContainer: Start container
MosqContainer->>MosqContainer: Load /mosquitto/config/mosquitto.conf
MosqContainer->>MosqContainer: Bind listener 1883
MosqContainer->>MosqContainer: Bind listener 9001
User->>HealthCheck: Start health check loop
Note over HealthCheck: Max 10 attempts, 10 second intervals
loop Until healthy or max attempts
HealthCheck->>Docker: docker-compose ps --format json
Docker-->>HealthCheck: Container status JSON
HealthCheck->>HealthCheck: Parse 'State' field
alt State == "running"
HealthCheck-->>User: ✓ Service healthy
else State != "running"
HealthCheck->>HealthCheck: Sleep 10 seconds
HealthCheck->>HealthCheck: Increment attempt counter
end
end
alt Max attempts exceeded
HealthCheck-->>User: ✗ Health check failed after 100s
User->>Docker: docker-compose logs mosquitto
Docker-->>User: Error logs
end
Sources : .github/workflows/ci.yml:17-37 docker-compose.yml:4-9 mosquitto.conf:1-6
Configuration Validation
Validating docker-compose.yml
The following elements must be correctly configured in docker-compose.yml:1-18:
Validation Commands :
Sources : docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample1
Validating mosquitto.conf
The mosquitto.conf:1-6 file has a simple structure but specific syntax requirements:
| Line | Directive | Purpose | Common Errors |
|---|---|---|---|
| 1 | listener 1883 | Native MQTT protocol port | Missing port number, invalid port range (1-65535) |
| 2 | allow_anonymous true | Authentication setting | Typo in allow_anonymous, missing boolean value |
| 3 | (blank) | Separator | N/A |
| 4 | listener 9001 | WebSocket protocol port | Duplicate port number, port conflict with line 1 |
| 5 | protocol websockets | Protocol specification for listener | Typo in websockets, missing protocol directive |
Validation Procedure :
Sources : mosquitto.conf:1-6
Environment Variable Issues
Missing or Invalid CLOUDFLARE_TUNNEL_TOKEN
The CLOUDFLARE_TUNNEL_TOKEN environment variable is the critical authentication credential for establishing the tunnel connection.
Common Token Issues :
| Problem | Symptom | Root Cause | Solution |
|---|---|---|---|
| Token not set | cloudflared exits with “missing token” error | .env file not created or empty | Copy .env.sample1 to .env and populate token |
| Token syntax error | Authentication fails immediately | Token contains extra spaces, newlines, or quotes | Ensure token is on single line with no surrounding quotes: CLOUDFLARE_TUNNEL_TOKEN=eyJh... |
| Token expired/revoked | “unauthorized” or “tunnel not found” error | Token regenerated in Cloudflare dashboard or tunnel deleted | Generate new token from Cloudflare dashboard and update .env |
| Wrong token | Tunnel connects but routes to wrong service | Token from different tunnel configuration | Verify token matches the tunnel name in Cloudflare dashboard |
| .env not loaded | Variable shows as empty in container | .env file not in correct directory or not named exactly .env | Verify .env is in same directory as docker-compose.yml |
Debugging Environment Variables :
Sources : docker-compose.yml:16-17 .env.sample1 README.md53
flowchart TB
ViewLogs["View Container Logs"]
ViewLogs --> MosqLogs["docker compose logs mosquitto"]
ViewLogs --> CFDLogs["docker compose logs cloudflared"]
ViewLogs --> AllLogs["docker compose logs"]
MosqLogs --> MosqSuccess["Success Indicators"]
MosqLogs --> MosqErrors["Error Indicators"]
MosqSuccess --> MS1["'Opening ipv4 listen socket on port 1883'"]
MosqSuccess --> MS2["'Opening websockets listen socket on port 9001'"]
MosqSuccess --> MS3["'mosquitto version X.X.X running'"]
MosqErrors --> ME1["'Error: Unable to open config file'\n→ Volume mount issue"]
MosqErrors --> ME2["'Error: Invalid bridge parameter'\n→ Config syntax error"]
MosqErrors --> ME3["'Error: Address already in use'\n→ Port conflict"]
CFDLogs --> CFDSuccess["Success Indicators"]
CFDLogs --> CFDErrors["Error Indicators"]
CFDSuccess --> CS1["'Connection established'"]
CFDSuccess --> CS2["'Registered tunnel connection'"]
CFDSuccess --> CS3["'Serving tunnel'"]
CFDErrors --> CE1["'authentication failed'\n→ Invalid token"]
CFDErrors --> CE2["'tunnel not found'\n→ Token/tunnel mismatch"]
CFDErrors --> CE3["'connection refused'\n→ Network issue"]
Log Analysis
Reading Container Logs
Both containers produce diagnostic output that can be analyzed for troubleshooting:
Log Analysis Commands :
Sources : docker-compose.yml:4-17
Network Diagnostics
Docker Network Verification
Both containers must be on the same Docker network for internal DNS resolution to function:
Sources : docker-compose.yml:1-18 README.md64
Cloudflare Dashboard Verification
Issues may originate from misconfiguration in the Cloudflare Zero Trust dashboard:
| Configuration Item | Location | Expected Value | Common Mistakes |
|---|---|---|---|
| Tunnel type | Networks → Tunnels → Tunnel details | Cloudflared | Wrong tunnel type selected |
| Tunnel status | Networks → Tunnels → Tunnel list | “HEALTHY” with green indicator | Shows as “DOWN” if container not running or token invalid |
| Public hostname | Networks → Tunnels → Public Hostnames tab | Subdomain and domain configured | Missing or incorrect hostname |
| Service type | Public hostname configuration | HTTP | HTTPS selected (creates double encryption issue) |
| Service URL | Public hostname configuration | mosquitto:9001 | Incorrect hostname (e.g., localhost, IP address) or wrong port |
Verification Steps :
- Navigate to Cloudflare Zero Trust dashboard
- Go to Networks → Tunnels
- Locate your tunnel and verify status shows “HEALTHY”
- Click tunnel name to view details
- Check Public Hostname tab for correct configuration:
- Service: HTTP (not HTTPS)
- URL:
mosquitto:9001(matches docker-compose.yml6 container name and mosquitto.conf4 listener port)
Sources : README.md:29-72 docker-compose.yml6 mosquitto.conf:4-5
Client Connection Issues
Protocol and Port Requirements
External clients must use WebSockets over SSL/TLS on port 443:
| Client Protocol | Port | Result |
|---|---|---|
wss://subdomain.domain | 443 | ✓ Correct - WebSockets over SSL through tunnel |
mqtt://subdomain.domain | 443 | ✗ Wrong protocol - Native MQTT not supported through tunnel |
ws://subdomain.domain | 443 | ✗ Wrong protocol - Non-SSL WebSocket rejected |
wss://subdomain.domain | 1883 | ✗ Wrong port - Port 1883 not exposed through tunnel |
mqtt://localhost | 1883 | ✓ Works only from Docker host (not through tunnel) |
Client Configuration Example :
Testing Connection :
Sources : README.md:66-82 mosquitto.conf:4-5
Advanced Debugging Techniques
Container Introspection
Resource Constraints
Complete System Reset
Sources : docker-compose.yml:1-18
Related Issues and Resources
- GitHub Issue #25 : Additional troubleshooting information for connection issues (README.md84)
- For protected branch features with ACL restrictions, see Protected Branch Features
- For production monitoring strategies, see Monitoring and Health Checks
- For CI/CD health check implementation details, see Continuous Integration
- For initial setup guidance, see Getting Started
Summary of Common Error Messages
| Error Message | File/Component | Likely Cause | Solution Reference |
|---|---|---|---|
| “Address already in use” | mosquitto | Port 1883 or 9001 conflict | [Container Startup Failures](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Container Startup Failures) |
| “Unable to open config file” | mosquitto | Volume mount issue | [Container Startup Failures](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Container Startup Failures) |
| “authentication failed” | cloudflared | Invalid CLOUDFLARE_TUNNEL_TOKEN | [Environment Variable Issues](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Environment Variable Issues) |
| “tunnel not found” | cloudflared | Token/tunnel mismatch | [Environment Variable Issues](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Environment Variable Issues) |
| “connection refused” | cloudflared | Network/firewall issue | [Network Diagnostics](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Network Diagnostics) |
| “protocol error” | Client | Using mqtt:// instead of wss:// | [Client Connection Issues](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Client Connection Issues) |
| “DNS resolution failed” | cloudflared | Container name mismatch | [Network Diagnostics](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Network Diagnostics) |
| “Invalid bridge parameter” | mosquitto | Config syntax error in mosquitto.conf | [Configuration Validation](https://github.com/jzombie/docker-mqtt-mosquitto-cloudflare-tunnel/blob/59f1274c/Configuration Validation) |
Sources : docker-compose.yml:1-18 mosquitto.conf:1-6 .env.sample1 README.md:1-93
Dismiss
Refresh this wiki
Enter email to refresh