MQTT is the lightweight publish/subscribe protocol behind most IoT and home-automation setups. Devices publish messages to topics (home/livingroom/temperature), and other clients subscribe to the topics they care about. A broker sits in the middle and routes the messages. Eclipse Mosquitto is the most widely used open-source broker: small, fast and easy to run on a VPS, giving your devices a stable, reachable broker anywhere.

Install

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

Docker alternative: eclipse-mosquitto:2 with a mounted mosquitto.conf, data and log directories.

Since Mosquitto 2.0, the default config only allows local connections without authentication. You must configure listeners explicitly for remote access, which is safe by default.

Basic configuration with authentication

/etc/mosquitto/conf.d/default.conf:

per_listener_settings false
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl

# plain MQTT only on localhost (local apps)
listener 1883 127.0.0.1

# TLS for remote clients
listener 8883
certfile /etc/mosquitto/certs/fullchain.pem
keyfile /etc/mosquitto/certs/privkey.pem

Create users:

sudo mosquitto_passwd -c /etc/mosquitto/passwd sensor1      # -c only for the first user
sudo mosquitto_passwd /etc/mosquitto/passwd nodered
sudo chown mosquitto: /etc/mosquitto/passwd && sudo chmod 640 /etc/mosquitto/passwd

ACLs: who can read and write which topics

/etc/mosquitto/acl:

# sensors may only publish their own data
user sensor1
topic write sensors/sensor1/#

# the automation service reads all sensors and writes commands
user nodered
topic read sensors/#
topic write commands/#

# pattern: each client may use its own subtree (%u = username)
pattern readwrite devices/%u/#

Least privilege matters. A compromised device shouldn’t be able to control everything.

TLS with Let’s Encrypt

Get a certificate for mqtt.example.com (standalone, or via your web server):

sudo certbot certonly --standalone -d mqtt.example.com
sudo mkdir -p /etc/mosquitto/certs
sudo cp /etc/letsencrypt/live/mqtt.example.com/{fullchain,privkey}.pem /etc/mosquitto/certs/
sudo chown mosquitto: /etc/mosquitto/certs/*.pem && sudo chmod 600 /etc/mosquitto/certs/privkey.pem

Mosquitto can’t read Certbot’s root-only files directly. Use a deploy hook to copy the files and reload Mosquitto on renewal:

sudo tee /etc/letsencrypt/renewal-hooks/deploy/mosquitto.sh >/dev/null <<'EOF'
#!/bin/sh
cp /etc/letsencrypt/live/mqtt.example.com/fullchain.pem /etc/letsencrypt/live/mqtt.example.com/privkey.pem /etc/mosquitto/certs/
chown mosquitto: /etc/mosquitto/certs/*.pem
systemctl reload mosquitto
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto.sh

See Let’s Encrypt.

Restart and open the firewall:

sudo systemctl restart mosquitto
sudo ufw allow 8883/tcp

Don’t expose unencrypted 1883 to the internet, since credentials would travel in plain text.

Test

# subscribe (terminal 1)
mosquitto_sub -h mqtt.example.com -p 8883 -u nodered -P 'secret' -t 'sensors/#' -v --capath /etc/ssl/certs
# publish (terminal 2)
mosquitto_pub -h mqtt.example.com -p 8883 -u sensor1 -P 'secret' -t 'sensors/sensor1/temp' -m '21.5' --capath /etc/ssl/certs

Check the logs for authentication or ACL denials:

sudo journalctl -u mosquitto -f

WebSockets (for browser dashboards)

listener 9001
protocol websockets
certfile /etc/mosquitto/certs/fullchain.pem
keyfile /etc/mosquitto/certs/privkey.pem

Browser clients (MQTT.js) connect to wss://mqtt.example.com:9001. Alternatively, proxy WebSockets through Nginx on 443. See Nginx WebSocket proxy.

Useful MQTT features

  • QoS 0/1/2: delivery guarantees, from fire-and-forget to exactly-once.
  • Retained messages: the broker keeps the last value per topic for new subscribers (-r with mosquitto_pub). Good for device states.
  • Last Will: the broker publishes a message when a client disconnects unexpectedly (“device offline”).
  • Bridging: connect a local broker (at home) to the VPS broker, so local devices stay local and selected topics sync to the cloud. Configure connection blocks in the local mosquitto.conf.

Integrations

Quick reference

Task Command / setting
Add user mosquitto_passwd /etc/mosquitto/passwd USER
No anonymous allow_anonymous false
TLS listener listener 8883 + certfile/keyfile
Subscribe mosquitto_sub -t 'topic/#' -v
Publish mosquitto_pub -t topic -m msg
Logs journalctl -u mosquitto

Related: time-series databases and UFW firewall.

  • IoT
  • Self-Hosting
  • Networking