Connection Refused: The Mosquitto Docker Checklist (MQTT That Actually Works)

If you've ever typed a perfectly reasonable mosquitto_sub command and been slapped with:

Error: Connection refused

…welcome to the club. The club has jackets. The jackets say "it was the port mapping."

This post is a battle-tested checklist for Mosquitto running in Docker, designed to get you from "it's broken" to "I can prove exactly what's broken" fast — without exposing anything sensitive.


The Rule of MQTT Debugging

Stop guessing. Start isolating.

There are only a few moving parts:

  1. Is the broker running?
  2. Is the broker listening on the port you think?
  3. Is Docker publishing that port to the host?
  4. Are you connecting the right way (host vs container)?
  5. Is it TLS or plaintext?
  6. Is auth/ACL blocking you?

"Connection refused" typically means you're failing at #1–#4 (sometimes #5).


Phase 1 — Confirm the broker exists and is alive

1) Check container status

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

What you want:

  • Container is Up
  • Ports show a host mapping like 0.0.0.0:1883->1883/tcp or 0.0.0.0:8883->8883/tcp

What bites people:

  • You see 1883/tcp (container-only) without 0.0.0.0:1883->1883/tcp (not published to host)

2) Check broker logs (it tells the truth)

docker logs --tail 200 mosquitto

You're looking for:

  • Startup lines
  • Errors like "Address already in use"
  • Config parse failures
  • TLS/cert issues

If Mosquitto is crash-looping, you'll see it here.


Phase 2 — Ports: what's listening, what's published

3) Inspect the actual port publishing

docker port mosquitto

Example "good" output:

  • 1883/tcp -> 0.0.0.0:1883
  • 8883/tcp -> 0.0.0.0:8883

If it outputs nothing, nothing is published.

4) Verify the host is listening on that port

sudo ss -lntp | egrep ':1883|:8883'

What you want:

  • A listening socket on the published port(s)

If Docker says it's published but ss shows nothing:

  • Container not actually bound
  • Service inside container isn't listening
  • Docker daemon/networking issues

Phase 3 — The #1 gotcha: localhost vs container network

This one causes so much pain it deserves its own warning.

You are in one of these worlds:

World A: Running the client on the HOST

  • Use -h localhost (only if the port is published)
  • Or use -h <host-ip>

World B: Running the client INSIDE the container

  • Use -h localhost (broker and client share container network namespace)

World C: Running the client in ANOTHER container

  • Use the broker container name on a Docker network, e.g. -h mosquitto
  • Or publish ports and connect via host gateway

Wrong combination = connection refused.


Phase 4 — TLS vs plaintext (the sneaky "refused" cousin)

If your broker exposes only 8883, and you try to connect to 1883, you'll fail.

If your broker listens only on TLS, and you connect without TLS, you'll fail (often with handshake errors).

5) Determine what listeners are configured

docker exec -it mosquitto sh -lc "grep -R \
  \"^listener\\|^port\\|^cafile\\|^certfile\\|^keyfile\\|^allow_anonymous\\|^password_file\\|^acl_file\" \
  -n /mosquitto/config /mosquitto/config/conf.d 2>/dev/null"

Look for:

  • listener 1883 or port 1883 (plaintext)
  • listener 8883 with cert/key settings (TLS)
  • allow_anonymous false (auth required)

If you don't see any listeners, Mosquitto might be using defaults — or your config isn't being loaded.


Phase 5 — Prove the broker works with a local loopback test

This is the fastest sanity check.

6) Run subscriber and publisher in the SAME environment

Option 1: Test from inside the container (most reliable baseline)

Terminal 1:

docker exec -it mosquitto sh -lc "mosquitto_sub -h localhost -t 'test/#' -v"

Terminal 2:

docker exec -it mosquitto sh -lc "mosquitto_pub -h localhost -t 'test/ping' -m 'hello from inside the container'"

If that works, the broker is fine internally.

If that fails with refused:

  • Mosquitto isn't listening
  • Wrong entrypoint/image
  • Config broke the service

Option 2: Test from the host (validates port publishing)

Terminal 1:

mosquitto_sub -h localhost -t 'test/#' -v

Terminal 2:

mosquitto_pub -h localhost -t 'test/ping' -m 'hello from the host'

If inside-container works but host fails:

  • Your port mapping is the problem (published ports, firewall, bind address)

Phase 6 — Auth/ACL checks (not "refused" but often the next wall)

Once you get past connection refused, you may hit:

  • Not authorized
  • Denied PUBLISH
  • Silent disconnects

7) If auth is enabled, test with username/password

mosquitto_sub -h localhost -p 1883 -u "<user>" -P "<pass>" -t "test/#" -v

If you get Not authorized:

  • Wrong creds
  • Password file not loaded
  • allow_anonymous false without proper users

If you get Denied PUBLISH:

  • ACL is doing its job
  • Your rules don't match your topic tree

The Fast Diagnosis Map

When you see Connection refused, it's usually one of these:

  • Container not running — start it, check logs.
  • Port not published — container-only port shown, but no host mapping.
  • Connecting to the wrong place — host vs container vs other container mismatch.
  • Wrong port / TLS mismatch — you're hitting 1883 but only 8883 exists (or vice versa).
  • Mosquitto isn't listening due to config error — container "Up" but broker isn't bound.

Closing (Reps4Thor-style)

When MQTT breaks, it doesn't break creatively. It breaks in the same boring ways every time.

"Connection refused" is not a mystery — it's a checklist item that's mad you skipped it.

Next post idea: "Denied PUBLISH: How to Write MQTT ACLs Without Losing Your Mind" — because once you're connected, permissions become the real boss fight.