Stage 3 of 6 · Lab · about 20 minutes
The broker
Mosquitto is the open-source MQTT broker, small enough that the Pi runs it without noticing. Installing it takes one command; making it reachable takes understanding, because its modern defaults are locked down on purpose, and you will hit two walls that every real deployment hits. This page is those walls, and the door through each.
Install: the apt cycle returns
Over SSH on the Pi, the Week 3 ritual, update then install:
you@lastname-pi:~ $ sudo apt update you@lastname-pi:~ $ sudo apt install -y mosquitto mosquitto-clients
Two packages on purpose: mosquitto is the broker itself; mosquitto-clients brings mosquitto_pub and mosquitto_sub, the command-line clients that will be your stethoscope all term, the fastest way to see what is really flowing, independent of any code you wrote.
Mosquitto is a service
Installing did more than copy files: Mosquitto registered as a service (a daemon, in Unix speak), a program the operating system supervises rather than one you launch in a terminal. Week 2's boot chain ended with "the init system starts services"; today you own one. The supervisor is systemd, and systemctl is how you speak to it:
you@lastname-pi:~ $ sudo systemctl enable mosquitto # start at every boot you@lastname-pi:~ $ systemctl status mosquitto # is it running right now? ● mosquitto.service - Mosquitto MQTT Broker Active: active (running) since Mon 2026-11-02 14:52:10 EST; 1min ago
active (running) in the status output is the fact that matters. Two more verbs complete the kit: sudo systemctl restart mosquitto (needed after every configuration change, below) and sudo journalctl -u mosquitto -e to read the service's log when something is wrong, the broker's version of a traceback. Enabled means the broker survives reboots unattended: from today, your Pi serves MQTT the moment power arrives, term-long infrastructure, no hands.
The two walls
Try to connect from any other machine right now and you will fail, by design. Since version 2.0, Mosquitto ships secure-by-default: with no listener configured it binds to loopback only (reachable solely from the Pi itself), and any listener you do open refuses anonymous clients unless told otherwise. Week 3's smallest-attack-surface principle, implemented by the vendor; your job is to open exactly as much as the bench needs, in a configuration file, on the record. Mosquitto reads drop-in files from /etc/mosquitto/conf.d/, so create one:
you@lastname-pi:~ $ sudo nano /etc/mosquitto/conf.d/bench.conf
# bench.conf: open the standard port to the bench LAN, no credentials.
# Scoped decision for a supervised classroom network. See Week 9, pubsub.html#security.
listener 1883
allow_anonymous true
you@lastname-pi:~ $ sudo systemctl restart mosquitto you@lastname-pi:~ $ systemctl status mosquitto # confirm: active (running), not failed
Restart, then re-check status, every time
Configuration is read at start-up only: an edited file does nothing until the restart. And a typo in the file makes the restart fail, taking the broker down entirely, so the status check after every restart is not politeness, it is how you find out. If status shows failed, read sudo journalctl -u mosquitto -e: Mosquitto names the bad line.
The firewall: allow before use
Wall three would have been the firewall, except Week 3 trained you for it: UFW currently allows SSH and nothing else, so MQTT's port is closed to the network no matter what Mosquitto listens on. Same discipline, new port:
you@lastname-pi:~ $ sudo ufw allow 1883 you@lastname-pi:~ $ sudo ufw status verbose To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 1883 ALLOW IN Anywhere
Your Pi's open-port inventory is now two lines long, and you can justify every line, which is the entire Week 3 standard. (Production would also scope who may reach 1883; on the bench LAN, "anywhere" is the LAN.)
Prove it: the loopback test
Before any ESP32 or Python enters the picture, prove the broker with the command-line clients alone, two SSH terminals to the Pi (your Week 3 skills make this cheap):
# Terminal A: subscribe, verbosely (-v prints the topic with each message), and wait you@lastname-pi:~ $ mosquitto_sub -t "bench/test" -v
# Terminal B: publish one message to that topic you@lastname-pi:~ $ mosquitto_pub -t "bench/test" -m "hello bench"
Terminal A prints bench/test hello bench: a complete publish-broker-subscribe round trip, on one machine. Two refinements worth thirty seconds each: re-run terminal A with the filter "bench/#" and publish to bench/anything/else, watching the wildcard theory come true; and keep a mosquitto_sub -t "#" -v habit in your pocket, the firehose that shows everything crossing the broker, which is the single most useful debugging move of the next six weeks. From another machine on the bench network (the clients install on most systems), the same subscribe with -h naming the Pi's address proves network reach; the real cross-machine proof arrives with the node, next page.
The path of a message
You have now built every checkpoint a message must clear. Hold this picture; it is also the troubleshooting map for the whole week:
Checklist for this stage
Check yourself
What exactly did sudo systemctl enable mosquitto change, and what did it not change?
start does) and it did not configure anything about how Mosquitto listens.A classmate's node gets "connection refused" from the Pi. Which wall is that, and why not the firewall?
bench.conf, or Mosquitto not restarted/failed. A firewall block drops packets silently, which shows up as a timeout, not a refusal.Why does editing bench.conf have no effect until a restart, and what must you check right after restarting?
sudo systemctl restart mosquitto, check systemctl status mosquitto: a typo in the file makes the restart fail and the broker stay down, and the status line (plus journalctl -u mosquitto -e) is where that fact surfaces.Defend allow_anonymous true on this Pi in two sentences, using Week 3's language.
bench.conf. The same line in production would be negligence; there, authentication, TLS on 8883 and topic ACLs replace it.Which single command shows every message crossing the broker, and when would you reach for it?
mosquitto_sub -t "#" -v: the firehose. Reach for it whenever publishers and subscribers disagree about what is flowing; it shows the broker's ground truth, including the exact topic strings, where typos hide.