Lock it down, then learn to read the log
Your broker now carries the MQTT messages for the whole house, which is exactly what makes it one of the doors an attacker most wants to try. This last part of the series does two jobs. First it closes the doors: which of the five ports are actually open on your Home Assistant host, why an EMQX with no authenticator lets every client connect, and how rate limits, TLS and a protected admin surface stack into layers. Then it turns to the day something breaks, with four symptom-and-fix checklists and the diagnostic order — log, ports, resources — that solves most of it before you ever consider reinstalling anything.
Security is not one setting, it is four stretches of one path
Everything in this series so far has been about getting messages to move. A broker that carries every sensor reading, every light command and every automation trigger in the house is also the single thing an attacker most wants to reach: anyone who can connect, get past authentication or subscribe could read your sensor data or drive your devices.
The Woow EMQX add-on runs in host_network mode, which opens its ports straight on the Home Assistant host. There is no container-level private network sitting in between. That makes the security boundary thinner than it would be for a container confined to a virtual network, and it means you have to hold that line by hand.
It helps to stop thinking of security as a switch and start thinking of it as four stretches of the path a message travels:
The official security checklist points out one fact that is easy to miss, and it is the single most important line in this chapter: until you configure Authentication, EMQX allows every client to connect. Setting up authentication is not extra credit you get around to later. It is something you finish before you start EMQX up.
A shop with the door propped open and nobody on the till is not secure just because nothing has gone missing yet. Nothing going missing is a fact about this afternoon, not about the lock. An EMQX with no authenticator configured is that shop: every client that knocks is let in, and the quiet is not evidence.
flowchart TD C["A device, Z2M or
Home Assistant"] --> L["Listener: 1883, 8083, 8084, 8883
is there TLS on this port?"] L --> AU["Authentication
may this client connect at all?"] AU --> AZ["Authorization, the ACL
which topics may it publish
and subscribe to?"] AZ --> R["Resource protection
Rate Limit, Blacklist, Flapping Detect"] R --> B["The broker carries
the message"] ADM["The admin surface
Dashboard and REST API on 18083"] --> B
| Term | Plain English | In one line |
|---|---|---|
| Host Network | host network mode | The add-on uses the host's ports directly |
| Exposure | attack surface | The range of listener ports that can be reached from outside |
| Rate Limit | rate limit | Caps the connection and publish rate so resources are not exhausted |
| TLS | transport encryption | Encrypts the traffic between a client and the broker |
| Least Privilege | least privilege | Give an account only the rights it needs to do its job |
| API Key | API key | The credential the REST API authenticates with, not the Dashboard password |
admin/public is public knowledge — it is written in the documentation, which means everybody has it. Change it the moment you enable the add-on. And when you write anything down for a colleague or paste it into a forum, keep API keys and MQTT credentials as obvious placeholders such as <your-password> or YOUR_TOKEN, and never print details of your local network.Five ports, and who can reach them
host_network in the Woow add-on means EMQX uses the Home Assistant host's ports directly. Five of them:
| Port | What it carries | Encrypted |
|---|---|---|
1883 | MQTT | No — plaintext |
8083 | MQTT over WebSocket | No — plaintext |
8084 | WSS, MQTT over secure WebSocket | Yes |
8883 | MQTTS | Yes |
18083 | The Dashboard, the HTTP admin surface | The admin port — the last thing you want exposed |
The risk comes down to a single question: who can reach those ports? If the Home Assistant host stays on a trusted local network the threat is low and the risk is manageable. But as soon as you forward a port on the router, put the host in a DMZ, or expose 1883 through ngrok, you have pushed the broker onto the public internet, and those listener ports can be scanned from it.
Most add-ons are a flat in a block: to reach the front door, somebody first has to get past the entrance downstairs. host_network is a house that opens straight onto the street. Nothing is wrong with a house on a street — it just means the front door is the only lock there is, so it had better be a good one, and you had better know how many doors the house has.
Secure deployment comes down to one rule: expose only the listeners you need. In practice that is three habits.
- Open only the listeners you need. Do not expose all five ports at once because it was easier to open them together.
- Keep plaintext
1883on the local network, or use it only as a stopgap. For anything facing outward, prefer the encrypted8883and8084. - Restrict the Dashboard on
18083to a trusted network, use HTTPS, and allow only the accounts you actually need.
flowchart TD A["A listener port on the
Home Assistant host"] --> B{"Can anything outside the
local network reach it?"} B -->|"no, trusted LAN only"| C["Manageable risk. Still set
authentication and an ACL"] B -->|"yes: router forward,
DMZ or ngrok"| D{"Which port is it?"} D -->|"18083, the Dashboard"| E["Do not expose it. Local network
or a trusted connection only,
over HTTPS"] D -->|"1883, plaintext MQTT"| F["Local network, or a stopgap.
Move outward traffic to 8883"] D -->|"8883 or 8084, encrypted"| G["Acceptable once authentication,
the ACL and a Rate Limit
are all in place"]
The hands-on pass, in order
Four steps, and the order matters: the password first because it is public knowledge, then who may connect, then what they may do, then how much they may do it.
-
Step 1
Change the default admin password
If the Dashboard asks you to at first login, replace
publicwith a strong password of your own. If it does not ask, go into the Dashboard's user settings and change it by hand anyway. Leaving it means anybody can log in with the published default. -
Step 2
Set up Authentication
Go to Access Control → Authentication and create an authenticator. Password-Based → Built-in Database is the recommended one to start with. Then create real users, in place of the default anonymous connection. Before you expose a listener you need at least this much: no anonymous connections.
-
Step 3
Narrow the ACL with Authorization
Go to Access Control → Authorization and write each user's Allow rules to least privilege: which topics Home Assistant needs to publish and subscribe to, which ones Z2M needs. List the specific topics one by one. Do not open everything with
#. -
Step 4
Add rate limits and review the listeners
On the Management → Listeners page, set a Rate Limit on every listener that faces outward — max connections per second, for example. While you are on that page, confirm that
18083is not directly exposed: keep the Dashboard on the local network, or put authentication in front of it.
8883 (MQTTS) and 8084 (WSS). Part 4 of this series, on listeners and TLS, has the details.What stops one client taking the broker down
Rate Limit — the limiter — is an EMQX 5.0 and later mechanism that caps the rate at the entry point, so a single client or listener cannot be flooded. You set a value per listener on the Dashboard's Management → Listeners page, and you can also set it in emqx.conf.
| Type | UI label | What it does | Default behavior when exceeded |
|---|---|---|---|
bytes_rate | Data Publish Rate | Bytes published per client per second | Stops taking messages from that client |
messages_rate | Messages Publish Rate | Messages published per client per second | Stops taking messages from that client |
max_conn_rate | Maximum Connection Rate | Connections that listener accepts per second | Stops taking new connections |
Time units are s, m, h and d; sizes are KB, MB and GB. In emqx.conf, limiting the default TCP listener looks like this:
listeners.tcp.default {
bind = "0.0.0.0:1883"
max_conn_rate = "1000/s"
messages_rate = "1000/s"
bytes_rate = "1MB/s"
}When a listener is exposed, Rate Limit works alongside two other mechanisms. Blacklist shuts out a client you have identified as a problem. Flapping Detect catches a client that connects and disconnects over and over. Together the three stop one client from flooding the broker or abusing reconnects.
Encryption on the wire, and a lock on the control room
When traffic crosses an untrusted network, use TLS. EMQX's encrypted listeners are 8883 (MQTTS) and 8084 (WSS). Three things go with that:
- Certificates should be issued by a trusted CA, or by your own internal PKI if you run one.
- Rotate them before they expire. An expired certificate does not degrade gracefully; every client stops connecting at once.
- To identify a device by its certificate, add X.509 authentication and mTLS (
verify_peer), so the broker checks the client's certificate as well as the client checking the broker's.
The admin surface then has a few hard rules of its own.
Change the password, restrict who can reach it
Leaving the Dashboard on its default password is a warning sign, not a minor oversight. Change it at first login, and make sure only the people who need it can reach the page at all.
Give the REST API its own key
The REST API should authenticate with an API Key from system → API Key, granted the smallest role possible — not with a Dashboard login.
Bind the Dashboard somewhere trusted
Where you can, bind it to a trusted interface: localhost, the local network, or a dedicated management segment.
Least privilege applies at every layer, and it is the thread running through all of this: give each device its own credentials, let the ACL allow only the topics that are needed, and do not open everything with a wildcard. The finer the controls, the smaller the hole a single leaked credential opens.
The cleaner gets a key to the front door and the store cupboard, not a key that opens the safe and the server room as well. When that key goes missing you change two locks, not fourteen. That is the whole of least privilege, and it is why one credential per device beats one shared credential for the house.
Log, then ports, then resources
When EMQX misbehaves, the hard part is rarely that something is broken. It is not knowing where to start looking. Plenty of people see the add-on fail to start, remove and reinstall the whole thing, and end up wiping their settings and losing their accounts too.
The Woow add-on README and the EMQX documentation agree on the same first move, and the order is worth memorizing.
1883 or 18083, EMQX cannot listen. Check whether Mosquitto, WebRTC and the like are still running.Reinstalling before you have read the log is replacing the boiler because one radiator is cold. It might even work. But you will have spent a weekend and a lot of money on something a bleed key would have fixed in five minutes — and if the real fault was the pump, the new boiler has not fixed it either.
Every repair step should be the smallest safe action. Do not delete data on a hunch, and do not quietly move a port out onto the open network to see whether that helps. Before you touch data at all, take a backup.
Set a diagnostic baseline
-
Step 1
Confirm the add-on really started
Open the Woow EMQX page in Home Assistant and see whether the state is started, or a restart loop. A restart loop usually shows a port or memory reason in the log.
-
Step 2
Check 1883 and 18083
Make sure no Mosquitto or WebRTC is holding
1883or8083, so EMQX does not exit at startup because its listener port is taken. -
Step 3
Search the log for keywords
Searching the add-on log narrows the problem down fast. Three phrases do most of the work:
address already in use— something else already holds that portcannot bind— the same problem, worded differentlyout of memory— the host does not have enough RAM left -
Step 4
Test with an MQTT client you trust
Connect to
1883with a small tool you already have — the WebSocket client, or your Home Assistant MQTT integration. If it connects, the broker is accepting connections. If it does not, check whether authentication is set up, and whether the port and address are right.
flowchart TD A["The add-on will not start,
or keeps restarting"] --> B["Read the add-on log first"] B --> C{"Does it say address already
in use, or cannot bind?"} C -->|"yes"| D["Port conflict. Mosquitto
holds 1883, WebRTC holds 8083.
Stop the conflicting one"] C -->|"no"| E{"Does it say out of memory,
or not enough system RAM?"} E -->|"yes"| F["Resources. At least 512MB RAM
is recommended. Turn off other
add-ons that eat memory"] E -->|"no"| G["Test a connection to 1883
with a client you trust"] G --> H{"Does it connect?"} H -->|"yes"| I["The broker is accepting.
Look at the client's credentials
and the address it uses"] H -->|"no"| J["Check Authentication, the
listener port and the address"]
Symptom, check, safe fix
Four checklists cover most of what actually goes wrong. Each one is a symptom you can see, a check that finds the cause, and a fix that deletes nothing.
1 · It will not start, or it is running out of resources
Symptom: the add-on drops back to stopped shortly after it starts, or hangs on starting.
- Check: read the log first. Then confirm whether another service is holding the port — Mosquitto uses
1883, WebRTC uses8083— and confirm the host still has enough memory. - Safe fix: stop the conflicting add-on or shed some load, then start EMQX once more. If memory is short, turn off the other add-ons that eat resources. Leave the data directory alone.
EMQX needs more RAM and CPU than Mosquitto does; the Woow README recommends at least 512MB RAM. If your host also runs video or AI services, cut back there before you add anything here.
2 · It cannot connect, or authentication keeps failing
Symptom: Home Assistant, Z2M or an outside device drops when it connects to 1883, or the error says authentication failed.
- Check: confirm Authentication is configured in EMQX; confirm the username and password you are using exist in the built-in database and are correct; confirm the broker address (
homeassistant,1883) is right. - Safe fix: if authentication is not set up, create a user first. If an account was changed by mistake, create it again and switch to the correct password. Never print a real password into a log, or into an answer you give somebody else.
One thing to watch here, and it is the same fact from the start of this article seen from the other side: while EMQX has no authentication configured, it lets every client connect. A connection that works in that state is not proof that anything is set up properly. Either set up authentication, or turn anonymous access off.
3 · The Dashboard will not open, or a port is taken
Symptom: clicking Open Web UI gives you no page, or 18083 will not open.
- Check: whether another service is holding
18083; whether the add-on is in the started state; whether Ingress is actually reachable. - Safe fix: go in through the EMQX icon in the Home Assistant sidebar, which is the Ingress route. Stop or adjust whatever service conflicts with
18083. Restart the add-on and try again.
The Dashboard is the way in to the whole broker. If you do want to reach 18083 directly rather than through Ingress, make sure it is not exposed to the public internet first.
4 · Connections drop after a while
Symptom: everything connects, works for a stretch, and then clients start falling off.
- Check: the resource limits on the host, and the Rate Limit you set on the listener. EMQX uses more resources than Mosquitto, and a host tight on memory will make it less stable.
- Safe fix: if normal clients are being dropped after you set a rate limit, check the time unit and the number in the limit, and raise the Flapping threshold to a level a normal reconnect will not trip. Healthy devices should not be locked out by mistake.
The quick reference
| Symptom | What to do |
|---|---|
| The add-on will not start and the log shows a port already in use | Stop Mosquitto or WebRTC first; confirm 1883 and 8083 are free, then start it |
| Cannot connect, and authentication keeps failing | Confirm Authentication is set up, the credentials are correct, and the broker address and port are right; stop using the default public |
| The Dashboard will not open | Go in through Ingress; if you hit 18083 directly, confirm there is no port conflict and that it is not exposed straight to the public internet |
| Connections drop after a while | Check the resource limits and the Rate Limit; EMQX uses more resources than Mosquitto, and a host tight on memory will make it less stable |
| ngrok is enabled but there is no public URL | Go back to Part 8 of this series: check the authtoken, restart, and read the ngrok service log |
| 1883 is scanned from outside and people start trying to connect | Turn the listener off or restrict it to the local network first, and only consider exposing it once both authentication and the ACL are in place |
| The TLS connection fails | Check the certificate chain, that the private key matches, and the CA; with a self-signed certificate you also have to add that CA to the client's trust store |
| The Dashboard is reachable straight from the public internet | 18083 is the HTTP admin port and is the last thing you want exposed. Restrict it to the local network or a trusted connection, and switch to HTTPS with a strong password |
flowchart LR A["Nothing outside
can connect"] --> B["Test from a client on
the same local network"] B --> C{"Does the local
connection work?"} C -->|"yes"| D["EMQX is fine. Look at
the router, ngrok
or the firewall"] C -->|"no"| E["Usually authentication,
or the listener port
and address"]
The ones that come up every time
Do I have to set up TLS for MQTT?
8883 or 8084.Will a rate limit slow down normal use?
After I change the Dashboard password, does MQTT use the same one?
Does EMQX Open Source have RBAC?
Is reinstalling the add-on faster than fixing it?
/data/emqx first.How do I find out what is holding 1883?
1883, or the WebRTC integration, which uses 8083. Check the add-on and integration lists in Home Assistant and pause whichever one conflicts.How do I tell that memory is the problem?
out of memory or not enough system RAM. At least 512MB is recommended. With the resource-hungry add-ons paused, EMQX settles down more easily.Nothing outside can connect: is that EMQX or my network?
Where to go from here
That is the whole broker, front door included.
Nine parts ago EMQX was an add-on you had not installed. It now carries your messages, checks who is asking, decides what they may touch, routes what matters through the rule engine, and tells you where to look on the morning it does not. Keep this part bookmarked — the four checklists are the ones you will come back to.
Open the full guidePart 9 of the WoowTech EMQX Complete Guide series on the Apporo blog.
Adapted from the WoowTech EMQX Complete Guide, produced by WoowTech and released under CC BY 4.0. This adaptation is published by Apporo under the same licence.
Light · Air · Water · Control · apporo