Zero-Trust IoT Messaging: Dockerized Mosquitto and Cloudflare Tunnels
Dockerized Eclipse Mosquitto MQTT broker with outbound-only Cloudflare Tunnel and no inbound ports, for secure IoT messaging.
On this page
A Dockerized Eclipse Mosquitto (opens in new tab) MQTT (opens in new tab) broker integrated with
cloudflaredto establish an outbound-only, Zero-Trust tunnel for IoT messaging. For edge deployments that cannot expose inbound ports. Source: jzombie/docker-mqtt-mosquitto-cloudflare-tunnel (opens in new tab) .
Exposing port 1883 on a public server to run an MQTT broker is a liability. It invites automated scanners, demands constant firewall vigilance, and bets everything on the authentication layer holding.
The
docker-mqtt-mosquitto-cloudflare-tunnel (opens in new tab)
repository provides a structural fix: eliminate the inbound attack surface entirely.
The Outbound-Only Architecture
The deployment runs two containers in one Docker network: mosquitto (eclipse-mosquitto:latest) and cloudflared (cloudflare/cloudflared:latest).
The defining feature of the
docker-compose.yml (opens in new tab)
is what is missing: no published inbound host ports.
Instead, the cloudflared container runs tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} to establish a persistent outbound connection to the
Cloudflare (opens in new tab)
edge via
Cloudflare Tunnel (opens in new tab)
. The origin stays closed and invisible to the public internet. Full setup, including Zero Trust dashboard steps with screenshots and .env configuration, is in the repo
README (opens in new tab)
.
Note the dashboard screenshots have not been re-verified recently and may have drifted, so treat the official docs (opens in new tab) as source of truth.
Edge TLS and Internal Resolution
Public routing and cryptography are offloaded to Cloudflare:
- Edge TLS termination: Clients such as sensors, smart-home devices, and dashboards connect to
https://<subdomain>.<hostname>on port 443. TLS terminates at Cloudflare’s edge. - Internal Docker DNS: Cloudflare forwards through the tunnel to the internal service
mosquitto:9001. That hostname resolves via Docker DNS; it is not a public URL or public port. - Local WebSocket handshake:
mosquitto.conf(opens in new tab) listens on1883(native MQTT, container-internal only) and9001withprotocol websockets. The tunnel targets the WebSocket listener.
Result: high-throughput MQTT-over-WebSockets without opening anything inbound.
Is it Free?
Yes for this use case. Cloudflare Tunnel (opens in new tab) is included in the Cloudflare Zero Trust free tier (opens in new tab) at $0 for up to 50 users. That covers the tunnel connector, Zero Trust Network Access, and device client.
Note: the default
mosquitto.confonmainusesallow_anonymous true. Treat it as a starting primitive, not a production policy. For hardening patterns such as per-user ACLs and retained-message handling, see theprotected-no-wildcardbranch (opens in new tab) and compare view (opens in new tab) .