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:
- Is the broker running?
- Is the broker listening on the port you think?
- Is Docker publishing that port to the host?
- Are you connecting the right way (host vs container)?
- Is it TLS or plaintext?
- 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/tcpor0.0.0.0:8883->8883/tcp
What bites people:
- You see
1883/tcp(container-only) without0.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:18838883/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 1883orport 1883(plaintext)listener 8883with 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 authorizedDenied 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 falsewithout 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.