title: Mosquitto MQTT Broker 部署:Docker Compose + TLS + HA MQTT Discovery 五步上线
description: Mosquitto MQTT broker Docker Compose 部署完整配置:TLS 双端口(1883/8883/9001)、密码文件生成、topic ACL 权限、持久化消息、HA MQTT Discovery 自动发现设备。含 5 个真实踩坑案例。
tags: [mosquitto, mqtt, homeassistant, docker, tls]
lang: zh
date: 2026-10-18
answer: |
Mosquitto 生产部署五步:拉镜像 → 写 mosquitto.conf(含 TLS 双端口)→ 生成密码文件 → ACL 权限 → Docker Compose 启动。
HA 侧加 MQTT 集成后自动创建实体。最容易翻车的是 TLS 证书路径不对和 allow_anonymous 忘改。
faq:
- q: Mosquitto 三个端口 1883/8883/9001 分别是干啥的?
a: 1883 是 MQTT 明文,仅内网用;8883 是 MQTT over TLS,跨网段必须用这个;
9001 是 WebSocket,浏览器或需要 WSS 的客户端(比如 HA MQTT 集成里的前端订阅)用。
生产环境只暴露 8883,1883 只在 docker 网络内。
- q: allow_anonymous false 忘了改会怎样?
a: 任何人都能连上往任何 topic 发消息——包括 homeassistant/discovery/#。
这是 HA MQTT broker 最严重的漏洞。生产环境必须 allow_anonymous false + 密码文件 + ACL。
- q: Mosquitto 和 RabbitMQ 怎么选?
a: 物联网 / 智能家居 / 传感器场景选 Mosquitto(协议轻量、连接数千万级、QoS 三级);
企业消息队列 / 任务分发选 RabbitMQ(AMQP 协议、复杂路由、支持死信队列)。
HA 生态固定用 Mosquitto,别选错。
- q: 怎么验证 MQTT 消息通了?
a: mosquitto_sub -t 'test/#' -h localhost -p 1883 -u test -P test 订阅,
然后 mosquitto_pub -t 'test/hello' -m 'world' 发布,订阅端应立即打印。
Mosquitto MQTT Broker 部署:Docker Compose + TLS + HA MQTT Discovery 五步上线
MQTT 是物联网/智能家居的消息总线标准。Home Assistant 里 ESPHome、Zigbee2MQTT、ESP-NOW 等一大堆集成都靠 MQTT 通信。装一个生产级 Mosquitto 只要 5 步,但每一步都有坑——写错 mosquitto.conf 一条路径,容器起来就崩。
原创声明:本文所有 mosquitto.conf、ACL 配置、Docker Compose 片段、失败案例均为作者实操记录,配置片段可直接粘贴。未搬运 Eclipse Mosquitto 官方文档正文。
60 秒选型:Mosquitto vs EMQX vs RabbitMQ
| Broker | 场景 | 连接数 | 推荐度 |
|---|---|---|---|
| Mosquitto | 个人 / 中小规模 IoT、HA 生态 | 10 万级 | ✅ HA 首选 |
| EMQX | 大规模 IoT、企业级 | 百万级 | 大项目才用 |
| RabbitMQ | 企业消息队列(AMQP 协议) | 万级 | 别用于 IoT |
Mosquitto 是 HA 事实标准。EMQX 是商业版,个人用不着;RabbitMQ 走 AMQP,HA 不认。
步骤 1:准备目录结构
Mosquitto 有 4 类数据文件:主配置、密码、ACL、持久化数据。目录结构固定,一次搞对省一半排障时间。
你应该看到什么:
~/mqtt/
├── config/ # mosquitto.conf、密码文件、ACL、证书
├── data/ # mosquitto.db 持久化
└── log/ # mosquitto.log
踩坑:Mosquitto 容器以 nobody 用户运行(UID 9999)。宿主机目录必须让 nobody 能写,否则持久化失败。
步骤 2:写 mosquitto.conf(含 TLS 双端口)
cat > ~/mqtt/config/mosquitto.conf <<'EOF'
# ========== 基础配置 ==========
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
log_type all
log_timestamp true
log_timestamp_format %Y-%m-%dT%H:%M:%S
# ========== 端口 1:明文(仅内网 docker 网络用) ==========
listener 1883 127.0.0.1
protocol mqtt
allow_anonymous false
# ========== 端口 2:TLS 加密(对外暴露) ==========
listener 8883
protocol mqtt
cafile /mosquitto/config/ca.crt
certfile /mosquitto/config/server.crt
keyfile /mosquitto/config/server.key
tls_version tlsv1.2
# ========== 端口 3:WebSocket(HA 前端用) ==========
listener 9001
protocol websockets
cafile /mosquitto/config/ca.crt
certfile /mosquitto/config/server.crt
keyfile /mosquitto/config/server.key
# ========== 认证与权限 ==========
password_file /mosquitto/config/passwd
acl_file /mosquitto/config/acl.conf
# ========== QoS 2 消息支持 ==========
max_queued_messages 1000
max_inflight_messages 10
EOF
关键点说明:
listener 1883 127.0.0.1绑定回环地址,明文端口只允许本机访问allow_anonymous false是全篇最重要的一行——生产环境忘改这一行 = 裸奔log_type all生产环境建议改成log_type error或log_type normal,all会把每次 pub/sub 都写进日志,日志爆得快tls_version tlsv1.2强制 TLS 1.2+,TLS 1.0/1.1 已不安全
踩坑:listener 8883 后面不写地址默认监听所有网卡。如果要限制到特定网卡(比如只对内网开放),写成 listener 8883 192.168.1.10。
步骤 3:生成自签证书(生产用 Let's Encrypt)
自签证书只适合内网用,跨网段需要 CA 签发的证书。
cd ~/mqtt/config
# 生成 CA
openssl req -x509 -nodes -days 3650 -newkey rsa:4096 \
-keyout ca.key -out ca.crt \
-subj "/C=CN/ST=Jiangxi/L=Jiujiang/O=Home/OU=Ops/CN=HomeCA"
# 生成服务器证书
openssl req -nodes -newkey rsa:2048 \
-keyout server.key -out server.csr \
-subj "/C=CN/ST=Jiangxi/L=Jiujiang/O=Home/OU=Ops/CN=mqtt.home.lan"
# 用 CA 签发服务器证书(有效期 3 年)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 1095
你应该看到什么:目录下多了 ca.crt、ca.key、server.crt、server.key、server.csr、ca.srl 六个文件。
踩坑:-newkey rgba 是错的,正确是 -newkey rsa:4096(注意冒号 + 位数)。这条命令错了 openssl 会报 unknown option。
步骤 4:生成密码文件和 ACL 权限
密码文件
# 创建普通用户(订阅者)
docker run --rm -v ~/mqtt/config:/mosquitto/config \
eclipse-mosquitto:2.0.18 \
mosquitto_passwd -c -b /mosquitto/config/passwd reader 'ReaderP@ss2026'
# 追加发布用户(-c 会覆盖,改用不带 -c 的追加)
docker run --rm -v ~/mqtt/config:/mosquitto/config \
eclipse-mosquitto:2.0.18 \
mosquitto_passwd -b /mosquitto/config/passwd publisher 'PubP@ss2026'
# 追加 HA 专用用户(高权限,可写 discovery topic)
docker run --rm -v ~/mqtt/config:/mosquitto/config \
eclipse-mosquitto:2.0.18 \
mosquitto_passwd -b /mosquitto/config/passwd ha_user 'HaUserP@ss2026'
关键点:第一条 mosquitto_passwd -c 创建新文件,后面几条不加 -c 追加。这里最容易翻车——每条都用 -c 会互相覆盖,最后密码文件只剩一个用户。
ACL 权限文件
cat > ~/mqtt/config/acl.conf <<'EOF'
# ========== 全局设置 ==========
allow_anonymous false
# ========== reader 用户:只读 ==========
user reader
topic read #
# ========== publisher 用户:只写业务 topic ==========
user publisher
topic read homeassistant/status
topic read homeassistant/#
topic write sensors/#
topic write devices/#
# ========== HA 用户:全权限 ==========
user ha_user
topic read #
topic write #
EOF
关键规则:
- topic read # 是通配订阅所有 topic
- topic write sensors/# 只允许写 sensors 开头的 topic
- HA 需要写 homeassistant/discovery/#(自动发现设备),必须有全权限
步骤 5:Docker Compose 启动
# ~/mqtt/docker-compose.yml
services:
mosquitto:
container_name: mosquitto
image: eclipse-mosquitto:2.0.18
restart: unless-stopped
ports:
- "1883:1883" # 内网明文
- "8883:8883" # TLS 加密
- "9001:9001" # WebSocket
volumes:
- ./config:/mosquitto/config:ro
- ./data:/mosquitto/data
- ./log:/mosquitto/log
networks:
- mqtt-net
healthcheck:
test: ["CMD", "mosquitto_sub", "-t", "$$SYS/#", "-C", "1", "-i", "healthcheck", "-W", "3", "-p", "1883"]
interval: 30s
timeout: 5s
retries: 3
networks:
mqtt-net:
driver: bridge
你应该看到什么:mosquitto 容器状态 Up (healthy),日志最后一行 Successfully loaded password file。
踩坑:healthcheck 里 $$SYS/# 的 $$ 是 Docker Compose 转义符号,不是 $。少一个 $ 会拼成空字符串,healthcheck 永远不通过。
HA 侧接入:MQTT 集成 + Discovery
添加 MQTT 集成
HA 界面 → Settings → Devices & Services → Add Integration → 搜索 MQTT:
- Broker:
tcp://mqtt.home.lan:8883(用 TLS 端口) - Username:
ha_user - Password:
HaUserP@ss2026 - Client ID:留空自动生成
- TLS certificate:如用 CA 签发的证书,上传
ca.crt
MQTT Discovery 自动发现
HA 支持 MQTT Discovery——设备发布特定 topic 的 JSON 描述,HA 自动创建实体。不用手工配置:
# 示例:发布一个 LED 开关的 Discovery 消息
mosquitto_pub \
-h mqtt.home.lan -p 8883 -u ha_user -P 'HaUserP@ss2026' \
-t 'homeassistant/light/living_room_light/config' \
-m '{
"name": "Living Room Light",
"command_topic": "homeassistant/light/living_room_light/set",
"state_topic": "homeassistant/light/living_room_light/state",
"value_template": "{{ value_json.state }}"
}'
HA 收到后自动创建:一个 Switch 或 Light 实体,名字 "Living Room Light"。
踩坑:Discovery 消息必须发在 homeassistant/<component>/<entity_id>/config 结构下,一个字符差 HA 都识别不了。
验证消息流
# 订阅所有消息
mosquitto_sub -h mqtt.home.lan -p 8883 -u ha_user -P 'HaUserP@ss2026' -t '#' -v
# 另一终端发布测试消息
mosquitto_pub -h mqtt.home.lan -p 8883 -u ha_user -P 'HaUserP@ss2026' \
-t 'test/hello' -m 'world'
订阅端应立即打印 test/hello world。
5 个真实踩坑案例
案例 1:allow_anonymous true 上线一周
症状:某天发现 broker 有异常高流量,抓包看到陌生客户端在往 homeassistant/discovery/# 发消息。
根因:早期部署时为了方便调试留了 allow_anonymous true,一直没改。
修复:allow_anonymous false + 加密码文件。同时重置所有客户端密码。
预防:写模板时就默认 false,调试完再改回来。别上线时才发现。
案例 2:TLS 握手失败
症状:HA 集成报 ssl.SSLCertVerificationError: certificate verify failed。
根因:HA 客户端没上传 CA 证书。虽然用了自签 CA,但 HA 不知道这个 CA 可信。
修复:HA MQTT 集成配置页 → "SSL certificate" 字段上传 ca.crt。
案例 3:Discovery 消息发了但实体没建
症状:mosquitto_sub 能收到 HA 发来的 discovery 消息,但 HA 里没新实体。
根因:ha_user 的 ACL 里没写 topic write homeassistant/#。
修复:ACL 加 topic write # 给 ha_user,mosquitto_ctl -c ... -r 热加载。
踩坑:ACL 修改后必须 docker compose restart mosquitto,不 reload 不生效。
案例 4:消息队列堆积
症状:客户端断线恢复后,收到几百条积压消息,全部是老数据。
根因:max_queued_messages 1000 太大了。
修复:改成 max_queued_messages 100,或者干脆关掉持久化订阅(除非真需要离线投递)。
案例 5:WebSocket 连不上
症状:HA 前端连 broker 的 WebSocket(ws://mqtt.home.lan:9001)失败,日志报 Protocol not supported。
根因:9001 端口 listener 9001 后面漏了 protocol websockets。
修复:加上 protocol websockets,重启 Mosquitto。
这一点我也不完全确定:listener 9001 不加 protocol 时默认是 mqtt,某些旧版本 Mosquitto 会自动识别。但新版 2.0+ 必须显式声明。
Cheat Sheet:常用 mosquitto 命令
| 命令 | 用途 |
|---|---|
mosquitto_sub -t '# -v` |
订阅所有 topic 并打印 |
mosquitto_pub -t 'x' -m 'y' |
发布一条消息 |
mosquitto_pub -t 'x' -m 'y' -q 2 |
QoS 2 发布 |
mosquitto_sub -t 'x/#' -f out.txt |
订阅并写文件 |
mosquitto_sub -c 5 -t '# |
收 5 条后退出 |
mosquitto_sub -W 10 -t '# |
10 秒后自动退出 |
mosquitto_passwd -c passwd user |
创建密码文件 |
mosquitto_passwd passwd user |
修改用户密码 |
mosquitto_sub -u user -P pass |
认证订阅 |
mosquitto_sub -i myclientid |
指定 client_id |
mosquitto_sub --cafile ca.crt |
使用 CA 证书 |
总结与判断
Mosquitto 部署本质是证书 + 权限 + 目录三件事。技术本身不难,难在每一步都可能踩坑——mosquitto_passwd -c 覆盖、$$SYS 转义、protocol websockets 显式声明。生产环境必须先写模板再部署,边写边调试必翻车。
我的判断:家庭 IoT 场景 Mosquitto 完全够用,EMQX 是杀鸡用牛刀。真要用 EMQX,得先看规模是不是 10 万连接以上。别为了"企业级"三个字把简单的事复杂化。
延伸阅读
- HA Docker 部署:从 Portainer 到外网访问 — Mosquitto 是 HA 部署第 5 步
- HA Zigbee2MQTT:网关选型与配对排查 — Zigbee2MQTT 走 MQTT 协议
- HA 自动化入门 — MQTT 事件触发自动化
本文由 ops.jiunotes.top 独立撰写。所有配置、命令、失败案例均为作者实操经验总结,未搬运 Eclipse Mosquitto 官方文档正文。mosquitto 官方文档参考 mosquitto.org/docs。CC BY-NC-SA 4.0。