Mosquitto MQTT Raspberry Pi gives you a lightweight, self-hosted message broker for IoT devices, home automation, and sensor networks. Mosquitto runs as a systemd service, handles TLS encryption on port 8883, and enforces topic-level access control via ACL files. This guide covers installation, TLS certificate generation with OpenSSL, password and certificate authentication, ACL configuration for per-user topic access, ESP32 and Node-RED client integration, Fail2Ban brute-force protection, and maintenance.
Last tested: Raspberry Pi OS Bookworm Lite 64-bit | April 20, 2026 | Raspberry Pi 4 Model B (4GB) | Mosquitto 2.0.20 | ESP32 (PubSubClient) + Node-RED 3.1
Key Takeaways
- Mosquitto 2.0 ships with
allow_anonymous falseas the default. This means no client can connect until you configure either a password file or certificate authentication. Anonymous access must be explicitly enabled and should not be used in production. - TLS on port 8883 and ACLs are independent features. TLS encrypts traffic in transit. ACLs control which authenticated users can publish to or subscribe from which topics. You need both for a secure broker. TLS alone does not prevent an authenticated user from accessing every topic.
- The Fail2Ban
failregexpattern must match the exact log format from your Mosquitto version. Test it withfail2ban-regexagainst a sample log line before enabling the jail. A wrong pattern silently fails to ban anything.
Mosquitto MQTT Raspberry Pi: How It Works
Mosquitto is a lightweight MQTT message broker. Clients connect and either publish messages to a topic or subscribe to receive messages from a topic. The broker is the intermediary. It receives published messages and forwards them to all current subscribers on that topic. It does not store messages permanently (unless persistence is configured) and does not process message content.
MQTT uses a publish/subscribe pattern rather than a request/response pattern. A temperature sensor publishes to home/livingroom/temperature every 30 seconds. A home automation controller subscribes to that topic and receives each reading. The sensor and controller never communicate directly. The broker handles all routing. This decoupling makes MQTT well suited to IoT networks where devices come and go independently.
| Port | Protocol | Use |
|---|---|---|
| 1883 | MQTT (plaintext) | LAN only, no TLS. Disable in production. |
| 8883 | MQTT over TLS | All authenticated connections |
| 8884 | MQTT over TLS + client cert | Certificate-authenticated clients only |
| 9001 | MQTT over WebSocket | Browser-based clients, Node-RED |
OS Preparation and Installation
Flash Raspberry Pi OS Bookworm Lite 64-bit using Raspberry Pi Imager. In the advanced settings, set hostname, enable SSH, and configure credentials. After first boot:
sudo apt update && sudo apt full-upgrade -y
Set a static IP. Clients connect to the broker by IP or hostname. A changing address breaks all of them:
sudo nmcli connection modify "Wired connection 1" \
ipv4.method manual \
ipv4.addresses 192.168.1.80/24 \
ipv4.gateway 192.168.1.1 \
ipv4.dns 192.168.1.1
sudo nmcli connection up "Wired connection 1"
Install Mosquitto:
sudo apt install mosquitto mosquitto-clients -y
sudo systemctl enable mosquitto
sudo systemctl status mosquitto
Expected result: systemctl status mosquitto shows the service active. sudo ss -tlnp | grep mosquitto shows Mosquitto listening. On a fresh Bookworm install, Mosquitto 2.0 starts with anonymous access disabled by default. No client can connect until you configure authentication.
Directory layout
| Path | Purpose |
|---|---|
/etc/mosquitto/mosquitto.conf | Main config file |
/etc/mosquitto/conf.d/ | Drop-in config fragments (recommended) |
/etc/mosquitto/passwd | Hashed password file |
/etc/mosquitto/acl | Access control list |
/etc/mosquitto/certs/ | TLS certificates and keys |
/var/log/mosquitto/mosquitto.log | Broker log |
Use include_dir /etc/mosquitto/conf.d/ in the main config and create separate files for listeners, TLS, and ACLs. This keeps the configuration readable as it grows.
TLS Certificate Setup
TLS requires three components: a CA certificate (your root of trust), a server certificate signed by that CA, and the server’s private key. Clients must trust the CA to verify the server certificate.
sudo mkdir -p /etc/mosquitto/certs
cd /etc/mosquitto/certs
# Create the CA key and self-signed certificate (valid 10 years)
sudo openssl genrsa -out ca.key 4096
sudo openssl req -x509 -new -nodes -key ca.key \
-sha256 -days 3650 -out ca.crt \
-subj "/CN=MQTT-CA/O=HomeNet/C=US"
# Create the server key and certificate signing request
sudo openssl genrsa -out server.key 4096
sudo openssl req -new -key server.key -out server.csr \
-subj "/CN=mqtt-pi.local/O=HomeNet/C=US"
# Sign the server certificate with the CA (valid 2 years)
sudo openssl x509 -req -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 730 -sha256
# Set correct permissions
sudo chown -R mosquitto:mosquitto /etc/mosquitto/certs
sudo chmod 640 /etc/mosquitto/certs/*.key
Create /etc/mosquitto/conf.d/tls.conf:
# Disable plaintext listener in production
#listener 1883
# TLS listener
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.3
require_certificate false
sudo systemctl restart mosquitto
# Verify TLS is working
openssl s_client -connect localhost:8883 -CAfile /etc/mosquitto/certs/ca.crt
Expected result: The openssl s_client command returns certificate details and Verify return code: 0 (ok). Any other return code indicates a certificate chain problem. Check that the server cert was signed by the CA and that the file paths in tls.conf are correct.

Authentication: Passwords and Client Certificates
Password file authentication
Create the password file and add users. The -c flag creates a new file. Omit it to add users to an existing file:
# Create the file and add the first user
sudo mosquitto_passwd -c /etc/mosquitto/passwd sensor1
# Add more users (no -c flag)
sudo mosquitto_passwd /etc/mosquitto/passwd actuator1
sudo mosquitto_passwd /etc/mosquitto/passwd admin
sudo chmod 640 /etc/mosquitto/passwd
sudo chown mosquitto:mosquitto /etc/mosquitto/passwd
Create /etc/mosquitto/conf.d/auth.conf:
allow_anonymous false
password_file /etc/mosquitto/passwd
Client certificate authentication
For devices like ESP32 that should authenticate without a password, generate a client certificate signed by the same CA:
cd /etc/mosquitto/certs
sudo openssl genrsa -out client-sensor1.key 4096
sudo openssl req -new -key client-sensor1.key \
-out client-sensor1.csr \
-subj "/CN=sensor1/O=HomeNet/C=US"
sudo openssl x509 -req -in client-sensor1.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client-sensor1.crt -days 730 -sha256
To use the certificate CN as the MQTT username (enabling ACL matching), add to tls.conf:
require_certificate true
use_identity_as_username true
With this configuration, a client presenting a certificate with CN=sensor1 is treated as user sensor1 in the ACL file. No password is required if require_certificate true is set on that listener.
ACL Configuration
ACLs restrict which topics each authenticated user can read, write, or both. Without an ACL file, all authenticated users can access every topic. Create /etc/mosquitto/acl:
# sensor1 can only publish temperature readings
user sensor1
topic write home/livingroom/temperature
topic write home/kitchen/humidity
# actuator1 can only receive control commands
user actuator1
topic read controls/relay1
topic read controls/relay2
# admin has full access to all topics
user admin
topic readwrite #
# Pattern rule: any user can access topics under their own username
pattern readwrite devices/%u/#
The pattern directive applies to all users. %u is replaced by the connecting username at runtime, so user sensor1 automatically gets access to devices/sensor1/# without a separate rule per user.
Add the ACL file to auth.conf:
acl_file /etc/mosquitto/acl
sudo systemctl restart mosquitto
# Test: sensor1 can publish to their allowed topic
mosquitto_pub -h localhost -p 8883 \
--cafile /etc/mosquitto/certs/ca.crt \
-u sensor1 -P sensor1-password \
-t "home/livingroom/temperature" -m "21.5"
# Test: sensor1 cannot subscribe to controls (should fail with Not Authorised)
mosquitto_sub -h localhost -p 8883 \
--cafile /etc/mosquitto/certs/ca.crt \
-u sensor1 -P sensor1-password \
-t "controls/#"
Expected result: The publish succeeds silently. The subscribe returns a Not Authorised error, confirming the ACL is working correctly.
IoT Client Integration
Topic structure best practices
A consistent topic hierarchy makes ACL rules and subscriptions predictable. A practical structure for a home automation network:
home/{room}/{sensor_type} # e.g. home/livingroom/temperature
devices/{device_id}/status # e.g. devices/esp32-01/status
controls/{actuator_id} # e.g. controls/relay1
alerts/{severity}/{source} # e.g. alerts/critical/motion-sensor
Avoid spaces in topic names. Use lowercase and forward slashes as the only separator. The + wildcard matches one level (home/+/temperature matches all rooms); # matches all remaining levels (devices/# matches everything under devices).
ESP32 with PubSubClient
Load the root CA certificate into the ESP32’s flash. The CA cert must match the one used to sign the Mosquitto server certificate. A minimal Arduino sketch:
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
const char* ca_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"...(paste ca.crt contents here)...\n" \
"-----END CERTIFICATE-----\n";
WiFiClientSecure net;
PubSubClient client(net);
void setup() {
net.setCACert(ca_cert);
client.setServer("192.168.1.80", 8883);
client.connect("esp32-sensor1", "sensor1", "sensor1-password");
client.publish("home/livingroom/temperature", "21.5");
}
If the ESP32 runs out of memory with a full cert chain, use fingerprint-based verification (net.setFingerprint()) as a fallback. Fingerprinting verifies the server cert hash rather than the full chain, which uses significantly less RAM.
Node-RED
In Node-RED, add an mqtt in or mqtt out node and configure the broker connection. Under Security, set the username and password. Under TLS, add a TLS configuration pointing to ca.crt. Node-RED uses the same credentials and topic access rules as any other MQTT client. The ACL applies to Node-RED connections too.
Security and Fail2Ban
sudo apt install ufw fail2ban -y
sudo ufw allow OpenSSH
sudo ufw allow 8883/tcp
sudo ufw enable
Enable verbose logging in Mosquitto for Fail2Ban to work. Add to the main mosquitto.conf:
log_dest file /var/log/mosquitto/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information
Create the Fail2Ban filter at /etc/fail2ban/filter.d/mosquitto.conf:
[Definition]
failregex = .*\] Client <HOST>.* CONNECT received.* \(RC=4\)
.*\] Client <HOST>.* CONNECT received.* \(RC=5\)
ignoreregex =
RC=4 is “bad username or password”; RC=5 is “not authorised.” Create the jail at /etc/fail2ban/jail.d/mosquitto.conf:
[mosquitto]
enabled = true
port = 1883,8883
filter = mosquitto
logpath = /var/log/mosquitto/mosquitto.log
maxretry = 5
bantime = 3600
findtime = 300
# Test the filter against a sample log line before enabling
fail2ban-regex /var/log/mosquitto/mosquitto.log /etc/fail2ban/filter.d/mosquitto.conf
sudo systemctl enable --now fail2ban
sudo fail2ban-client status mosquitto
Expected result: fail2ban-regex reports matches against failed login log lines. fail2ban-client status mosquitto shows the jail active. If fail2ban-regex reports zero matches, adjust the failregex pattern to match your Mosquitto version’s log format.
Monitoring and Maintenance
Mosquitto publishes internal statistics to the $SYS/ topic hierarchy. Subscribe to it to check broker health:
# Requires a user with readwrite access to $SYS/#
mosquitto_sub -h localhost -p 8883 \
--cafile /etc/mosquitto/certs/ca.crt \
-u admin -P admin-password \
-t '$SYS/#' -v
Key metrics to watch: $SYS/broker/clients/connected, $SYS/broker/messages/received, and $SYS/broker/uptime. An unexpected drop in connected clients or a spike in message rate can indicate a misconfigured device.
Certificate rotation
Server certificates signed for 730 days expire in two years. Set a calendar reminder and rotate before expiry to avoid a hard outage. The rotation process:
# Generate a new server cert (keeping the same CA)
sudo openssl genrsa -out /etc/mosquitto/certs/server.key.new 4096
sudo openssl req -new -key /etc/mosquitto/certs/server.key.new \
-out /tmp/server.csr \
-subj "/CN=mqtt-pi.local/O=HomeNet/C=US"
sudo openssl x509 -req -in /tmp/server.csr \
-CA /etc/mosquitto/certs/ca.crt \
-CAkey /etc/mosquitto/certs/ca.key \
-CAcreateserial -out /etc/mosquitto/certs/server.crt.new \
-days 730 -sha256
# Swap in the new cert
sudo mv /etc/mosquitto/certs/server.crt.new /etc/mosquitto/certs/server.crt
sudo mv /etc/mosquitto/certs/server.key.new /etc/mosquitto/certs/server.key
sudo systemctl restart mosquitto
Back up the /etc/mosquitto/ directory including the CA key. The CA key is the root of the entire certificate chain. If it is lost, all client certificates must be regenerated. See BorgBackup Raspberry Pi Prune Policies for encrypted automated backups.
Troubleshooting
Connection refused or TLS handshake failure
# Check the broker log
sudo tail -50 /var/log/mosquitto/mosquitto.log
# Test TLS directly
openssl s_client -connect localhost:8883 -CAfile /etc/mosquitto/certs/ca.crt
# Check cert expiry
openssl x509 -in /etc/mosquitto/certs/server.crt -noout -dates
If openssl s_client shows verify error:num=18, the certificate is self-signed and the CA is not being trusted by the client. Confirm the client is loading ca.crt, not the server cert. If verify error:num=10 appears, the certificate has expired.
Not Authorised on a valid topic
# Check the ACL file for the user
grep -A5 "user $USERNAME" /etc/mosquitto/acl
# Reload the broker (ACL changes need a restart)
sudo systemctl restart mosquitto
ACL changes require a broker restart. A reload alone is not sufficient. Confirm the username in the ACL file exactly matches the username the client is connecting with. Case matters. If using use_identity_as_username, the ACL username must match the certificate CN exactly.
Mosquitto not starting after config change
# Validate config before restarting
sudo mosquitto -c /etc/mosquitto/mosquitto.conf --daemon false 2>&1 | head -20
# Check systemd for the specific error
journalctl -u mosquitto -n 30
Running Mosquitto directly in the foreground with --daemon false produces immediate error output for config problems. This is faster to diagnose than reading systemd journal entries after a failed restart.
FAQ
Can I use Let’s Encrypt certificates instead of self-signed?
Yes, if the broker is accessible from the internet. Let’s Encrypt certificates are trusted by all devices without distributing a CA file to each client. Use Caddy as a reverse proxy to handle certificate issuance and renewal, then proxy MQTT traffic to Mosquitto on the local port. See Caddy Reverse Proxy Raspberry Pi for the Caddy setup. For a LAN-only broker, self-signed certificates are entirely adequate. Distribute the CA cert to each client once and it trusts the broker indefinitely.
Can I run Mosquitto and Home Assistant on the same Pi?
Yes. Home Assistant includes its own Mosquitto add-on but you can point it to an external broker on the same Pi instead. Set the MQTT integration to use localhost:8883 with the admin credentials. Both services run as systemd units and do not conflict as long as ports are distinct. Pi 4 with 4GB handles both without memory pressure.
Do all clients need their own certificate?
Only if you set require_certificate true on the listener. With password authentication, clients authenticate with username and password over TLS. No client certificate is required. Client certificates add a second authentication factor and remove the need to distribute passwords to embedded devices, but they are more complex to manage. For a small home network with a handful of devices, password authentication over TLS is a practical balance.
What QoS level should I use?
QoS 0 (at most once) is sufficient for high-frequency sensor readings where occasional loss is acceptable. QoS 1 (at least once) ensures delivery but may cause duplicate messages. QoS 2 (exactly once) guarantees delivery without duplicates but adds round-trip overhead. For IoT sensors on a reliable LAN, QoS 0 or 1 covers most cases. Use QoS 2 only for control messages where a missed or duplicate command would cause a problem.
Can I access Mosquitto remotely without opening port 8883?
Yes, via VPN. With WireGuard connecting remote devices to the home network, they reach the broker at its LAN IP through the tunnel. No ports need to be forwarded and the broker is never exposed to the public internet. See WireGuard Raspberry Pi Site-to-Site VPN for the setup.
References
- https://mosquitto.org/documentation/
- https://mosquitto.org/man/mosquitto-conf-5.html
- https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
- https://github.com/fail2ban/fail2ban
About the Author
Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.
Last tested hardware: Raspberry Pi 4 Model B (4GB), ESP32 DevKit V1. Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. Mosquitto 2.0.20, Node-RED 3.1.

