Install on Debian / Ubuntu

sudo apt install mosquitto mosquitto-clients
sudo systemctl status mosquitto

By default, Mosquitto on a fresh install listens on 127.0.0.1:1883 only and accepts no authentication. Don't expose that to the LAN until you've added at least password auth.

Add a user and password

sudo mosquitto_passwd -c /etc/mosquitto/passwd amir
# Enter and confirm a password

# Add a second user without -c so the first isn't overwritten
sudo mosquitto_passwd /etc/mosquitto/passwd home-assistant

Configuration

Drop in /etc/mosquitto/conf.d/local.conf:

per_listener_settings true

listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd

# Persistent storage for retained messages and QoS state
persistence true
persistence_location /var/lib/mosquitto/

# Logging
log_type error
log_type warning
log_type notice
log_dest syslog
sudo systemctl restart mosquitto
sudo systemctl status mosquitto

Test it

Open two terminals. In one, subscribe:

mosquitto_sub -h localhost -u amir -P <password> -t 'test/#' -v

In the other, publish:

mosquitto_pub -h localhost -u amir -P <password> -t 'test/hello' -m 'hi there'

The subscriber prints test/hello hi there. That's the whole MQTT model: clients publish to a topic, other clients subscribe to a topic pattern, the broker routes messages. Topic patterns use + for one segment and # for "everything below this point."

TLS

For anything that crosses the network — or simply to make password-sniffing impossible on the LAN — add TLS. Two paths:

  1. Self-signed for a closed LAN. Generate a CA, sign a server cert, copy the CA to each client.
  2. Step CA (see that tutorial). Get a short-lived cert from your internal CA automatically.

In either case, the listener config gains:

listener 8883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd

cafile   /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile  /etc/mosquitto/certs/server.key

Clients connect to mqtts://broker.lab:8883 and verify the chain. For mutual TLS (clients also present a cert), add require_certificate true and use_identity_as_username true.

ACLs: restrict which topics each user can touch

Add an ACL file:

# /etc/mosquitto/acl
user home-assistant
topic readwrite #

user esp-livingroom
topic readwrite home/livingroom/#
topic read      home/+/temperature

user zigbee2mqtt
topic readwrite zigbee2mqtt/#

Reference it from the config:

acl_file /etc/mosquitto/acl

Restart. Now each device can only publish/subscribe to the topics its role needs — a compromised ESP32 can't, say, listen for or fake commands from the front door lock.

Retained messages, QoS, and Last Will

Three MQTT features that come up constantly:

  • Retained — the broker remembers the last message published to a topic and delivers it to any new subscriber immediately. Used for state ("the light is on") rather than events ("a light-change happened").
  • QoS — 0 (fire-and-forget), 1 (delivered at least once), 2 (delivered exactly once). Cost grows with QoS; for sensor data 0 is fine, for irreversible actions 1 or 2.
  • Last Will and Testament — a message the broker publishes on behalf of a client if that client disconnects ungracefully. Used to publish "offline" on disconnect so other clients know the sensor is dead.
mosquitto_pub -t 'home/livingroom/light' -m 'on' -r -q 1
# -r retained, -q 1 at-least-once

Bridging to a second broker

A bridge is a Mosquitto-to-Mosquitto link that mirrors topics. Useful for connecting two LANs over the internet, or sending a subset of metrics from a home broker to a cloud broker:

# /etc/mosquitto/conf.d/bridge.conf
connection cloud-bridge
address mqtt.example.com:8883
remote_username cloud-user
remote_password <cloud-password>

bridge_cafile /etc/ssl/certs/ca-certificates.crt
bridge_insecure false

topic sensors/# out 1 ""
topic commands/+ in 1 ""

The out direction publishes local messages to the remote; in brings remote messages local. Both ends keep their full topic trees independent.

Integrate with Home Assistant

In Home Assistant: Settings → Devices & Services → Add Integration → MQTT. Enter the broker hostname, port, and the home-assistant credentials created above. Home Assistant's MQTT discovery protocol picks up any device that publishes a configuration message to a special topic prefix — ESPHome devices, Zigbee2MQTT, and many other tools speak it natively.

Integrate with Zigbee2MQTT

Z2M is the most common MQTT-driven smart-home integration after Home Assistant itself. In its configuration.yaml:

mqtt:
  base_topic: zigbee2mqtt
  server: 'mqtt://broker.lab:1883'
  user:     zigbee2mqtt
  password: <password>
homeassistant: true     # publishes discovery messages so HA finds devices

Operational notes

  • Persistence file/var/lib/mosquitto/mosquitto.db holds retained messages and durable subscriptions. Back it up alongside passwd, acl, and the config.
  • Max payload size — default 0 (unlimited). For brokers that face the internet, set message_size_limit to a sane value.
  • Anonymous, exposed to the internet, no auth — the default Mosquitto failure mode on cloud VMs. The "allow_anonymous false" line above is the single most important config in this file.