How to Install Eclipse Mosquitto MQTT Broker on Ubuntu 24.04 VPS: IoT Messaging Setup
MQTT is the de facto messaging protocol for IoT, home automation, industrial telemetry, and any system that needs lightweight publish/subscribe communication over unreliable networks. Eclipse Mosquitto is the reference open-source MQTT broker: small, fast, and production-proven on everything from Raspberry Pis to multi-million-device deployments. This guide walks you through installing Mosquitto on an Ubuntu 24.04 VPS with authentication, access control lists, TLS, WebSockets, bridging, and a hardened systemd configuration.
Need a VPS to run Mosquitto? Mosquitto is extremely lightweight — a single broker can handle thousands of concurrent clients on 1 GB of RAM. CloudCore Starter has more than enough capacity for most personal and small-business IoT deployments.
Table of Contents
What is MQTT and Mosquitto?
MQTT (Message Queuing Telemetry Transport) is a lightweight publish/subscribe messaging protocol designed for constrained devices and unreliable networks. Clients connect to a central broker and either publish messages to named topics or subscribe to topics they care about. The broker routes every incoming message to all subscribers of the matching topic. This decoupling — publishers do not know who is listening, and subscribers do not know who is sending — makes MQTT ideal for fan-out telemetry, command-and-control, and device-to-device messaging at scale.
MQTT supports three Quality of Service (QoS) levels (0 = at most once, 1 = at least once, 2 = exactly once), retained messages so late-joining subscribers receive the last known value, Last Will and Testament messages that fire when a client disconnects unexpectedly, and a binary wire format that is dramatically smaller than HTTP or AMQP — a typical sensor message is under 20 bytes.
Eclipse Mosquitto is the Eclipse Foundation's open-source implementation of an MQTT broker, written in C. It supports MQTT versions 3.1, 3.1.1, and 5.0, plus MQTT over WebSockets, TLS encryption, bridging to other brokers, dynamic security plugins, and a full client library. Mosquitto is the broker behind Home Assistant, Node-RED, AWS IoT Greengrass, Azure IoT Edge, and countless industrial deployments. The single binary is under 200 KB, starts in milliseconds, and routinely sustains tens of thousands of concurrent clients per broker on modest hardware.
Why Self-Host an MQTT Broker?
Managed cloud brokers (AWS IoT Core, HiveMQ Cloud, Azure IoT Hub, EMQX Cloud) are convenient, but self-hosting Mosquitto on your own VPS has concrete benefits:
- Flat-rate cost, no per-message charges — AWS IoT charges roughly $1 per million messages plus per-device connectivity fees. A small fleet of 50 devices sending one message per minute easily costs $30–$80 per month. A Mosquitto VPS handles the same load for the cost of the VPS itself, regardless of message volume.
- No device count limits — Cloud brokers price per connected device or per session. Your own broker accepts any number of clients within the hardware's capacity (typically 10,000+ concurrent connections on 1 vCPU).
- Complete data sovereignty — All telemetry, sensor readings, camera triggers, and control commands stay on infrastructure you own. No third party sees the traffic, which matters for industrial control systems, medical devices, and GDPR-regulated deployments.
- Unlimited retained messages and topic depth — Cloud brokers cap retained message size, payload size, and topic hierarchy depth. Mosquitto has no hard limits other than RAM.
- Bridging flexibility — Connect multiple Mosquitto brokers across sites, bridge to cloud providers selectively, or mirror topics for redundancy. Cloud brokers restrict this.
- Local-network low latency — When your broker, Home Assistant, and Node-RED all run on the same VPS or LAN, round-trip time is under 1 ms. Cloud brokers add 30–100 ms per hop.
- Full protocol control — Enable MQTT 5.0 features, custom authentication plugins, and non-standard ports without waiting for a managed provider to add support.
Cost Comparison: Mosquitto VPS vs. Cloud IoT Brokers
| Scenario | AWS IoT Core | HiveMQ Cloud | Self-Hosted Mosquitto (VPS) |
|---|---|---|---|
| 50 devices, 1 msg/min | ~$5–15/mo | $50+/mo | Included in VPS |
| 500 devices, 1 msg/min | ~$30–80/mo | $199+/mo | Included in VPS |
| 5,000 devices, 1 msg/min | ~$200+/mo | Enterprise tier | Included in VPS |
| Data egress | Extra per GB | Included at tier | Included in VPS bandwidth |
| TLS / client certs | Extra config | Yes | Yes (this guide) |
| WebSockets | Yes | Yes | Yes (this guide) |
| Bridging to other brokers | Limited | Limited | Unlimited |
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to your server
- A domain name pointing to the VPS public IP (required for Let's Encrypt TLS in Step 7) — e.g.
mqtt.yourdomain.com - Ports 1883, 8883, 8884, and 80 (for certificate renewal) reachable from the networks your clients will connect from
- At least 1 GB of RAM and 1 vCPU — Mosquitto is extremely lightweight
Recommended Plan: CloudCore Starter>
For a personal or small-business MQTT broker serving a few hundred devices, the CloudCore Starter plan is more than enough:>
- 2 vCPU cores
- 4 GB RAM
- 50 GB NVMe SSD
- Unmetered bandwidth>
This gives you headroom to run Mosquitto alongside Home Assistant, Node-RED, or InfluxDB on the same server if you want a complete self-hosted IoT stack.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages
Start with an up-to-date package index and base system.
sudo apt update && sudo apt upgrade -yIf the kernel was upgraded, reboot before continuing:
sudo rebootStep 2: Install Mosquitto and Clients
Ubuntu 24.04 ships Mosquitto in the default repositories, but the mosquitto-dev PPA provides the latest stable 2.x release with MQTT 5.0 improvements. For most users the distro version is fine — install the broker and the CLI clients together:
sudo apt install -y mosquitto mosquitto-clientsThe mosquitto package installs:
/usr/sbin/mosquitto— the broker binary/etc/mosquitto/mosquitto.conf— the main configuration file/etc/mosquitto/conf.d/— drop-in directory for additional config snippets- A systemd unit at
/lib/systemd/system/mosquitto.service - A
mosquittosystem user/group
mosquitto-clients package provides mosquitto_pub and mosquitto_sub — the two CLI tools you use for manual testing.Confirm the installed version:
mosquitto -h | head -5Expected output:
mosquitto version 2.0.18
mosquitto is an MQTT v5.0/v3.1.1/v3.1 broker.Step 3: Verify the Default Installation
The default install starts Mosquitto listening on 127.0.0.1:1883 with anonymous access allowed locally. Check the service status:
sudo systemctl status mosquittoExpected output:
● mosquitto.service - Mosquitto MQTT Broker
Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 30s ago
Main PID: 1234 (mosquitto)Run a quick smoke test. In one terminal subscribe to a test topic:
mosquitto_sub -h localhost -t test/hello -vIn a second SSH session publish a message:
mosquitto_pub -h localhost -t test/hello -m "hello world"You should see test/hello hello world appear in the first terminal. The broker is alive. Now let's configure it properly.
Step 4: Configure Authentication
Anonymous access is fine for a local smoke test but unacceptable for anything reachable from the internet. Create a password file with one or more users.
Create an empty password file and add a user named iotuser:
sudo mosquitto_passwd -c /etc/mosquitto/passwd iotuserYou will be prompted to enter and confirm a password. The -c flag creates the file (and overwrites any existing one). To add additional users without wiping the file, omit -c:
sudo mosquitto_passwd /etc/mosquitto/passwd homeassistant
sudo mosquitto_passwd /etc/mosquitto/passwd noderedLock down the file:
sudo chown mosquitto:mosquitto /etc/mosquitto/passwd
sudo chmod 640 /etc/mosquitto/passwdInspect the file (passwords are hashed with PBKDF2-SHA512):
sudo cat /etc/mosquitto/passwdExpected output:
iotuser:$7$101$abc...xyz$def...uvw==
homeassistant:$7$101$...
nodered:$7$101$...You will reference this file from mosquitto.conf in Step 6.
Step 5: Set Up Access Control Lists
Authentication proves who a client is; an ACL controls what topics that client can publish to or subscribe to. This is critical on a shared broker — you do not want a compromised sensor to publish fake commands to your door lock topic.
Create /etc/mosquitto/acl:
sudo tee /etc/mosquitto/acl > /dev/null <<'EOF'
===== Default: no access unless explicitly granted =====
----- iotuser: full read/write under sensors/ and devices/ -----
user iotuser
topic readwrite sensors/#
topic readwrite devices/#
topic read $SYS/#----- homeassistant: full access under home/ and homeassistant/ -----
user homeassistant
topic readwrite home/#
topic readwrite homeassistant/#
topic read sensors/#
topic read $SYS/#----- nodered: read everything, publish only to automation/ -----
user nodered
topic read #
topic write automation/#----- Pattern ACLs: each client gets a private namespace by username -----
pattern readwrite clients/%u/#
EOFSet permissions:
sudo chown mosquitto:mosquitto /etc/mosquitto/acl
sudo chmod 640 /etc/mosquitto/aclKey syntax:
user <name>— following rules apply to that username until the nextuserlinetopic readwrite <pattern>— grant publish + subscribe access (read= subscribe only,write= publish only)#— multi-level wildcard;+— single-level wildcardpattern— template rules;%uexpands to the connecting username,%cto the client ID.clients/%u/#gives every user a private subtree$SYS/#— Mosquitto's internal telemetry topics (broker stats, uptime, connected clients)
Step 6: Configure Listeners
Now wire the password file and ACL into Mosquitto, and add three listeners: plain MQTT on localhost:1883 for local apps, TLS-encrypted MQTT on :8883 for internet clients, and WebSockets on :8884 for browser-based clients.
Replace the main config with a clean, annotated version:
sudo tee /etc/mosquitto/mosquitto.conf > /dev/null <<'EOF'
========== Global settings ==========
pid_file /run/mosquitto/mosquitto.pidpersistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information
connection_messages true
log_timestamp true
Security defaults
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/aclLoad any drop-in configs
include_dir /etc/mosquitto/conf.d========== Listener 1: plain MQTT, localhost only ==========
listener 1883 127.0.0.1
protocol mqtt========== Listener 2: MQTT over TLS (public) ==========
listener 8883
protocol mqtt
cafile /etc/letsencrypt/live/mqtt.yourdomain.com/chain.pem
certfile /etc/letsencrypt/live/mqtt.yourdomain.com/fullchain.pem
keyfile /etc/letsencrypt/live/mqtt.yourdomain.com/privkey.pem
tls_version tlsv1.2========== Listener 3: MQTT over WebSockets (public, TLS) ==========
listener 8884
protocol websockets
cafile /etc/letsencrypt/live/mqtt.yourdomain.com/chain.pem
certfile /etc/letsencrypt/live/mqtt.yourdomain.com/fullchain.pem
keyfile /etc/letsencrypt/live/mqtt.yourdomain.com/privkey.pem
tls_version tlsv1.2
EOFReplace mqtt.yourdomain.com throughout with your actual hostname. We will generate the certificates in the next step.
Key directives explained:
allow_anonymous false— Every client must authenticate. Anonymous connects are rejected.listener 1883 127.0.0.1— Plain MQTT on port 1883, bound to localhost only. Perfect for apps running on the same VPS (Home Assistant, Node-RED, custom scripts) that do not need the overhead of TLS.listener 8883(no bind address) — TLS MQTT on the standard MQTT-over-TLS port, listening on all interfaces.listener 8884— MQTT over WebSockets, the standard for browser clients using libraries like MQTT.js or Paho JavaScript.protocol websockets— Must be set explicitly; without it the port would speak raw MQTT.tls_version tlsv1.2— Minimum TLS version. Mosquitto 2.x supports 1.3 automatically if OpenSSL does; setting 1.2 as the floor ensures compatibility with ESP8266/ESP32 devices that lack 1.3 support.
Step 7: Obtain Let's Encrypt TLS Certificates
Install Certbot and request a certificate for your MQTT hostname. Because Mosquitto does not serve HTTP, use Certbot's standalone mode on port 80.
sudo apt install -y certbotTemporarily free port 80 (skip this if no web server is running):
sudo systemctl stop nginx 2>/dev/null || true
sudo systemctl stop apache2 2>/dev/null || trueRequest the certificate:
sudo certbot certonly --standalone \
-d mqtt.yourdomain.com \
--agree-tos \
--email [email protected] \
--non-interactiveExpected output:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/mqtt.yourdomain.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/mqtt.yourdomain.com/privkey.pem
This certificate expires on 2026-07-15.Grant Mosquitto Access to the Certificates
Let's Encrypt stores private keys with restrictive permissions. Mosquitto runs as the mosquitto user and cannot read them by default. Add mosquitto to the ssl-cert group and apply group-readable permissions to the live/ and archive/ directories:
sudo usermod -aG ssl-cert mosquitto
sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive
sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archiveAuto-Reload Mosquitto on Certificate Renewal
Certbot renews automatically via a systemd timer, but Mosquitto will not pick up the new certificate until it is reloaded. Add a deploy hook:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/mosquitto-reload.sh > /dev/null <<'EOF'
#!/bin/bash
systemctl reload mosquitto
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-reload.shNow restart Mosquitto to pick up the full config:
sudo systemctl restart mosquitto
sudo systemctl status mosquittoVerify all three listeners are bound:
sudo ss -tlnp | grep mosquittoExpected output:
LISTEN 0 100 127.0.0.1:1883 0.0.0.0:* users:(("mosquitto",pid=1234,fd=5))
LISTEN 0 100 0.0.0.0:8883 0.0.0.0:* users:(("mosquitto",pid=1234,fd=6))
LISTEN 0 100 0.0.0.0:8884 0.0.0.0:* users:(("mosquitto",pid=1234,fd=7))Step 8: Enable Persistence and Logging
Persistence was already enabled in the main config (persistence true). This means retained messages, queued QoS 1/2 messages for offline clients, and subscription state survive broker restarts — stored in /var/lib/mosquitto/mosquitto.db.
Fine-tune persistence behaviour with a drop-in snippet:
sudo tee /etc/mosquitto/conf.d/persistence.conf > /dev/null <<'EOF'
Write the persistence DB to disk every 30 minutes or every 1000 changes
autosave_interval 1800
autosave_on_changes falseKeep retained messages and offline client state for 14 days
persistent_client_expiration 14dMax message size (256 KB should cover most IoT payloads; raise for firmware OTA)
message_size_limit 262144Per-client inflight + queued message limits (prevent memory exhaustion)
max_inflight_messages 40
max_queued_messages 1000
EOFLogs already go to /var/log/mosquitto/mosquitto.log. Ubuntu's default logrotate config at /etc/logrotate.d/mosquitto handles weekly rotation with compression, so no further action is needed.
Reload the config:
sudo systemctl reload mosquittoCheck that the persistence DB exists after a few minutes of broker activity:
sudo ls -lh /var/lib/mosquitto/Expected output:
-rw------- 1 mosquitto mosquitto 4.0K Apr 16 10:15 mosquitto.dbStep 9: Configure the Firewall with UFW
Open only the ports you actually serve. Port 1883 stays closed at the firewall level — it is already bound to 127.0.0.1 only, but defense-in-depth is cheap.
sudo ufw allow OpenSSH
sudo ufw allow 8883/tcp comment 'MQTT over TLS'
sudo ufw allow 8884/tcp comment 'MQTT over WebSockets'
sudo ufw allow 80/tcp comment 'Certbot HTTP challenge'
sudo ufw --force enable
sudo ufw status verboseExpected output:
Status: active
To Action From -- ------ ---- 22/tcp (OpenSSH) ALLOW IN Anywhere 8883/tcp ALLOW IN Anywhere # MQTT over TLS 8884/tcp ALLOW IN Anywhere # MQTT over WebSockets 80/tcp ALLOW IN Anywhere # Certbot HTTP challenge
If you know every client's static IP, tighten further:
sudo ufw delete allow 8883/tcp
sudo ufw allow from 203.0.113.10 to any port 8883 proto tcp
sudo ufw allow from 198.51.100.0/24 to any port 8883 proto tcpStep 10: Test Pub/Sub End to End
Validate every listener with the CLI clients.
Local plain MQTT (port 1883)
# Subscriber (terminal 1)
mosquitto_sub -h localhost -p 1883 -u iotuser -P 'yourpassword' -t 'sensors/#' -vPublisher (terminal 2)
mosquitto_pub -h localhost -p 1883 -u iotuser -P 'yourpassword' \
-t 'sensors/livingroom/temp' -m '22.4'You should see sensors/livingroom/temp 22.4 on the subscriber.
TLS MQTT (port 8883)
From any client with network access to your VPS:
mosquitto_sub -h mqtt.yourdomain.com -p 8883 \
-u iotuser -P 'yourpassword' \
--capath /etc/ssl/certs/ \
-t 'sensors/#' -vmosquitto_pub -h mqtt.yourdomain.com -p 8883 \
-u iotuser -P 'yourpassword' \
--capath /etc/ssl/certs/ \
-t 'sensors/outdoor/temp' -m '15.1'The --capath /etc/ssl/certs/ tells the client to validate the server certificate against the system CA bundle, which includes the Let's Encrypt root.
WebSockets (port 8884) from a browser
Use mqttx.app or the HiveMQ WebSocket client with these settings:
- Host:
mqtt.yourdomain.com - Port:
8884 - Path:
/mqtt(or blank) - TLS: enabled (wss://)
- Username / password: one of the accounts you created
sensors/# from the browser and confirm messages flow both directions.ACL enforcement test
Attempt a write that the ACL forbids:
mosquitto_pub -h localhost -u nodered -P 'yourpassword' \
-t 'sensors/shouldfail' -m 'nope'Check the log:
sudo tail -20 /var/log/mosquitto/mosquitto.logExpected output:
Denied PUBLISH from nodered (u'nodered', t'sensors/shouldfail')Good — the ACL is working.
Step 11: Set Up Broker Bridging
A bridge is a persistent MQTT connection from your broker to another broker that mirrors selected topics. Use cases include mirroring a remote site's sensors to a central broker, selectively forwarding telemetry to a cloud broker (AWS IoT, HiveMQ Cloud) while keeping control traffic local, and setting up active/active redundancy.
Create a bridge config snippet:
sudo tee /etc/mosquitto/conf.d/bridge.conf > /dev/null <<'EOF'
===== Bridge to a remote broker =====
connection bridge-to-central
address central.example.com:8883
bridge_protocol_version mqttv50Authentication on the remote side
remote_username bridgeuser
remote_password changemeTLS
bridge_cafile /etc/ssl/certs/ca-certificates.crt
bridge_insecure falseTopics to forward:
topic <pattern> <direction> <qos> <local-prefix> <remote-prefix>
direction: out = publish local -> remote
in = subscribe on remote, publish locally
both = bidirectional
topic sensors/# out 1 "" siteA/
topic commands/# in 1 siteA/ ""Clean session / persistence
cleansession false
try_private true
notifications true
start_type automatic
restart_timeout 10 30
EOFExplanation of the topic mapping:
topic sensors/# out 1 "" siteA/— Every message published locally undersensors/is forwarded to the remote broker with the prefixsiteA/(sosensors/livingroom/tempbecomessiteA/sensors/livingroom/tempremotely). This is how multi-site aggregation works — each site publishes under its own prefix on the central broker.topic commands/# in 1 siteA/ ""— The bridge subscribes tositeA/commands/#on the remote broker; incoming messages are re-published locally undercommands/#, stripping thesiteA/prefix.
sudo systemctl reload mosquitto
sudo journalctl -u mosquitto -n 20 --no-pagerExpected output:
Connecting bridge (step 1) bridge-to-central (central.example.com:8883)
Bridge bridge-to-central sending CONNECTIf the remote side rejects authentication, the log will show Connection Refused: not authorised.
Bridges survive network outages automatically — restart_timeout 10 30 means Mosquitto waits 10 seconds before the first reconnect attempt and backs off to 30 seconds.
Step 12: Harden the systemd Service
The stock systemd unit is fine, but a few hardening directives reduce the blast radius if Mosquitto is ever compromised. Create a drop-in override instead of editing the distro unit:
sudo systemctl edit mosquittoPaste the following into the editor:
[Service]
Restart automatically on crash
Restart=on-failure
RestartSec=5Filesystem and privilege hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictNamespaces=trueAllow writes only to the directories Mosquitto actually needs
ReadWritePaths=/var/lib/mosquitto /var/log/mosquitto /run/mosquittoResource limits
LimitNOFILE=65536
TasksMax=4096Save and exit. Apply the changes:
sudo systemctl daemon-reload
sudo systemctl restart mosquitto
sudo systemctl status mosquittoVerify the process can still write to its directories:
sudo ls -la /var/lib/mosquitto/ /var/log/mosquitto/The LimitNOFILE=65536 bump is important at scale — each connected MQTT client consumes one file descriptor, and the default of 1024 caps you around 1000 concurrent clients.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Connection Refused: not authorised | Wrong username/password or ACL blocks the topic | Re-check mosquitto_passwd entry; tail /var/log/mosquitto/mosquitto.log for the denied topic pattern |
Unable to load certificate file on startup | mosquitto user cannot read Let's Encrypt keys | Re-run the chgrp -R ssl-cert + chmod g+rX commands from Step 7 |
| WebSockets client hangs on connect | Port 8884 blocked or protocol websockets line missing | sudo ufw status, check ss -tlnp \</td><td>grep 8884<code>, confirm </code>protocol websockets appears under the listener line |
Error: Address already in use | Another broker or stale process holds the port | sudo lsof -i :1883; kill the conflicting process |
Bridge stays in step 1 forever | TLS CA file missing, hostname wrong, or remote blocks IP | Set bridge_insecure true temporarily to isolate TLS vs auth; check remote broker logs |
| Retained messages lost after restart | persistence true missing or /var/lib/mosquitto not writable | Confirm persistence true in mosquitto.conf; check ls -la /var/lib/mosquitto/ |
Mosquitto dies under load with Too many open files | Default LimitNOFILE too low | Apply the systemd override from Step 12 (LimitNOFILE=65536) |
| ESP32/ESP8266 cannot connect over TLS | Device clock wrong or root CA missing from firmware | Sync NTP on the device; embed the ISRG Root X1 certificate in firmware |
Useful diagnostic commands
# Live broker logs
sudo journalctl -u mosquitto -fMosquitto-specific log file
sudo tail -f /var/log/mosquitto/mosquitto.logBuilt-in broker telemetry (requires subscribe access to $SYS)
mosquitto_sub -h localhost -u iotuser -P 'yourpassword' -t '$SYS/#' -vPort 8883 TLS handshake test
openssl s_client -connect mqtt.yourdomain.com:8883 -servername mqtt.yourdomain.com </dev/nullThe $SYS/# tree exposes broker stats in real time: connected clients, messages received, bytes sent, uptime, subscriptions. Feed these into Prometheus via mosquitto_exporter for dashboards.
FAQ
How many MQTT clients can a single Mosquitto broker handle?
A single Mosquitto 2.x instance on a 2 vCPU / 4 GB VPS comfortably handles 10,000–50,000 concurrent clients at low-to-moderate message rates (1 message per client per minute). Raw throughput exceeds 100,000 messages per second on modest hardware because MQTT's binary framing is cheap and Mosquitto's event loop is single-threaded but highly efficient. The practical ceiling is usually file descriptors (raise LimitNOFILE per Step 12) and RAM for retained messages, not CPU. For six-figure client counts or multi-million messages per second, consider EMQX or VerneMQ, which scale across cores.
Should I use plain MQTT (1883) or TLS (8883)?
Anything that traverses the internet must use TLS. The overhead on modern hardware is negligible — Mosquitto adds under 1 ms of latency per message, and AES-GCM on even a low-end ARM Cortex-M33 MCU easily keeps up with typical IoT message rates. Use the plain 1883 listener only for localhost-bound inter-process communication between apps on the same VPS. If you need plain MQTT across a LAN, bind it to the internal interface only (e.g. listener 1883 10.0.0.1) and restrict with UFW.
Do I need client certificates as well as username/password?
Mutual TLS (mTLS) with client certificates adds a second factor: a device must possess a valid private key and know its password. It is excellent for fleets where you control provisioning — each device gets a unique certificate baked in at manufacture time, and revoking a compromised device is a matter of removing one line from a CRL. For hobby setups, username/password over TLS is sufficient. To enable mTLS, add require_certificate true and use_identity_as_username true to a listener, then issue certificates signed by your own CA. The Mosquitto TLS documentation at mosquitto.org/documentation/ has a complete walkthrough.
How do I monitor broker health?
Three complementary approaches. First, the built-in $SYS/# topic tree exposes dozens of metrics — $SYS/broker/clients/connected, $SYS/broker/messages/received, $SYS/broker/load/messages/sent/1min, and so on. Subscribe to them with mosquitto_sub or feed them into any MQTT-aware dashboard. Second, install the Prometheus mosquitto_exporter to scrape $SYS into Prometheus and visualize in Grafana. Third, use Uptime Kuma's MQTT probe type to alert if the broker stops responding or if a heartbeat topic goes stale. For production deployments, all three together give you deep visibility.
Can Mosquitto store messages for offline clients?
Yes, with QoS 1 or QoS 2 and clean_session=false set by the client on connect. When a subscriber with a persistent session disconnects, Mosquitto queues matching messages up to max_queued_messages per client (1000 by default in our config). On reconnect, the broker replays the queue. This is how battery-powered devices that wake up briefly every hour receive commands issued while they were asleep. Retained messages are different — they store the last value on a topic and deliver it once to each new subscriber regardless of sessions.
How do I migrate from AWS IoT Core or HiveMQ Cloud?
Most client libraries (Paho, MQTT.js, ESP-MQTT) only need a hostname, port, username, and password change to switch brokers. The wire protocol is identical. The main migration work is: recreate users and ACLs (Step 4–5), reissue any client certificates if you used mTLS, update device firmware with the new hostname, and rebuild any cloud-broker-specific features you depended on (AWS IoT Rules, shadows, jobs). For AWS IoT Device Shadow equivalents on Mosquitto, pair it with Node-RED or a small custom service that maintains last-known-state on retained topics.
Next Steps
Now that Mosquitto is running, here are the natural additions for a complete self-hosted IoT stack:
- Install Home Assistant — Hook Home Assistant into your Mosquitto broker for the most popular open-source home automation platform. The MQTT integration auto-discovers devices published with the Home Assistant discovery schema. See How to Install Home Assistant on Ubuntu.
- Install Node-RED — Flow-based visual programming for IoT. Drag MQTT in/out nodes onto a canvas and wire sensor data into dashboards, databases, HTTP calls, and automations without writing code. See How to Install Node-RED on Ubuntu.
- Install n8n — A more general-purpose workflow automation platform with a first-class MQTT trigger node. Ideal for integrating IoT events with business systems (Slack alerts, Google Sheets logging, CRM updates). See How to Install n8n on Ubuntu.
- Store telemetry in InfluxDB or TimescaleDB — Mosquitto is a message bus, not a database. Pair it with a time-series database and Telegraf's MQTT consumer input to persist every sensor reading for dashboards and historical analysis.
- Graph live data with Grafana — Connect Grafana to InfluxDB (or directly to MQTT via the MQTT data source plugin) for live dashboards of every topic.
- Read the official Mosquitto documentation — mosquitto.org/documentation/ covers advanced topics including the dynamic security plugin, custom auth plugins, cluster-style deployments, and MQTT 5 features like shared subscriptions and topic aliases.
Need a VPS for your MQTT broker?>
CloudCore Starter gives you a 2 vCPU / 4 GB RAM / 50 GB NVMe VPS that comfortably runs Mosquitto plus Home Assistant, Node-RED, and InfluxDB on the same machine — the full self-hosted IoT stack on one server.>
- 2 vCPU cores
- 4 GB RAM
- 50 GB NVMe SSD
- Unmetered bandwidth
- Root SSH access, Ubuntu 24.04>
Deploy your IoT VPS now