IoT

MQTT Broker Setup on Windows 11 with Docker for IoT

Tech Setup6 min read
TS

Tech Setup

Reviewed July 30, 2026

MQTT Broker Setup on Windows 11 with Docker for IoT

Introduction

Developing and testing Internet of Things (IoT) applications locally on Windows 11 often presents friction. While Linux remains the native habitat for edge infrastructure, modern Windows development environments—bolstered by Windows Subsystem for Linux (WSL 2) and Docker Desktop—offer a robust, containerized alternative. For developers building systems that rely on lightweight publish-subscribe messaging, running an MQTT (Message Queuing Telemetry Transport) broker locally is a standard requirement.

This guide details how to spin up, configure, and secure an enterprise-grade MQTT broker—Eclipse Mosquitto—on Windows 11 using Docker. We will move beyond a basic "Hello World" deployment to establish a persistent, secure, and production-aligned local development environment suitable for Tier-1 software engineering workflows.

Prerequisites and Environment Setup

Before configuring the MQTT broker, ensure your Windows 11 environment is optimized for containerized workloads. Running Docker on Windows requires the backend integration of WSL 2 for optimal I/O performance and resource utilization.

System Requirements

  • OS: Windows 11 Pro, Enterprise, or Education (Home is supported with WSL 2 backend, but Pro is recommended for Hyper-V features).
  • Virtualization: Enabled in UEFI/BIOS.
  • RAM: Minimum 8GB allocated to the system (Docker Desktop should be configured to use at least 2GB).
  • Tools: PowerShell 7+ or Windows Terminal, Docker Desktop for Windows, and an MQTT client utility (such as mosquitto-clients or mqttui).

Verifying Docker and WSL 2

Open your terminal (Windows Terminal is preferred) and verify that your Docker daemon is running and communicating correctly with the WSL 2 backend:

docker version
wsl --status

Ensure that the Docker Desktop context is set to default or windows-integration is active for your preferred WSL distribution.

Designing the Directory Structure

To maintain a clean separation of concerns, we will structure our local deployment using a dedicated project directory. This directory will house our docker-compose.yml file, the Mosquitto configuration file (mosquitto.conf), and persistent storage volumes for logs, data, and TLS certificates.

Open your terminal and run the following commands to initialize the workspace:

mkdir -p C:\docker\mqtt\config
mkdir -p C:\docker\mqtt\data
mkdir -p C:\docker\mqtt\log
cd C:\docker\mqtt

This layout ensures that container restarts do not wipe out retained messages, client session states, or access control lists (ACLs).

Configuring Eclipse Mosquitto

Eclipse Mosquitto is a lightweight, open-source MQTT broker implementing versions 5.0, 3.1.1, and 3.1 of the MQTT protocol. To configure it for both unencrypted local testing and secure production-like validation, we need to supply a custom configuration file.

Create a file named mosquitto.conf inside your C:\docker\mqtt\config\ directory. Populate it with the following production-aligned configuration:

# Config file for Eclipse Mosquitto

# Persistence settings to save retained messages and subscriptions
persistence true
persistence_location /mosquitto/data/

# Logging configuration
log_dest file /mosquitto/log/mosquitto.log
log_dest stdout
log_type all
connection_messages true
log_timestamp true

# Default listeners
# Standard unencrypted listener for local development
listener 1883
allow_anonymous true

# WebSockets listener (optional, useful for web-based dashboards)
listener 9001
protocol websockets
allow_anonymous true

# Secure TLS listener placeholder (uncomment when certificates are added)
# listener 8883
# cafile /mosquitto/config/certs/ca.crt
# certfile /mosquitto/config/certs/server.crt
# keyfile /mosquitto/config/certs/server.key
# require_certificate false

Note: For initial testing, allow_anonymous true is enabled. In production environments, you must set this to false and enforce authentication using password files (password_file) and Access Control Lists (acl_file).

Constructing the Docker Compose Manifest

Using Docker Compose allows us to declare our broker’s network configuration, port mappings, and volume mounts reproducibly. This makes it trivial to spin the stack up or down across different developer workstations.

Create a docker-compose.yml file in the root of C:\docker\mqtt\:

version: '3.8'

services:
  mosquitto:
    image: eclipse-mosquitto:2.0.18
    container_name: mqtt-broker
    restart: unless-stopped
    ports:
      - "1883:1883"
      - "9001:9001"
    volumes:
      - ./config/mosquitto.conf:/mosquitto/config/mosquitto.conf
      - ./data:/mosquitto/data
      - ./log:/mosquitto/log
    networks:
      - iot-network
    healthcheck:
      test: ["CMD-SHELL", "nc -z localhost 1883 || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3

networks:
  iot-network:
    driver: bridge

Key Considerations in the Manifest:

  • Image Pinning: We explicitly pin eclipse-mosquitto:2.0.18 rather than using the latest tag to prevent breaking changes from upstream maintainers.
  • Port Mapping: Ports 1883 (MQTT) and 9001 (WebSockets) are exposed to the host machine, allowing external IoT devices on the local network or host-based microservices to communicate with the broker.
  • Healthcheck: The container uses a netcat (nc) check to ensure the MQTT port is actively accepting connections before marking the container status as healthy.

Deploying and Verifying the Broker

With the configuration and compose manifest in place, launch the container stack in detached mode using Docker Compose:

docker compose up -d

Verify that the container is running and passes its health checks:

docker compose ps

You should see the mqtt-broker container running with a status of healthy.

Inspecting Container Logs

To confirm that Mosquitto has successfully initialized without configuration syntax errors, tail the container logs:

docker compose logs -f mosquitto

A healthy startup sequence will output lines similar to:

mqtt-broker  | 1700000000: mosquitto version 2.0.18 starting
mqtt-broker  | 1700000000: Config loaded from /mosquitto/config/mosquitto.conf
mqtt-broker  | 1700000000: Opening IPv4 listen socket on port 1883.
mqtt-broker  | 1700000000: Opening IPv4 listen socket on port 9001.
mqtt-broker  | 1700000000: Mosquitto broker running.

Testing Pub/Sub Functionality

To validate that the broker is actively routing messages, we can run a dual-terminal test using Mosquitto's built-in command-line tools inside the container, or via a local client.

Terminal 1: Subscribe to a Topic

Execute an interactive shell inside the running container to run a subscriber client:

docker exec -it mqtt-broker mosquitto_sub -h localhost -p 1883 -t "iot/sensors/temperature"

Terminal 2: Publish a Message

Open a second PowerShell window, and publish a payload to the target topic:

docker exec -it mqtt-broker mosquitto_pub -h localhost -p 1883 -t "iot/sensors/temperature" -m "{\"device_id\": \"sensor-01\", \"temp\": 22.5, \"unit\": \"C\"}"

In your first terminal, you should instantly see the JSON payload printed to stdout:

{"device_id": "sensor-01", "temp": 22.5, "unit": "C"}

This confirms that the TCP loopback, internal container networking, and message broker loop are fully operational.

Hardening Security for Production-Ready Workloads

Leaving an MQTT broker open to anonymous access and unencrypted traffic (1883) is a critical security vulnerability. For development environments that mimic production, or environments exposed to local network testing, you should enable password authentication and TLS encryption.

Step 1: Implementing Password Authentication

Modify your C:\docker\mqtt\config\mosquitto.conf file to disable anonymous access and point to a password file:

allow_anonymous false
password_file /mosquitto/config/passwd

Next, generate a password file and add a user (e.g., iot_admin) using the Mosquitto password utility inside the container:

docker exec -it mqtt-broker mosquitto_passwd -c /mosquitto/config/passwd iot_admin

You will be prompted to enter and confirm a secure password. Restart the container to apply the changes:

docker compose restart mosquitto

When publishing or subscribing henceforth, you must pass authentication credentials:

docker exec -it mqtt-broker mosquitto_sub -h localhost -p 1883 -t "iot/#" -u "iot_admin" -P "your_secure_password"

Step 2: Generating Local TLS Certificates for Port 8883

For end-to-end encryption, MQTT uses TLS over port 8883. While production systems utilize certificates issued by a trusted Certificate Authority (CA) like Let's Encrypt, local development environments typically rely on a self-signed local CA.

Create a certs directory inside your configuration folder:

mkdir C:\docker\mqtt\config\certs
cd C:\docker\mqtt\config\certs

Using OpenSSL (available natively in Git Bash or WSL 2 on Windows 11), generate a Certificate Authority and server certificates:

# 1. Generate CA Private Key and Certificate
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=LocalIoT-CA"

# 2. Generate Broker Server Private Key and CSR
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"

# 3. Sign the Server Certificate with the Local CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256

Update your mosquitto.conf to enable the TLS listener:

listener 8883
cafile /mosquitto/config/certs/ca.crt
certfile /mosquitto/config/certs/server.crt
keyfile /mosquitto/config/certs/server.key
require_certificate false

Expose port 8883 in your docker-compose.yml:

    ports:
      - "1883:1883"
      - "8883:8883"
      - "9001:9001"

Restart the stack to activate TLS encryption:

docker compose down
docker compose up -d

Performance Tuning and Troubleshooting on Windows 11

When running high-throughput IoT message brokers under Docker on Windows 11, developers may occasionally run into platform-specific performance bottlenecks.

WSL 2 Resource Constraints

If your broker experiences dropped packets or high latency under load, check your WSL 2 resource allocation. Create or edit the .wslconfig file in your Windows user profile directory (C:\Users\YourUsername\.wslconfig):

[wsl2]
memory=4GB
processors=4
swap=2GB
localhostForwarding=true

Restart WSL from PowerShell to apply changes:

wsl --shutdown

Common Troubleshooting Commands

  • Port Conflicts: If port 1883 is already bound (often by a natively installed Mosquitto service running on Windows), check and stop the conflicting service:
    Get-Service -Name *mosquitto*
    Stop-Service -Name mosquitto
    
  • File Permission Issues: If Mosquitto fails to start due to permission errors on configuration files mapped from the Windows host, ensure that Docker Desktop has proper file-sharing permissions configured for the C:\docker\mqtt path via Settings > Resources > File Sharing.
  • Real-time Container Metrics: Monitor CPU and memory utilization of your broker container:
    docker stats mqtt-broker
    

Conclusion

By combining Windows 11, Docker Desktop, and Eclipse Mosquitto, you have established a reliable, isolated, and production-mirrored MQTT broker environment. This configuration supports both rapid prototyping with unencrypted local sockets and rigorous security validation using access control lists and TLS encryption over port 8883. You are now equipped to connect physical microcontrollers, edge gateways, or backend microservices to your local Windows development pipeline.