Tutorials

Run Mosquitto in Docker (docker run & docker-compose)

Run the eclipse-mosquitto image with docker run or Docker Compose: config, data and log volumes, ports 1883 and 9001, plus a password file via docker exec.

Updated · 7 min read

Docker is the quickest way to get a disposable, reproducible MQTT broker. The official eclipse-mosquitto image is small and well maintained, but its defaults surprise almost everyone the first time: without a config file, you cannot connect from outside the container. This guide sets up Mosquitto in Docker properly — with persistent volumes, TCP and WebSocket ports and password authentication.

1. Create the folder layout

The image expects three directories under /mosquitto. Mirror them on the host:

bash
mkdir -p mosquitto/config mosquitto/data mosquitto/log
touch mosquitto/config/mosquitto.conf
Host folderContainer pathPurpose
./mosquitto/config/mosquitto/configmosquitto.conf, password file, ACL file, certificates
./mosquitto/data/mosquitto/dataPersistence database (retained messages, sessions)
./mosquitto/log/mosquitto/logLog files, if you log to a file

2. Write a minimal mosquitto.conf

Mosquitto 2.x only listens on localhost when no listener is configured — inside a container that means nobody can reach it. This config opens MQTT on 1883 and WebSockets on 9001:

mosquitto/config/mosquitto.conf
# MQTT over TCP
listener 1883
# MQTT over WebSockets
listener 9001
protocol websockets

persistence true
persistence_location /mosquitto/data/

log_dest file /mosquitto/log/mosquitto.log
log_dest stdout

# For a first test only — switched to a password file in step 5
allow_anonymous true

Want TLS, ACLs or bridge settings? The mosquitto.conf generator builds a valid file from a form, and the install guide explains the local-only mode in more detail.

3. Start it with docker run

docker run
docker run -d --name mosquitto \
  --restart unless-stopped \
  -p 1883:1883 -p 9001:9001 \
  -v "$PWD/mosquitto/config:/mosquitto/config" \
  -v "$PWD/mosquitto/data:/mosquitto/data" \
  -v "$PWD/mosquitto/log:/mosquitto/log" \
  eclipse-mosquitto:2

Pinning the major version tag (eclipse-mosquitto:2) avoids surprise upgrades. Check the logs with docker logs -f mosquitto; you should see Opening ipv4 listen socket on port 1883 and a websockets line for 9001.

4. Or use Docker Compose

For anything beyond a quick test, a Compose file is easier to keep in version control:

compose.yaml
services:
  mosquitto:
    image: eclipse-mosquitto:2
    container_name: mosquitto
    restart: unless-stopped
    ports:
      - "1883:1883"   # MQTT
      - "9001:9001"   # MQTT over WebSockets
    volumes:
      - ./mosquitto/config:/mosquitto/config
      - ./mosquitto/data:/mosquitto/data
      - ./mosquitto/log:/mosquitto/log
bash
docker compose up -d
docker compose logs -f mosquitto

Other services in the same Compose project (Node-RED, Home Assistant, your app) reach the broker by service name: mqtt://mosquitto:1883. Only publish ports to the host that something outside Docker actually needs.

5. Add a password file via docker exec

Anonymous access is fine for a minute of testing, never for anything longer. Create a user with mosquitto_passwd, which ships inside the image:

Create the password file and first user
# -c creates the file (overwrites it if it exists!) — use only for the first user
docker exec -it mosquitto mosquitto_passwd -c /mosquitto/config/passwd alice

# add more users without -c
docker exec -it mosquitto mosquitto_passwd /mosquitto/config/passwd bob

Then switch the config from anonymous to password authentication:

mosquitto/config/mosquitto.conf (changes)
allow_anonymous false
password_file /mosquitto/config/passwd
bash
docker restart mosquitto

ACLs and TLS work the same way — the files go in the config volume. See securing Mosquitto with passwords, ACLs and TLS.

6. Test the container

From the host (with the Mosquitto clients installed):

bash
mosquitto_sub -h localhost -p 1883 -u alice -P 'secret' -t 'demo/#' -v &
mosquitto_pub -h localhost -p 1883 -u alice -P 'secret' -t 'demo/hello' -m 'hi from docker'

No clients on the host? Use the ones inside the container:

bash
docker exec -it mosquitto mosquitto_sub -h localhost -u alice -P 'secret' -t 'demo/#' -v

The WebSocket listener on 9001 is what browser clients use (ws://<host>:9001). Keep in mind that a page loaded over HTTPS may only open wss:// connections, so testing a local broker from a hosted web client usually needs TLS in front of port 9001 — more in MQTT over WebSockets. To see what a working round trip looks like in the browser, try a public broker first:

Try it in the online MQTT client

Troubleshooting the container

SymptomCauseFix
Log says "Starting in local only mode"The config was not mounted, or it defines no listenerCheck the volume path and that mosquitto.conf contains listener 1883
Connection refused from the hostPort not publishedAdd -p 1883:1883 (or the ports: entry) and recreate the container
"not authorised" / CONNACK 5Anonymous access disabled, wrong user or passwordRecreate the user with mosquitto_passwd, then docker restart mosquitto
"Unable to open log file" or persistence errorsThe broker user cannot write to the mounted folderGive UID 1883 write access to ./mosquitto/data and ./mosquitto/log
Container restarts in a loopInvalid directive in the configRead the last lines of docker logs mosquitto; the bad line is named there

For CONNACK codes beyond these, the MQTT reason codes reference and the connection errors guide explain what the broker is telling you.

Updating the image

Because config, data and logs live on the host, upgrading is just pulling a newer image and recreating the container. Retained messages and persistent sessions survive as long as the data volume is kept:

Compose
docker compose pull mosquitto
docker compose up -d mosquitto
docker compose logs --tail 20 mosquitto

With plain docker run, pull the image, then docker rm -f mosquitto and run the same docker run command again. Keeping that command in a small shell script (or switching to Compose) saves you from retyping the volume flags.

Production tips

  • Pin an exact image tag and upgrade deliberately; read the Mosquitto changelog before major upgrades.
  • Keep data on a volume, otherwise retained messages and persistent sessions vanish when the container is recreated.
  • Put TLS on 8883 (and on the WebSocket listener) before exposing anything to the internet — see MQTT ports for the conventions.
  • To debug, read docker logs mosquitto rather than running mosquitto -v via docker exec — that starts a second broker inside the container and fails with "address already in use".

Frequently asked questions

Why can I not connect to the Mosquitto Docker container from my host?

Without a mounted mosquitto.conf that defines a listener, Mosquitto 2.x runs in local only mode and only accepts connections from inside the container. Mount a config with listener 1883 and an authentication setting, and publish the port with -p 1883:1883.

How do I add a user to Mosquitto running in Docker?

Run mosquitto_passwd inside the container with docker exec, writing to a password file in the mounted config folder, then restart the container so the broker reloads the file.

Which ports does the eclipse-mosquitto image use?

Whatever listeners you configure. By convention 1883 is plain MQTT over TCP, 8883 is MQTT over TLS and 9001 is MQTT over WebSockets. Each must be defined in mosquitto.conf and published with Docker.