MQTT Security: TLS, Authentication and ACLs Done Properly

Quick answer

Plain MQTT on port 1883, the default configuration covered in this site’s basic broker guide, sends credentials and every message in cleartext, fine for a quick local test, not acceptable for anything reachable beyond your own machine. TLS encryption on port 8883, per-device username and password authentication, and topic-level access control lists restricting which device can publish or subscribe to which topics, together close that gap properly, and are worth treating as standard practice for any MQTT broker actually exposed to the internet.

What’s actually at risk with unsecured MQTT

This builds directly on Self-Hosted MQTT Broker on a VPS and the broader principles in the VPS Security Hardening Checklist, applied specifically to MQTT’s own particular weaknesses. An unsecured broker, no TLS, no authentication, accepting connections from anywhere, has two distinct exposure points worth understanding separately: anyone who finds the broker can read every message flowing through it (your sensor data, your device states), and anyone can publish messages too, potentially triggering automations or sending false data into your system. Neither requires sophisticated attack tooling, basic port scanning finds exposed brokers routinely.

Step one: enabling TLS encryption

This encrypts the connection itself, so credentials and message contents can’t be read in transit, the same principle as HTTPS for a website. Using Let’s Encrypt certificates, the same ones likely already issued for any web-facing dashboards covered elsewhere on this site:

# /etc/mosquitto/conf.d/tls.conf
listener 8883
certfile /etc/letsencrypt/live/your-domain/fullchain.pem
keyfile /etc/letsencrypt/live/your-domain/privkey.pem
cafile /etc/letsencrypt/live/your-domain/chain.pem

# Optionally disable the old unencrypted listener entirely
listener 1883 127.0.0.1

That last line is worth highlighting specifically: binding the unencrypted listener to 127.0.0.1 only, rather than removing it entirely, keeps local, same-machine connections (useful for debugging) working while ensuring nothing outside the VPS itself can ever connect without TLS. Devices then connect on port 8883 instead of 1883, a one-line change in most MQTT client configurations, including the ESP32 example covered in ESP32 and MicroPython: Getting Your First Device Talking to a VPS.

Step two: per-device authentication, not one shared password

mosquitto_passwd -c /etc/mosquitto/passwd device-kitchen-sensor
mosquitto_passwd /etc/mosquitto/passwd device-living-room-sensor
mosquitto_passwd /etc/mosquitto/passwd dashboard-readonly

A genuinely important habit worth establishing early, before a fleet grows large enough to make it painful to retrofit: one credential per device or service, not one shared password reused everywhere. This means a single compromised device’s credentials can be revoked individually without disrupting everything else, and gives a clear audit trail of which device is actually connecting, both lost entirely the moment everything shares one login.

# /etc/mosquitto/conf.d/auth.conf
allow_anonymous false
password_file /etc/mosquitto/passwd

Step three: topic-level access control lists

This is the layer most basic MQTT tutorials skip entirely, and the one that actually limits the damage a single compromised or misbehaving device can do: restricting which topics each authenticated user can publish to or subscribe from.

# /etc/mosquitto/conf.d/acl.conf
acl_file /etc/mosquitto/acl

# /etc/mosquitto/acl
user device-kitchen-sensor
topic write sensors/kitchen/#

user device-living-room-sensor
topic write sensors/living-room/#

user dashboard-readonly
topic read sensors/#

With this in place, the kitchen sensor’s credentials, even if somehow leaked, can only ever publish to its own topic namespace, not impersonate other sensors or, more importantly, subscribe to and read everyone else’s data. The dashboard’s read-only account can see everything but write nothing, the correct permission shape for something that only ever needs to display data, never inject it.

A worked example: the genuine difference this makes

Consider a single compromised device on an unsecured broker versus the same scenario with the setup covered in this guide. Unsecured: the attacker reads every sensor’s data across the entire deployment and can publish fake readings or trigger automations on any topic, total compromise from a single weak point. With TLS, per-device auth and ACLs in place: the attacker, even with that one device’s credentials, is confined to that device’s own narrow topic namespace, unable to read or write anything belonging to any other device. This containment, limiting blast radius rather than assuming perfect security, is the same principle that runs through every hardening guide on this site, applied specifically to MQTT’s topic structure.

Designing a topic structure that makes ACLs easy, not painful

ACLs work best with a deliberate, hierarchical topic naming convention decided early, sensors/{location}/{device}/{measurement} or similar, rather than ad hoc topic names invented per device as a fleet grows. A consistent structure means new ACL rules are predictable, topic write sensors/new-location/# for a new device, rather than needing bespoke rules worked out individually for every addition.

Frequently asked questions

Does enabling TLS meaningfully impact a small VPS’s performance?

No, modern hardware, even the entry-level VPS tiers covered in this site’s buying guides, handles MQTT’s TLS overhead comfortably at the message volumes typical of the projects covered throughout this site.

Is client certificate authentication, not just username and password, worth the extra setup effort?

For most projects on this site, username and password authentication over TLS is a reasonable, proportionate security level; client certificates add genuinely stronger device identity verification but considerably more operational overhead managing and rotating certificates per device, worth reserving for higher-stakes industrial deployments rather than a typical home or small business setup.

Can existing devices already configured for plain MQTT be migrated to TLS without reflashing firmware?

Usually yes, if the device’s MQTT library supports TLS at all (most modern ones do, including the umqtt.simple library covered in this site’s ESP32 guide), it’s typically a configuration change, switching the port and adding a CA certificate, rather than a firmware rewrite.

What happens if a device’s password is compromised?

Revoke it immediately via mosquitto_passwd -D /etc/mosquitto/passwd device-name, then issue a new password and restart Mosquitto; the ACL-based containment covered in this guide limits the damage in the meantime, but prompt revocation remains good practice regardless.

Does this guide’s setup work the same way for ThingsBoard’s built-in MQTT support, not just standalone Mosquitto?

The same TLS and authentication principles apply, though ThingsBoard manages device credentials through its own device-management interface rather than Mosquitto’s password file directly; worth reviewing alongside the ThingsBoard Self-Hosted Setup Guide for the platform-specific configuration.

Is it worth running a separate broker instance for genuinely sensitive data versus general telemetry?

For most projects on this site, properly configured ACLs on a single broker achieve the same practical isolation without the added operational overhead of running and maintaining multiple broker instances; a separate instance becomes worth considering only at a scale or sensitivity level genuinely beyond what this site’s typical projects involve.

Testing that security is actually working, not just configured

Worth doing deliberately before trusting a secured broker in production: test each credential against each topic to confirm ACLs are enforced as expected, not just present in the configuration. The mosquitto_pub and mosquitto_sub command-line tools handle this directly:

# This should SUCCEED for the kitchen sensor on its own topic
mosquitto_pub -h your-vps -p 8883 --cafile ca.pem 
  -u device-kitchen-sensor -P its-password 
  -t sensors/kitchen/temperature -m "21.5"

# This should FAIL - the kitchen sensor can't write to living room
mosquitto_pub -h your-vps -p 8883 --cafile ca.pem 
  -u device-kitchen-sensor -P its-password 
  -t sensors/living-room/temperature -m "spoofed"

# This should FAIL - anonymous connections refused entirely
mosquitto_pub -h your-vps -p 8883 --cafile ca.pem 
  -t sensors/kitchen/temperature -m "anonymous"

Running all three of these tests, and confirming the expected outcome of each, takes less than five minutes and gives genuine confidence that the configuration is working correctly rather than merely appearing to in the Mosquitto config file.

Keeping credentials current as a deployment grows

The one ongoing maintenance task worth building a habit around: rotating credentials when devices change hands, leave service, or when any personnel with access to credentials leave a project. Mosquitto’s password file approach makes individual credential rotation cheap, a two-command operation, which makes the habit easier to actually maintain compared to shared-credential approaches where changing anything requires updating every device simultaneously.

What a well-secured MQTT setup looks like end to end

Pulling everything in this guide together into one coherent picture: devices connect to port 8883 using TLS, providing their username and password as part of the MQTT CONNECT packet, which is encrypted in transit. Mosquitto validates the credentials against the password file, checks the connecting user’s permissions in the ACL file, and either accepts or rejects the connection. Accepted connections are allowed to publish only to their own assigned topic namespace and subscribe only to topics explicitly granted to them. Nothing flows in cleartext. No single compromised credential can access anything beyond its own defined scope. This is genuinely adequate security for the projects covered throughout this site, without requiring the complexity of client certificate infrastructure that industrial deployments might eventually warrant.

Mosquitto logs as a diagnostic and security tool

Worth knowing about and using: Mosquitto’s log file, typically at /var/log/mosquitto/mosquitto.log, records connection attempts including failed ones. A device that suddenly stops appearing in logs is a signal worth investigating; repeated failed authentication attempts from an unexpected source are worth paying attention to. The same log that helps diagnose connection problems also gives visibility into whether anyone is probing the broker with incorrect credentials, which, for a broker correctly listening only on TLS port 8883 with authentication required, should result in nothing connecting successfully but is still worth knowing about.

Frequently asked questions

Does enabling TLS meaningfully impact performance on a small VPS?

No, modern hardware handles MQTT’s TLS overhead comfortably at the message volumes typical of the projects covered throughout this site.

Is client certificate authentication worth the extra effort?

For most projects on this site, username and password over TLS is proportionate. Client certificates add stronger device identity verification but considerably more operational overhead managing per-device certificates, worth reserving for higher-stakes industrial deployments.

Can existing devices configured for plain MQTT migrate to TLS without reflashing?

Usually yes, if the device’s MQTT library supports TLS. For ESP32 with MicroPython covered elsewhere on this site, it’s typically a configuration change to the connection call rather than a firmware rewrite.

What if a device’s password is compromised?

Revoke it immediately: mosquitto_passwd -D /etc/mosquitto/passwd device-name, issue a new password, restart Mosquitto. The ACL-based containment covered in this guide limits damage in the meantime.

Does this work the same way for ThingsBoard’s built-in MQTT support?

The same TLS and authentication principles apply, though ThingsBoard manages device credentials through its own device-management interface rather than Mosquitto’s password file directly. See the ThingsBoard Self-Hosted Setup Guide for the platform-specific configuration details.

Monitoring broker health as part of the wider uptime picture

The MQTT broker is the single most critical piece of infrastructure in most of this site’s IoT architectures: if it goes down, every device stops reporting and every automation stops triggering, silently, without necessarily generating any obvious error visible from outside. Adding a TCP-port check to your Uptime Kuma instance (covered in VPS Uptime for 24/7 IoT Operations) on port 8883, confirming the broker is actually accepting connections rather than just showing the VPS as online, catches broker-specific failures that a general ping check would miss entirely.

What a well-secured broker actually looks like from the outside

After implementing everything covered in this guide, what an external observer sees when they encounter the broker: a single open port (8883) that requires a valid TLS handshake before any further communication, then valid credentials presented in the MQTT CONNECT packet before any topics are accessible, then per-topic ACL enforcement governing what an authenticated client can actually do. A client with invalid credentials gets a CONNECTION_REFUSED response. A client with valid credentials but no ACL permission to publish or subscribe to a specific topic gets an appropriate refusal at that level. No data leaks through any of these layers without passing all of them, which is precisely the defence-in-depth model good IoT security is built on. Each layer individually reduces risk; all three together make an unsophisticated, opportunistic attacker’s job significantly harder, which covers the vast majority of realistic threat scenarios for the projects covered throughout this site. It is also directly compatible with the hardening checklist and certificate guidance covered in the site’s other security guides. TLS on MQTT and WireGuard on remote access together handle the two most common IoT attack surfaces simultaneously.