Mochi MQTT Broker on Ubuntu 24.04 on Azure User Guide
Overview
Mochi MQTT is a fully featured, fully self contained MQTT broker written in Go. It speaks MQTT v5 and v3.1.1 and is a popular embeddable broker for Internet of Things fleets, telemetry pipelines and application message buses.
Upstream ships Mochi as a Go library, and its stock example server runs with an allow-all authentication hook and a world-readable HTTP statistics dashboard. The cloudimg image ships something different: a thin, secure-by-default broker built from the pinned Mochi source (v2.7.9). It requires a per-VM username and password that are generated on first boot, it rejects anonymous and wrong-credential connections, and it exposes only the authenticated MQTT listener on TCP 1883 — there is no WebSocket listener and no world-open stats dashboard.
What is included:
- Mochi MQTT v2.7.9, built from source into a single static binary at
/usr/local/bin/mochi-mqtt-server - MQTT v5 and v3.1.1 support on TCP port 1883
- A per-VM
cloudimgbroker username and password generated at first boot (openssl rand) - Anonymous and wrong-credential connections rejected at connect (default-deny auth ledger)
mosquitto-clients(mosquitto_pub,mosquitto_sub) for testing and scripting- The broker running as an unprivileged
mochisystem user under systemd hardening - Opt-in TLS on port 8883
- 24/7 cloudimg support
Prerequisites
- An Azure VM launched from the cloudimg Mochi MQTT image. A
Standard_B2sis ample. - A Network Security Group that allows inbound TCP 22 (SSH management) and TCP 1883 (MQTT) from your own device/IoT networks. Restrict the source ranges — although the broker is authenticated, you should not expose it to the whole internet. For production, prefer TLS on TCP 8883 (see Step 6).
- An SSH session to the VM as
azureuser.
Step 1: Verify the broker is running
The broker starts automatically once first boot has generated the per-VM credentials.
systemctl is-active mochi-mqtt.service
systemctl status mochi-mqtt.service --no-pager | head -n 7
ss -tln | grep ':1883'
file /usr/local/bin/mochi-mqtt-server | head -n 1
You should see the service active (running) and a listener bound to *:1883.

Step 2: Read the per-VM credentials
The broker username and password are generated uniquely on each VM's first boot and written to a root-only file. Two VMs launched from this image never share a password.
sudo cat /stage/scripts/mochi-mqtt-credentials.log
The MOCHI_MQTT_USER and MOCHI_MQTT_PASSWORD lines are the credentials your MQTT clients use.

Step 3: Confirm it is secure by default
Prove that the broker refuses unauthenticated and wrong-password clients. Neither of the commands below carries the correct credential, so both must be rejected.
OUT_ANON=$(mosquitto_pub -h localhost -t demo -m hi 2>&1 || true)
case "$OUT_ANON" in
*[Rr]efused*|*"not authori"*) echo 'anonymous connection was correctly rejected' ;;
*) echo 'unexpected: anonymous connection was accepted' ;;
esac
OUT_BAD=$(mosquitto_pub -h localhost -u cloudimg -P wrong-password -t demo -m hi 2>&1 || true)
case "$OUT_BAD" in
*[Rr]efused*|*"not authori"*) echo 'wrong-credential connection was correctly rejected' ;;
*) echo 'unexpected: wrong-credential connection was accepted' ;;
esac
Both lines report a correct rejection. The broker ships with no working default credential of any kind.
Step 4: Authenticated publish/subscribe round-trip
Read the per-VM password into a shell variable, subscribe to a topic tree, and publish to it. The subscriber receives every matching message.
PASS=$(sudo grep '^MOCHI_MQTT_PASSWORD=' /stage/scripts/mochi-mqtt-credentials.log | cut -d= -f2-)
# Subscribe in the background (bounded), publish three messages, then show what arrived:
timeout 6 mosquitto_sub -h localhost -u cloudimg -P "$PASS" -t 'sensors/#' -v -q 1 &
sleep 1
mosquitto_pub -h localhost -u cloudimg -P "$PASS" -t sensors/room1/temp -m 21.4 -q 1
mosquitto_pub -h localhost -u cloudimg -P "$PASS" -t sensors/room1/humidity -m 48 -q 1
mosquitto_pub -h localhost -u cloudimg -P "$PASS" -t sensors/door/state -m open -q 1
wait
In normal use you would run the subscriber in one terminal and publish from another; the topic tree sensors/# matches every topic under sensors/.

Step 5: Connect from a remote MQTT client
From another machine, connect to the VM's public IP (shown on the VM's Overview page in the Azure portal) on port 1883 with the per-VM credentials. Make sure your NSG allows inbound TCP 1883 from that machine's address.
$ mosquitto_sub -h YOUR-VM-PUBLIC-IP -p 1883 -u cloudimg -P 'YOUR-PASSWORD' -t 'sensors/#' -v
$ mosquitto_pub -h YOUR-VM-PUBLIC-IP -p 1883 -u cloudimg -P 'YOUR-PASSWORD' -t sensors/room1/temp -m 21.4
Any standard MQTT v5 or v3.1.1 client library (Paho, MQTT.js, and others) connects the same way — host, port 1883, username cloudimg, and the per-VM password.
Step 6: Enable TLS (optional, recommended for production)
The broker supports TLS on a separate port. Provide a certificate and key and point the broker at them, then open TCP 8883 in your NSG instead of 1883. The example below uses a self-signed certificate; use a CA-issued certificate for production.
# Generate a self-signed certificate (replace the CN with your DNS name):
$ sudo openssl req -x509 -newkey rsa:2048 -nodes -days 825 \
-keyout /etc/mochi-mqtt/tls.key -out /etc/mochi-mqtt/tls.crt \
-subj "/CN=your-broker.example.com"
$ sudo chown mochi:mochi /etc/mochi-mqtt/tls.key /etc/mochi-mqtt/tls.crt
$ sudo chmod 0640 /etc/mochi-mqtt/tls.key
# Point the broker at the cert/key and the TLS port, then restart:
$ sudo tee -a /etc/mochi-mqtt/mochi.env >/dev/null <<'EOF'
MOCHI_MQTT_TCP_ADDR=:8883
MOCHI_MQTT_TLS_CERT=/etc/mochi-mqtt/tls.crt
MOCHI_MQTT_TLS_KEY=/etc/mochi-mqtt/tls.key
EOF
$ sudo systemctl restart mochi-mqtt.service
# Clients then connect over TLS on 8883:
$ mosquitto_pub -h YOUR-VM-PUBLIC-IP -p 8883 --cafile ca.crt -u cloudimg -P 'YOUR-PASSWORD' -t demo -m hi
Step 7: Components and file layout
ls -l /usr/local/bin/mochi-mqtt-server
systemctl is-enabled mochi-mqtt.service
id mochi
ls -la /etc/mochi-mqtt/
| Component | Path |
|---|---|
| Broker binary | /usr/local/bin/mochi-mqtt-server (static Go binary, Mochi v2.7.9) |
| Broker credentials (env) | /etc/mochi-mqtt/mochi.env (mode 0600 root:root) |
| Human-readable credentials | /stage/scripts/mochi-mqtt-credentials.log (mode 0600 root:root) |
| systemd service | /etc/systemd/system/mochi-mqtt.service (runs as mochi) |
| First-boot rotation | /etc/systemd/system/mochi-mqtt-firstboot.service |
| MQTT port | TCP 1883 (TLS opt-in on 8883) |

Security notes
- No default credential. The image ships with no working broker credential. Each VM generates its own username/password on first boot, so two VMs from this image differ, and there is nothing to change on a "default" account because none exists.
- Default-deny authentication. The broker authorises exactly one configured user; anonymous connections and any other username/password are rejected at connect.
- Restrict network exposure. Even though the broker is authenticated, limit inbound 1883 (or 8883) to the source ranges that actually need it, and prefer TLS for anything crossing an untrusted network.
- Rotate the password when you need to. Edit
MOCHI_MQTT_PASSWORDin/etc/mochi-mqtt/mochi.env, update/stage/scripts/mochi-mqtt-credentials.log, thensudo systemctl restart mochi-mqtt.service. - Patching. The image tracks Ubuntu security updates via unattended-upgrades.
Support
cloudimg images are backed by 24/7 support. If you have any questions about this deployment, contact the cloudimg support team.