Self-Hosted MQTT Broker on a VPS (Mosquitto)

Quick answer

Mosquitto is the most widely used open-source MQTT broker, and running your own on a VPS gives every IoT device on your network one shared, always-on place to publish and subscribe to messages, without depending on a vendor’s cloud. The standard ports are 1883 (plaintext) and 8883 (MQTT over TLS). Authentication and topic-level access control are not optional for anything reachable outside your home network.

The problem MQTT actually solves

Without a message broker, every device-to-device or device-to-dashboard connection is its own point-to-point integration: your doorbell talks to its own app, your thermostat talks to its own app, and getting them to talk to each other means hacking together vendor-specific APIs that can change or disappear at any time. MQTT flips this: every device publishes to a topic on a shared broker, and anything that cares about that data subscribes to it. Add a new device, and it just publishes to a new topic, no new integration required on the receiving end.

It’s a genuinely old, genuinely boring protocol by design (it dates to 1999, built for low-bandwidth satellite links), which is exactly why it’s still the standard: small message overhead, works on flaky connections, and does one job well rather than trying to be everything.

Installing Mosquitto on a fresh VPS

sudo apt update
sudo apt install mosquitto mosquitto-clients
sudo systemctl enable mosquitto

At this point Mosquitto is running, but in its default state it’s not something you want reachable beyond localhost: no authentication, no encryption, anyone who can reach port 1883 can publish and subscribe to everything. The rest of this guide is making it safe to actually expose.

How the pieces fit together

Sensor

Camera

Gauge / PLC

VPS MQTT Broker Mosquitto

Node-RED

Dashboard

Devices publish once – anything subscribed to the topic gets the data

Step 1: Authentication with a password file

Create a password file and add your first user:

sudo mosquitto_passwd -c /etc/mosquitto/passwd youruser

Then point Mosquitto at it and disable anonymous access, in /etc/mosquitto/mosquitto.conf:

password_file /etc/mosquitto/passwd
allow_anonymous false

allow_anonymous false is the single most important line in this entire guide. Without it, the password file exists but isn’t actually enforced, and anyone can still connect without credentials.

Step 2: Topic-level access control with an ACL file

A password gets someone onto the broker. An ACL file controls what they can actually do once connected, which matters because not every device needs access to every topic. Create /etc/mosquitto/acl:

user youruser
topic readwrite #

user doorbell-device
topic write home/doorbell/#
topic read home/doorbell/commands

Then reference it in the config:

acl_file /etc/mosquitto/acl

This means a compromised or misbehaving doorbell can only publish to its own topic tree, not read your entire home’s sensor data or publish commands to other devices. Worth doing even on a small setup, not just at scale.

Step 3: TLS encryption (port 8883)

Without TLS, MQTT credentials and message contents travel in plaintext, readable to anyone positioned to intercept the traffic. For a broker reachable outside your home network, this isn’t optional. Using Let’s Encrypt for a free, trusted certificate:

sudo apt install certbot
sudo certbot certonly --standalone -d mqtt.yourdomain.com

Then point Mosquitto at the certificate in a new listener block:

listener 8883
cafile /etc/letsencrypt/live/mqtt.yourdomain.com/chain.pem
certfile /etc/letsencrypt/live/mqtt.yourdomain.com/cert.pem
keyfile /etc/letsencrypt/live/mqtt.yourdomain.com/privkey.pem

Restart Mosquitto and devices should connect on 8883 with TLS rather than 1883 in plaintext. Most modern MQTT client libraries (including Home Assistant’s integration) support TLS connections natively, it’s usually just a checkbox and a port change in the client configuration.

Step 4: Firewall rules

Only open the ports you’re actually using. If every device connects over TLS, there’s no reason to leave 1883 open to the world:

sudo ufw allow 8883/tcp
sudo ufw deny 1883/tcp    # or simply don't allow it through the firewall at all

A common, reasonable pattern: leave 1883 open only to localhost or your VPN subnet (useful for local debugging with mosquitto_sub/pub), and require TLS on 8883 for anything connecting from outside.

MQTT ports at a glance

Port Use Recommended exposure
1883 MQTT, plaintext Localhost/VPN only, or closed entirely
8883 MQTT over TLS Open, this is the one devices outside your network should use
9001 (commonly used) MQTT over WebSockets Open only if a browser-based client needs it; not an IANA-standard port, configurable

Testing it works

From the VPS itself, or any machine with mosquitto-clients installed and the right credentials:

mosquitto_sub -h mqtt.yourdomain.com -p 8883 --cafile chain.pem -u youruser -P yourpassword -t "test/#"
mosquitto_pub -h mqtt.yourdomain.com -p 8883 --cafile chain.pem -u youruser -P yourpassword -t "test/hello" -m "it works"

If the subscriber receives the published message, authentication, ACLs and TLS are all working correctly together.

What to connect next

A broker on its own just moves messages around, it doesn’t do anything with them. The natural next steps: Node-RED to build automation logic on top of the data, or Grafana + InfluxDB to actually visualise it. A small VPS running just Mosquitto comfortably handles thousands of small messages a day on the cheapest plans available; LumaDock‘s entry tier is genuinely enough to start with.

Troubleshooting common problems

A handful of issues account for almost every MQTT broker problem people run into. Worth checking these in order before assuming something more complex is wrong.

“Connection refused” when a device tries to connect. Almost always a firewall issue: confirm the relevant port (1883 or 8883) is actually open with sudo ufw status, and confirm Mosquitto itself is running with sudo systemctl status mosquitto. A surprisingly common variant: the device is configured to connect on 1883 but you’ve only opened 8883 in the firewall, or vice versa.

“Connection refused” specifically with TLS enabled. Usually a certificate path issue in mosquitto.conf, or a certificate that’s expired (Let’s Encrypt certificates need renewing every 90 days, automated by certbot’s default cron job, but worth confirming that’s actually running with sudo certbot renew --dry-run).

A device connects but messages never arrive at the subscriber. Check the topic strings match exactly, including case, since MQTT topics are case-sensitive and a single typo (Home/sensor vs home/sensor) means the subscriber simply never sees the message, with no error on either side to indicate why.

“Not authorized” errors after setting up the ACL file. The most common cause is a missing default-deny: without an explicit pattern matching a user’s actual topic, Mosquitto denies by default, which is the correct secure behaviour but catches people off guard if they expected an implicit allow. Double-check the exact topic patterns in the ACL file against what the device is actually publishing to.

High CPU usage from Mosquitto specifically (rare, but worth knowing the cause). Usually a misbehaving device publishing far more frequently than intended, sometimes due to a bug in its firmware causing a tight retry loop. Checking message rates per topic with mosquitto_sub -t '/#', which exposes Mosquitto’s own internal statistics topics, is the fastest way to spot this.

Last Will and Testament: a feature worth knowing about

MQTT has a built-in mechanism for handling devices that disconnect ungracefully (power loss, lost Wi-Fi, a crash) rather than cleanly: the Last Will and Testament, or LWT. When a client connects, it can register a message to be published automatically by the broker if that client disconnects without saying goodbye properly. In practice, this is commonly used to publish a “device offline” status to a dedicated topic, which downstream dashboards and automations can use to distinguish “this device just hasn’t reported a new value yet” from “this device has actually gone offline”, a distinction that matters a lot for reliable alerting.

Monitoring broker health over time

Mosquitto publishes its own internal statistics to a set of topics, covering message throughput, connected client counts, and uptime, without any extra configuration required. Subscribing to /# with mosquitto_sub gives a live view of broker health, and feeding this into the same Grafana dashboard used for device data (see Grafana + InfluxDB on a VPS) means broker-level problems show up in the same place you’re already looking, rather than requiring a separate monitoring setup.

Frequently asked questions

Does Mosquitto support MQTT 5?

Yes, recent Mosquitto versions support MQTT 5 alongside the older 3.1.1 protocol version, and most modern clients negotiate this automatically without extra configuration.

What’s a “retained message” and do I need to think about it?

A retained message is the last value published to a topic, stored by the broker and delivered immediately to any new subscriber, useful for “what’s the current state” without waiting for the next update. Worth knowing about, not something you need to configure specially to use.

Can I run Mosquitto in Docker instead of installing it directly?

Yes, and it’s a common pattern, especially if you’re already running other services in Docker on the same VPS. See Docker on a VPS for the general approach.

How many devices can a small VPS realistically handle?

Mosquitto is genuinely lightweight: thousands of concurrent connections and messages on hardware as modest as 1 vCPU and 1GB of RAM is realistic for typical home or small business telemetry volumes. You’ll outgrow other parts of the stack (database storage, dashboard rendering) long before MQTT itself becomes the bottleneck.

Should every device have its own username and password?

For anything beyond a handful of trusted devices, yes. Per-device credentials, paired with the ACL file restricting each device to its own topic tree, mean a single compromised or stolen device only exposes that device’s own data and permissions, not the entire broker.

Can I see message history, or does Mosquitto only show live traffic?

Mosquitto itself doesn’t store history beyond retained messages; it’s a message bus, not a database. For historical data, something downstream needs to subscribe and store it, which is exactly the role InfluxDB plays in the Grafana-based dashboard guide linked above.

Quality of Service levels, explained simply

MQTT defines three QoS levels, and choosing the right one per topic is worth understanding rather than leaving at the default everywhere. QoS 0 (“at most once”) fires and forgets, the fastest option, fine for frequent sensor readings where an occasional missed value doesn’t matter, since another one is coming shortly anyway. QoS 1 (“at least once”) guarantees delivery but can occasionally deliver a message more than once, suitable for anything where missing a message matters more than an occasional duplicate (an alert, for example). QoS 2 (“exactly once”) guarantees both delivery and no duplicates, at the cost of more network overhead, reserved for cases where a duplicate would genuinely cause a problem (a billing or counting system, for instance). For most home and small business telemetry on this site, QoS 0 or 1 covers the overwhelming majority of real use cases.

What happens to messages published while a subscriber is offline?

By default, they’re simply missed, unless the subscriber uses a persistent session with an appropriate Quality of Service level (QoS 1 or 2), in which case the broker queues messages for delivery once the subscriber reconnects. This is configured per-client in most MQTT libraries rather than on the broker itself.

Can I run more than one MQTT broker for redundancy?

Mosquitto supports bridging, where two brokers share messages between them, which can provide a basic redundancy pattern. For most projects on this site, a single broker on a well-hardened VPS with reasonable uptime is sufficient, and the added complexity of broker bridging is rarely worth it below genuinely critical-infrastructure scale.

Is there a limit to how many topics I can create?

No practical limit imposed by Mosquitto itself; topics are created implicitly the moment something publishes to them, with no separate registration step required. The practical limit is organisational, keeping a sensible naming structure, rather than technical.

What’s a sensible topic naming structure to start with?

A hierarchical pattern such as location/device-type/device-id/measurement (for example home/sensor/kitchen-temp/value) scales cleanly as a setup grows, and supports useful wildcard subscriptions later (home/sensor/# to see every sensor at once) without needing to redesign the structure retroactively.

Does Mosquitto need a powerful CPU for TLS encryption specifically?

No, TLS overhead on a broker handling typical IoT message volumes is negligible on even the smallest VPS plans covered in this guide; the computational cost of encrypting small, infrequent messages is far lower than people often assume from TLS’s reputation in other, higher-throughput contexts.