AstrBot × NapCat 完整部署文档(合一版)
本文档由原先的 5 份文档合并而成,只需下载这一个文件。
环境:某云服务器(RHEL 系 Linux) | AstrBot 4.28.1(Docker)| NapCat(Docker)整理日期:2026-09-21
怎么读:
- 想照着做 → 直接跳到
第二部分 - 遇到问题 → 跳到
第五部分 - 想了解为什么和官方不一样 → 看
第四部分 ⚠️ 先记住三个最容易踩的坑:
6199是 AstrBot 的接口(NapCat 填它);6099是 NapCat 的网页(浏览器开它)- 看到「适配器已被关闭」不代表失败——只有「适配器已连接」才是成功
- 两个端口都不需要对公网放行(同机通信走
127.0.0.1)
总目录
| 部分 | 内容 | 什么时候看 |
|---|---|---|
| 阅读指南 | 导航 | |
| 我的实际部署流程 | ⭐照着做时 | |
| 官方标准流程 | 查官方原话 | |
| 差异对比 | 理解原因 | |
| 踩坑复盘与勘误 | ⭐出问题时 |
第一部分 · 阅读指南
本目录共 5 份文档,每份只讲一件事。按需要挑着看,不用全读。
📖 我应该看哪一份?
| 我想…… | 看这份 | 有用程度 |
|---|---|---|
| 照着做一遍,把 QQ 机器人跑起来 | 01 · 我的实际部署流程 | ⭐⭐⭐最有用 |
| 了解官方文档是怎么写的 | 02 · 官方标准流程 | ⭐ 参考 |
| 知道我和官方差在哪、为什么 | 03 · 差异对比 | ⭐⭐ 理解原因 |
| 知道踩了哪些坑、怎么避 | 04 · 踩坑复盘 | ⭐⭐⭐避坑必读 |
🗂️ 四份文档的分工
00-阅读指南.md ← 你正在看的(导航)
│
├── 01-我的实际部署流程.md 【操作手册】
│ 从零到跑通,照着敲就行。
│ 内容:我要做什么、敲什么命令、怎么验证、怎么收尾
│
├── 02-官方标准流程.md 【资料存档】
│ 官方文档的忠实摘要,方便日后对照。
│ 内容:官方网页上写了什么
│
├── 03-我与官方方案的区别.md 【答疑】
│ 为什么我用的方法和官方不一样。
│ 内容:9 项差异 + 每项的原因和取舍
│
└── 04-踩坑复盘与官方文档勘误.md 【排错手册】
卡在"验证"这一步的真实原因,附源码证据。
内容:官方文档的 3 个缺口 + 排错顺序
🚀 只想快速跑通?三步走
- 打开 01 · 我的实际部署流程
- 照着「阶段 3」的命令逐条执行
- 遇到问题 → 转 04 · 踩坑复盘 的排错表
⚠️ 三条最容易踩的坑(先记住)
| 坑 | 正确做法 |
|---|---|
| 6099 / 6199 分不清 | 6199是 AstrBot 的接口(NapCat 填它);6099是 NapCat 的网页(浏览器开它) |
| 看到「适配器已被关闭」就以为失败 | ❌ 这不是失败。只有"适配器已连接"才代表成功 |
| 把 6099/6199 暴露到公网 | 同机通信走 127.0.0.1,两个端口都不需要公网放行 |
🏷️ 文档中的标记约定
| 标记 | 含义 |
|---|---|
| ✅ | 已验证可行 |
| ⚠️ | 需要留意 |
| ❌ | 错误做法 / 已排除 |
| 🔴🟡🟢 | 严重度:高 / 中 / 低 |
📌 环境速记
- 服务器:一台云服务器(RHEL 系 Linux),文中统一用
你的服务器IP代指 - AstrBot:Docker 部署,容器名
astrbot,WebUI 端口6185 - NapCat:Docker 部署,容器名
napcat,数据目录/data/napcat - 文档整理日期:2026-09-21
第二部分 · 我的实际部署流程
**这份文档的定位:从零到跑通,照着敲就行。**全部命令均已在真实环境验证通过 ✅
环境:某云服务器(RHEL 系 Linux) | AstrBot 4.28.1(Docker)| NapCat(Docker)日期:2026-09-21
〇、成果总览
最终运行状态:
NAMES IMAGE STATUS napcat mlikiowa/napcat-docker:latest Up astrbot soulter/astrbot:latest Up
平台配置:
| 平台 ID | 类型 | 用途 |
|---|---|---|
default_bot | qq_official | QQ 官方机器人 |
weixin_personal | weixin_oc | 个人微信机器人 |
mybot | aiocqhttp | 本文接入的 NapCat(真实 QQ 号) |
一、整体架构(先看懂这张图)
┌─────────────────┐ 反向 WebSocket ┌──────────────────┐
│ NapCat │ ───────────────────────────► │ AstrBot │
│ (OneBot v11) │ ws://127.0.0.1:6199/ws │ (aiocqhttp) │
│ --network host │ ◄─────────────────────────── │ 容器 172.17.0.3 │
└────────┬────────┘ Token 鉴权 └──────────────────┘
│
│ 登录 QQ
▼
QQ 服务器
🔑 谁是客户端,谁是服务端?
| 角色 | 说明 |
|---|---|
| AstrBot | 服务端,监听 6199,等着别人来连 |
| NapCat | 客户端,主动去连 AstrBot 的 6199 |
| NapCat WebUI | NapCat 自己的管理网页,端口 6099 |
⚠️ 两个端口千万别搞混
| 端口 | 是谁的 | 谁访问它 |
|---|---|---|
| 6199 | AstrBot 的接口 | NapCat去连它 |
| 6099 | NapCat 的网页 | 你的浏览器打开它 |
二、阶段 0:部署 AstrBot 本体
参考官方页面:
本环境已完成 ✅
# 拉取镜像 docker pull soulter/astrbot:latest # 启动(端口按需调整) docker run -d --name astrbot --restart always \ -p 6185:6185 \ -v /data/astrbot/data:/AstrBot/data \ soulter/astrbot:latest
确认部署成功:
docker ps | grep astrbot # 浏览器访问 http://你的服务器IP:6185 进入 WebUI
💡 AstrBot WebUI 端口默认
6185,容器内数据目录是/AstrBot/data,对应宿主机/data/astrbot/data(本文档后续会用到这个路径)。
三、阶段 1:接入已有平台
在 WebUI → 平台配置 中创建:
| 平台 | 类型 | 说明 |
|---|---|---|
| QQ 官方机器人 | qq_official | 填 AppID / Secret |
| 个人微信机器人 | weixin_oc | 按官方指引扫码 |
本环境已完成 ✅(这两个与本文主题无关,略过)
四、阶段 2:接入真实 QQ 号(本次任务)
参考官方页面:
4.1 在 AstrBot WebUI 创建 OneBot v11 平台
WebUI → 平台配置 → 添加平台 → 选 aiocqhttp:
| 配置项 | 建议值 | 说明 |
|---|---|---|
| ID | 任意,如 mybot | 仅用于区分实例 |
| 反向 WebSocket 主机地址 | 0.0.0.0 | 监听所有网卡 |
| 反向 WebSocket 端口 | 6199 | 默认值 |
| Token | 随机字符串(建议 32 位) | ⚠️ 强烈建议设置 |
| 启用 | ✅ |
生成随机 Token:
openssl rand -hex 32
生成后填进上面,并记牢——后面 NapCat 要填一模一样的。
点击 保存。
⚠️ 注意:点"保存"后日志里会出现
aiocqhttp 适配器已被关闭。这不代表失败,只是旧实例被正常重载。详见 04 · 踩坑复盘。
4.2 确认 AstrBot 正在监听 6199
sudo ss -ltnp | grep 6199
应看到 LISTEN 状态。
五、阶段 3:NapCat 部署与配置(核心)
5.0 执行方式说明(本次实际用的)
本次实际是通过
deploy_napcat.sh脚本执行的,脚本全文见附录 A 。脚本会自动完成 5.1~5.3 的全部动作(建目录 → 拉镜像 → 起容器),并打印后续指引。
下面把脚本做的事拆开写一遍——一是方便理解每一步在干什么,二是万一不想用脚本,可以手工照着敲。
⚠️ 踩到的一个小坑:脚本放错目录
❌ bash deploy_napcat.sh → bash: deploy_napcat.sh: No such file or directory (文件被放在了 / 根目录,而执行时不在那个目录下) ✅ mv deploy_napcat.sh /root/ && cd /root && bash deploy_napcat.sh → 成功
正确做法:把脚本放到 /root/ 或 /data/ 下,执行前先 cd 到那个目录。
定位三步:
pwd # 我在哪个目录 ls -l # 这里有这个文件吗 find / -name "deploy_napcat*" 2>/dev/null # 全盘找一下
标准执行流程:
# 1. 把脚本传到 /root/ 或 /data/ # 2. 执行 cd /root sudo bash deploy_napcat.sh
脚本执行后会做的事(对照下面的手工步骤):
| 脚本步骤 | 对应本文 |
|---|---|
| 环境检查(docker / root 权限) | — |
| 端口占用检查(只查不杀) | 5.1 前置 |
| 同名容器保护(存在会问你 y/N) | 5.3 前置 |
创建目录 /data/napcat/{config,qq} | 5.1 |
| 读取 AstrBot 的端口和 Token(只印长度,不印明文) | 4.1 校验 |
| 拉取镜像 | 5.2 |
| 启动容器 | 5.3 |
| 打印后续操作指引 | 5.4~5.7 |
💡 卸载:
sudo bash deploy_napcat.sh --uninstall(只删容器,保留数据目录)
5.1 创建数据目录
sudo mkdir -p /data/napcat/config /data/napcat/qq sudo chown -R 1000:1000 /data/napcat
目录规范:与 AstrBot 做邻居,统一放
/data/下,一服务一目录,便于备份。
5.2 拉取镜像
sudo docker pull mlikiowa/napcat-docker:latest
5.3 启动容器
sudo docker run -d --name napcat --restart always --network host \ -e NAPCAT_UID=1000 -e NAPCAT_GID=1000 \ -v /data/napcat/config:/app/napcat/config \ -v /data/napcat/qq:/app/.config/QQ \ mlikiowa/napcat-docker:latest
参数说明:
| 参数 | 作用 |
|---|---|
--name napcat | 容器名,后续运维都用它 |
--restart always | 开机/崩溃自动重启 |
--network host | 用宿主机网络,方便直接连 127.0.0.1:6199 |
NAPCAT_UID/GID | 容器内运行用户,需与数据目录属主一致 |
-v .../config | 配置文件持久化 |
-v .../qq | QQ 登录态持久化(别丢,丢了要重新扫码) |
确认容器状态:
sudo docker ps | grep napcat
5.4 登录 QQ
sudo docker logs -f napcat
日志里会出现登录二维码或登录链接,用手机 QQ 扫码登录。
5.5 打开 NapCat WebUI
⚠️ 这一步是本流程的关键难点,官方文档没有说明服务器环境下怎么访问。
方式 A:SSH 隧道(最安全,推荐)
在你自己的电脑上执行(不是 FinalShell 的服务器窗口!):
ssh -L 6099:127.0.0.1:6099 root@你的服务器IP
保持窗口不关,本机浏览器访问:
http://127.0.0.1:6099/webui
方式 B:临时放行公网(用完立刻关闭)
- 云控制台 → 安全组 → 添加入站规则:
text TCP:6099 来源=你的本机IP/32 允许 ← ⚠️ 绝不要填 0.0.0.0/0
- 浏览器访问
http://你的服务器IP:6099/webui - 配置完成后立刻删除这条规则
方式 C:FinalShell 内置隧道
连接列表右键 → 隧道/端口转发 → 本地 6099 → 目标 127.0.0.1:6099
5.6 获取 WebUI 登录 Token
sudo docker logs napcat 2>&1 | grep -i -A2 "token\|webui"
部分版本默认 Token 为
napcat,可先试这个。
5.7 配置反向 WebSocket(最关键一步)
进入 网络配置 → 新建 → 类型选「WebSocket 客户端」(注意是客户端,不是服务器):
| 字段 | 填写内容 | 说明 |
|---|---|---|
| 启用 | ✅打开 | 不开等于白配 |
| 开启 Debug | 关 | 排查问题时再开 |
| 名称 | 任意,如 astrbot | |
| URL | ws://127.0.0.1:6199/ws | ⚠️ 结尾的 /ws不能丢 |
| 上报自身消息 | ❌ 保持关闭 | 开了可能自问自答刷屏 |
| 消息格式 | Array | AstrBot 要求 |
| Token | AstrBot 里设置的那个 | ⚠️ 两边必须完全一致 |
| 心跳间隔 | 1000或 30000 | 官方建议 1000(恢复更快) |
| 重连间隔 | 1000或 30000 | 同上 |
| SSL 证书验证 | 保持默认 | ws://不走 TLS |
填完点击 保存。
⚠️ 最容易错的两个地方:
- URL 漏掉结尾的
/ws- Token 复制时带了多余空格或换行
六、验证是否成功
✅ 唯一可靠的判据:看日志
AstrBot WebUI → 数据与日志 → 日志,出现:
aiocqhttp(OneBot v11) 适配器已连接。
看到这句 = 成功。
🔧 辅助判据:看 TCP 连接状态
在 AstrBot 容器内(或宿主机)执行:
python3 - <<'PY'
import socket, struct
st={'01':'ESTABLISHED','06':'TIME_WAIT','0A':'LISTEN'}
ip=lambda h: socket.inet_ntoa(struct.pack('<L', int(h,16)))
for line in open('/proc/net/tcp').readlines()[1:]:
p=line.split()
lp=int(p[1].split(':')[1],16); rp=int(p[2].split(':')[1],16)
if lp==6199 or rp==6199:
print(f"{ip(p[1].split(':')[0])}:{lp} <-> {ip(p[2].split(':')[0])}:{rp} {st.get(p[3],p[3])}")
PY
判读标准:
| 现象 | 含义 | 处理 |
|---|---|---|
只有 0.0.0.0:6199 LISTEN | NapCat 还没连上 | 检查 NapCat 配置是否已启用 |
反复出现 TIME_WAIT,源端口不断变化 | NapCat 在重连但被拒绝 | ⚠️几乎肯定是 Token 不匹配 |
持续 ESTABLISHED(同一源端口) | ✅成功了 | 继续下一步 |
本次部署就是靠这个方法定位到"Token 填错"的根因。
七、功能测试
- 用另一个 QQ 号私聊机器人 → 应正常回复
- 或建个小群,把机器人拉进去
@一下 - 群里不响应 → 检查 AstrBot 的唤醒设置 / 唤醒前缀
八、安全收尾(必做)
8.1 关掉公网端口
原理: NapCat 与 AstrBot 在同一台机器上,通信走 127.0.0.1 回环,不经过公网网卡,所以:
NapCat ──► 127.0.0.1:6199 ──► Docker 端口映射 ──► AstrBot 容器
↑
走回环,不受安全组/防火墙"外网规则"约束
操作清单:
- 云控制台安全组:删除
6099、6199的入站规则 - 服务器防火墙(firewalld):
sudo firewall-cmd --remove-port=6099/tcp --permanent && sudo firewall-cmd --reload - 服务器防火墙(ufw):
sudo ufw delete allow 6099/tcp - 发条消息确认机器人仍然正常回复
以后要改 NapCat 配置,用 SSH 隧道即可:
ssh -L 6099:127.0.0.1:6099 root@你的服务器IP
8.2 权限隔离检查
WebUI → 平台配置 → 管理员:
- 只有名单内的用户 ID 才能调用 Agent 工具(执行命令、读写文件)
- 新接入的 QQ 号默认不在名单里 → 别人无法通过机器人执行系统命令 ✅
- 用
/sid指令查询某用户的 ID
🔒 建议:管理员名单只放你自己,并保持
provider_settings.computer_use_require_admin = True(默认即是)。
8.3 备份提醒
务必把 /data/napcat/qq 纳入备份——里面是 QQ 登录态,丢了要重新扫码。
九、日常运维命令
# 查看日志(实时) sudo docker logs -f napcat # 查看最近 50 行 sudo docker logs --tail 50 napcat # 重启 / 停止 / 启动 sudo docker restart napcat sudo docker stop napcat sudo docker start napcat # 查看容器状态 sudo docker ps | grep napcat # 删除容器(不动数据目录) sudo docker rm -f napcat # 完全卸载(含数据,需重新扫码) sudo rm -rf /data/napcat
数据目录说明:
| 路径 | 内容 |
|---|---|
/data/napcat/config | NapCat 配置 |
/data/napcat/qq | QQ 登录态(重点备份对象) |
十、端口速查表
本机同时运行多个服务,端口需注意冲突:
| 端口 | 服务 | 容器 | 是否需要公网放行 |
|---|---|---|---|
6185 | AstrBot WebUI | astrbot | 建议不放行(用隧道) |
6199 | AstrBot 反向 WS 接口 | astrbot | ❌不需要 |
6099 | NapCat WebUI | napcat | ❌不需要(用隧道) |
| 其他 | app-server / nginx / mysql | — | 按需 |
检查端口占用:
sudo ss -ltnp | grep -E "6099|6199|6185"
🆘 出问题了?
去 04 · 踩坑复盘与官方文档勘误 的排错速查表。
附录 A:一键部署脚本
保存为
deploy_napcat.sh,放到/root/或/data/下执行:text cd /root && sudo bash deploy_napcat.sh卸载:
sudo bash deploy_napcat.sh --uninstall
#!/usr/bin/env bash
# ============================================================
# NapCat (OneBot v11) 一键部署脚本 —— 纯 docker run 版
# 配合 AstrBot 的 aiocqhttp 平台使用
#
# 用法:sudo bash deploy_napcat.sh
#
# 本脚本只创建以下内容,不影响任何其他项目:
# - 目录:/data/napcat/{config,qq}
# - 容器:napcat (不会碰你已有的任何容器/网络/卷)
# 卸载:sudo bash deploy_napcat.sh --uninstall
# ============================================================
set -euo pipefail
# ---------- 可调参数(可用环境变量覆盖)----------
BASE_DIR="${BASE_DIR:-/data/napcat}" # 部署目录
NAME="${NAME:-napcat}" # 容器名
IMAGE="${IMAGE:-mlikiowa/napcat-docker:latest}" # NapCat 镜像
WEBUI_PORT="${WEBUI_PORT:-6099}" # NapCat WebUI 端口(检查占用)
ASTRBOT_CFG="${ASTRBOT_CFG:-/data/astrbot/data/cmd_config.json}"
RUN_UID="${RUN_UID:-1000}"
RUN_GID="${RUN_GID:-1000}"
G="\033[32m"; Y="\033[33m"; R="\033[31m"; B="\033[36m"; N="\033[0m"
info(){ echo -e "${G}[✔]${N} $*"; }
warn(){ echo -e "${Y}[!]${N} $*"; }
err(){ echo -e "${R}[✘]${N} $*"; }
step(){ echo -e "
${B}==== $* ====${N}"; }
# ---------- 卸载分支 ----------
if [[ "${1:-}" == "--uninstall" ]]; then
step "卸载 NapCat"
docker rm -f "$NAME" 2>/dev/null && info "容器 $NAME 已删除" || warn "容器 $NAME 不存在"
warn "数据目录 $BASE_DIR 保留未删(里面是登录态),要彻底清除请手动: rm -rf $BASE_DIR"
exit 0
fi
# ---------- 0. 环境检查 ----------
step "环境检查"
[[ $EUID -eq 0 ]] || { err "请用 root 或 sudo 运行"; exit 1; }
command -v docker >/dev/null 2>&1 || { err "没找到 docker"; exit 1; }
info "docker: $(docker --version)"
docker info >/dev/null 2>&1 || { err "docker 守护进程没跑起来"; exit 1; }
# ---------- 0.5 端口占用检查(只看不杀)----------
step "端口占用检查"
if command -v ss >/dev/null 2>&1; then
if ss -ltn "( sport = :$WEBUI_PORT )" 2>/dev/null | grep -q ":$WEBUI_PORT"; then
warn "端口 $WEBUI_PORT 已被占用,可能是别的项目。"
warn "如果那个项目要保留,请用: WEBUI_PORT=别的端口 sudo bash $0"
warn "(NapCat 默认就监听 6099,宿主机有冲突时它可能起不来)"
else
info "端口 $WEBUI_PORT 空闲"
fi
else
warn "没有 ss 命令,跳过端口检查"
fi
# 检查是否已有同名容器(避免误删别的项目)
if docker ps -a --format '{{.Names}}' | grep -qx "$NAME"; then
warn "已存在名为 $NAME 的容器。"
read -r -p "要删除它并重新创建吗?(y/N) " ans
[[ "$ans" == "y" || "$ans" == "Y" ]] || { err "已取消"; exit 1; }
docker rm -f "$NAME"
info "旧容器已删除(只删这一个,其它容器不受影响)"
fi
# ---------- 1. 建目录 ----------
step "创建目录 $BASE_DIR"
mkdir -p "$BASE_DIR/config" "$BASE_DIR/qq"
chown -R "$RUN_UID:$RUN_GID" "$BASE_DIR" 2>/dev/null || warn "chown 失败(UID 可能不存在),忽略"
info "目录已就绪:"
ls -ld "$BASE_DIR" "$BASE_DIR/config" "$BASE_DIR/qq"
# ---------- 2. 读取 AstrBot 侧参数 ----------
step "读取 AstrBot 的 aiocqhttp 参数"
TOKEN=""
PORT="6199"
if [[ -f "$ASTRBOT_CFG" ]]; then
read -r PORT TOKEN < <(python3 - "$ASTRBOT_CFG" <<'PY'
import json,sys
cfg=json.load(open(sys.argv[1],encoding='utf-8-sig'))
for p in cfg.get('platform',[]):
if p.get('type')=='aiocqhttp':
print(p.get('ws_reverse_port') or 6199, p.get('ws_reverse_token') or '')
break
else:
print(6199, '')
PY
) || true
info "AstrBot 反向WS 端口: $PORT"
if [[ -z "${TOKEN:-}" ]]; then
warn "AstrBot 的 token 是空的!别人能连上就等于能接管你的机器人。"
warn "建议先去 AstrBot WebUI -> 平台配置 -> aiocqhttp 设一个随机 Token。"
else
info "AstrBot token 已设置(长度 ${#TOKEN},脚本不打印明文)"
fi
echo " 提示:填 NapCat 时去 WebUI 复制原文即可"
else
warn "没找到 $ASTRBOT_CFG,请手动去 AstrBot WebUI 查看端口和 token"
fi
# ---------- 3. 拉镜像 ----------
step "拉取镜像 $IMAGE"
if ! docker pull "$IMAGE"; then
err "镜像拉取失败。可能原因:镜像名变了 / 网络不通。"
err "请到 https://hub.docker.com 搜 napcat 找可用镜像名,然后:"
err " IMAGE=正确的镜像名 sudo bash $0"
exit 1
fi
info "镜像就绪"
# ---------- 4. 启动容器(纯 docker run)----------
step "启动容器 $NAME"
docker run -d \
--name "$NAME" \
--restart always \
--network host \
-e NAPCAT_UID="$RUN_UID" \
-e NAPCAT_GID="$RUN_GID" \
-v "$BASE_DIR/config:/app/napcat/config" \
-v "$BASE_DIR/qq:/app/.config/QQ" \
"$IMAGE" >/dev/null
info "容器已启动:"
docker ps --filter "name=^${NAME}$" --format "table {{.Names}} {{.Status}} {{.Ports}}"
# ---------- 5. 后续指引 ----------
step "接下来你要做的事"
cat <<'TIP'
1) 看二维码 / 登录链接:
docker logs -f napcat
(Ctrl+C 退出日志,不影响容器运行)
2) 打开 NapCat WebUI 登录 QQ:
本机浏览器 : http://127.0.0.1:6099/webui
远程服务器 : ssh -L 6099:127.0.0.1:6099 你的用户@服务器IP
再在本机浏览器开 http://127.0.0.1:6099/webui
!! 云服务器千万别把 6099 直接暴露公网,SSH 隧道最安全 !!
3) 在 NapCat WebUI 添加「网络配置 -> WebSocket 客户端」:
URL : ws://127.0.0.1:6199/ws
Token : 去 AstrBot WebUI 复制你设的那个
保存并启用,NapCat 会主动连上 AstrBot。
4) 回 AstrBot WebUI 看日志,出现连接成功 + 平台变绿 = 完成。
常用命令(只影响 napcat 自己):
docker logs -f napcat # 看日志
docker restart napcat # 重启
docker stop napcat # 停止
docker rm -f napcat # 删除容器(不动数据目录)
sudo bash deploy_napcat.sh --uninstall # 一键卸载
TIP
info "脚本执行完毕,全部操作只涉及容器 [napcat] 和目录 [$BASE_DIR]"
脚本的设计要点
| 特性 | 说明 |
|---|---|
| 零依赖 | 纯 docker run,不需要 Compose |
| 端口检查只查不杀 | 发现 6099 被占用只提示,不动别人的服务 |
| 同名容器保护 | 已存在 napcat会先问 y/N,绝不擅自删除 |
| 不打印 Token 明文 | 只显示长度,避免密钥进入终端历史/日志 |
| 可覆盖参数 | BASE_DIR/NAME/IMAGE/WEBUI_PORT等均可用环境变量覆盖 |
| 支持卸载 | --uninstall只删容器,保留数据目录 |
脚本的"零影响"承诺
- 只创建
/data/napcat/一个目录 - 只管理
napcat一个容器 - 不碰你已有的镜像缓存、网络、数据卷、其他容器
想反悔:
docker rm -f napcat && rm -rf /data/napcat
第三部分 · 官方标准流程
**这份文档的定位:官方文档写了什么,忠实记录,方便日后对照。**内容全部来自 AstrBot 官方文档正文,未做推测性转述。
来源:
https://docs.astrbot.app/deploy/astrbot/docker.html https://docs.astrbot.app/platform/aiocqhttp.html https://docs.astrbot.app/use/computer.html 摘录日期:2026-09-21 | 对照版本:AstrBot 4.28.1
一、Docker 部署 AstrBot
官方页面:
📌 具体步骤以该页面为准,命令可能随版本更新。本文不重复抄录,仅记录关键结论。
要点:
- 使用 Docker 部署 AstrBot 本体
- WebUI 默认端口 6185
- 容器内数据目录
/AstrBot/data,需映射到宿主机持久化 - 部署完成后通过浏览器访问 WebUI 管理面板
二、接入 OneBot v11(aiocqhttp)
官方页面:
页面开头的 TIP
如果您打算将 AstrBot 接入 QQ,推荐使用 QQ 官方机器人(WebSockets),由 QQ 官方推出,更稳定,支持一键扫码登录。
关于 OneBot 的说明:
OneBot 是一个聊天机器人应用接口标准,旨在统一不同聊天平台上的机器人应用开发接口。AstrBot 支持接入所有适配了 OneBot v11 反向 WebSockets(AstrBot 做服务器端)的机器人协议端。
官方列出的常见协议实现端:
| 项目 | 连接的平台 |
|---|---|
| NapCat | |
| OneDisc | Discord |
| Tele-KiraLink | Telegram |
"请参阅对应的协议实现端项目的部署文档。"
2.1 配置 OneBot v11
官方原文步骤:
- 进入 AstrBot 的 WebUI
- 点击左边栏 机器人
- 点击机器人列表上方的 创建机器人
- 选择 OneBot v11
- 在出现的表单中,填写:
| 字段 | 官方说明 |
|---|---|
| ID (id) | 随意填写,仅用于区分不同的消息平台实例 |
| 启用 (enable) | 勾选 |
| 反向 WebSocket 主机地址 | 请填写你的机器的 IP 地址,一般情况下请直接填写 0.0.0.0 |
| 反向 WebSocket 端口 | 填写一个端口,默认为 6199 |
| 反向 Websocket Token | 只有当 NapCat 网络配置中配置了 token 才需填写 |
- 点击 保存
2.2 配置协议实现端
官方原文:
请参阅对应的协议实现端项目的部署文档。
官方给出的一些注意点:
- 协议实现端需要支持反向 WebSocket,即 AstrBot 端作为服务端,实现端作为客户端
- 反向 WebSocket 的 URL 为
ws(s)://<your-host>:6199/ws
2.3 验证
官方原文:
前往 AstrBot WebUI 的 数据与日志 → 日志,如果出现
aiocqhttp(OneBot v11) 适配器已连接。蓝色的日志,说明连接成功。如果没有,若干秒后出现
aiocqhttp 适配器已被关闭则为连接超时(失败),请检查配置是否正确。
⚠️ 注意:本文档只是忠实摘录。经源码核对,上述第二条判据在 4.28.1 上不成立——详见 04 · 踩坑复盘与官方文档勘误。
2.4 附录:部署 NapCat
官方给出两条部署路径,标注"推荐采用这种方式部署"(指一键脚本)。
路径一:一键启动脚本(官方推荐)
| 平台 | 官方指向的教程 |
|---|---|
| Windows | NapCat.Shell - Win 手动启动教程 |
| Linux | NapCat.Installer - Linux 一键使用脚本(支持Ubuntu 20+ / Debian 10+ / CentOS 9) |
关于 WebUI 在哪打开,官方说明:
- 如果是 linux 命令行一键部署的 napcat:
docker log <账号>- Docker 部署的 NapCat:
docker logs napcat
路径二:Docker Compose 部署
官方原文步骤:
- 下载或复制
astrbot.yml内容 - 将刚刚下载的文件重命名为
astrbot.yml - 编辑
astrbot.yml,将# - "6199:6199"修改为- "6199:6199",移除开头的# - 在
astrbot.yml文件所在目录执行:
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose -f ./astrbot.yml up -d
- 部署完毕之后,去 NapCat 的 WebUI(默认端口 6099)中新增 OneBot 连接实例:
- 点击 网络配置 → 新建 → WebSockets 客户端
- 勾选 启用
- URL 填写
ws://宿主机IP:端口/ws,如ws://127.0.0.1:6199/ws - 如果采用上面的 Docker Compose 部署,可以填写
ws://astrbot:6199/ws
- 心跳间隔和重连间隔可以改为
1000(1 秒) - 点击保存,然后前往 AstrBot WebUI 的 数据与日志 → 日志 检查是否连接成功,出现
aiocqhttp(OneBot v11) 适配器已连接日志即代表成功
官方 astrbot.yml 的实际内容
(来源:
# docker-compose.yml
services:
napcat:
environment:
- NAPCAT_UID=${NAPCAT_UID:-1000}
- NAPCAT_GID=${NAPCAT_GID:-1000}
- MODE=astrbot
ports:
- 6099:6099
container_name: napcat
restart: always
image: mlikiowa/napcat-docker:latest
volumes:
- ./data:/AstrBot/data
- ./napcat/config:/app/napcat/config
- ./ntqq:/app/.config/QQ
networks:
- astrbot_network
astrbot:
environment:
- TZ=Asia/Shanghai
image: soulter/astrbot:latest
container_name: astrbot
restart: always
ports:
- "6185:6185"
#- "6195:6195"
#- "6199:6199"
volumes:
- ./data:/AstrBot/data
networks:
- astrbot_network
networks:
astrbot_network:
driver: bridge
📌 注意:这份文件会同时新建 AstrBot 和 NapCat 两个容器,且使用自定义网络
astrbot_network。如果你的 AstrBot 是独立部署的,直接套用会与已有实例冲突。详见 03 · 我与官方方案的区别。
三、使用电脑能力(Computer Use)
官方页面:
本节与 NapCat 部署无关,但涉及 Agent 权限,一并存档。
三档模式
| 模式 | 官方说明 |
|---|---|
none | 不启用电脑能力,不给 Agent 挂载 Shell、Python、文件系统等工具(官方默认值) |
local | 在 AstrBot 所在机器上执行,适合需要访问本机文件、命令行工具或本地依赖的场景 |
sandbox | 在隔离沙盒中执行,适合希望降低本机风险、或让多用户使用自动化能力的场景 |
配置路径: WebUI → 配置文件 → AI 配置 → 能力 → 使用电脑能力
local 模式的要点
- 能力边界接近 AstrBot 进程本身:它能访问什么,取决于 AstrBot 进程的系统权限、运行用户、工作目录和操作系统限制
- 每个会话有独立 workspace:
data/workspaces/{normalized_umo} - Shell 执行时,工作目录也被设置为这个 workspace
- 本地文件工具的相对路径默认解析到 workspace 下
- 提供的工具:Shell、Python、文件读取、文件写入、文件编辑、Grep 搜索
- 不会挂载沙盒上传/下载工具,也没有浏览器自动化能力
权限模型
「需要 AstrBot 管理员权限」默认情况下是开启的。
开启后:
| 用户 | 可用能力 |
|---|---|
| 管理员 | local 模式下的 Shell、Python、文件读取/写入/编辑、Grep 搜索 |
| 非管理员 | ❌ 不能使用 Shell 和 Python;只能在受限目录内使用文件操作 |
非管理员在 local 模式下允许访问的目录:
data/skillsdata/plugins/*/skills(只读,用于插件内置 Skills)- 当前会话的
data/workspaces/{normalized_umo} - AstrBot 的临时目录
- 系统临时目录中的
.astrbot
管理员 ID 配置位置: 配置文件 → 平台配置 → 基本 → 管理员 ID(用户可用 /sid 获取自己的 ID)
⚠️ 官方明确的安全边界
本地 Shell 内置了基础危险命令拦截,例如
rm -rf、sudo、shutdown、reboot、kill -9等。但这不是完整安全沙箱,不能把它当作安全边界。
四、官方文档中的重要提醒
以下均为官方原文,直接引用:
1. 关于公网暴露
如果您对部署、网络配置不了解,请千万不要在公网暴露 Napcat 的端口。
2. 关于 QQ 接入方式
如果您打算将 AstrBot 接入 QQ,推荐使用 QQ 官方机器人(WebSockets),由 QQ 官方推出,更稳定,支持一键扫码登录。
3. 关于沙盒
Agent 沙盒环境…(v4.12.0 版本及之后引入,替代之前的代码执行器功能)沙盒环境驱动器支持:Shipyard Neo(当前推荐)、Shipyard(旧方案)、CUA
Shipyard Neo 的三部分组成:
| 组件 | 职责 |
|---|---|
| Bay | 控制面 API,负责创建和管理 sandbox |
| Ship | 负责 Python / Shell / 文件系统能力 |
| Gull | 负责浏览器自动化能力 |
工作区根目录: 固定为 /workspace,传入路径应相对于它(如 reports/result.txt)。
📌 一页速记
| 项目 | 官方说法 |
|---|---|
| AstrBot WebUI 端口 | 6185 |
| 反向 WS 端口 | 6199(默认) |
| 反向 WS URL 格式 | ws(s)://<your-host>:6199/ws |
| NapCat WebUI 端口 | 6099(默认) |
| 推荐的 QQ 接入 | QQ 官方机器人(WebSockets) |
| NapCat 部署推荐方式 | 一键启动脚本 |
| 成功判据 | 日志出现 aiocqhttp(OneBot v11) 适配器已连接。 |
| Computer Use 默认值 | none |
| 管理员权限开关默认值 | 开启 |
第四部分 · 我与官方方案的区别
**这份文档的定位:为什么我用的方法和官方不一样?**只回答"差异是什么、为什么、怎么选",不含操作步骤。操作步骤看 01,踩坑看 04。
一、一句话总结
官方文档假设你是「从零开始、用 Docker Compose 把 AstrBot 和 NapCat 一起部署」;我的实际情况是「AstrBot 早就单独部署好了,现在只是给它补一个协议端」。
前提不同,连锁导致网络模式、部署工具、目录组织等一整串差异。
功能上完全等价——只要 NapCat 能连上 AstrBot 的 6199,用什么方式连的,AstrBot 并不关心。
前提对比
| 官方路径 | 我的路径 | |
|---|---|---|
| 前提 | 两个服务一起新建 | 一个已存在,补齐另一个 |
| 工具 | Docker Compose | 纯 Docker |
| 网络 | 同一个 Compose 网络 | --network host |
| 互联地址 | ws://astrbot:6199/ws | ws://127.0.0.1:6199/ws |
二、差异总览表
| # | 维度 | 官方文档 | 我的方案 | 差异性质 |
|---|---|---|---|---|
| 1 | NapCat 部署 | 一键脚本 / 官方 astrbot.yml(Compose) | 纯 docker run --network host | 🟢 合理变通 |
| 2 | 容器互联 | 同 Compose 网络,用服务名互访 | host 网络,走 127.0.0.1 | 🟢 合理变通 |
| 3 | 心跳/重连间隔 | 建议 1000(1 秒) | 30000(界面默认值) | 🟡 可优化 |
| 4 | Token | 不强制(NapCat 配了才需填) | 两边都配,建议 32 位随机 | 🟢 安全加强 |
| 5 | 验证方式 | 看日志判据(但该判据有问题) | 日志 +/proc/net/tcp连接状态 | 🟢 更可靠 |
| 6 | 数据目录 | 未强制约定 | /data/napcat,与 AstrBot 同级 | 🟢 施工规范 |
| 7 | 公网暴露 | 明确警告"不要暴露 NapCat 端口" | 最终全部关闭,走 SSH 隧道 | 🟢 与官方一致 |
| 8 | 电脑能力默认值 | none(不挂载任何工具) | local(可执行 Shell/Python) | 🟡 有意为之 |
| 9 | QQ 接入推荐 | 优先推荐 QQ 官方机器人 | 两条路都走(官方机器人 + NapCat) | 🟢 互补 |
图例:🟢 = 合理变通 🟡 = 值得注意/可优化
三、逐项详解
1. NapCat 的部署方式
官方: 一键启动脚本(官方推荐)/ 官方 astrbot.yml(Docker Compose)
我的:
sudo docker run -d --name napcat --restart always --network host \ -e NAPCAT_UID=1000 -e NAPCAT_GID=1000 \ -v /data/napcat/config:/app/napcat/config \ -v /data/napcat/qq:/app/.config/QQ \ mlikiowa/napcat-docker:latest
为什么不一样:
| 原因 | 说明 |
|---|---|
| 前提不同 | 官方的 astrbot.yml是把 AstrBot 和 NapCat 写在同一个 Compose 里,一步到位。我的 AstrBot 早就独立跑着,没必要为了加 NapCat 去动已有服务 |
| 工具习惯 | 我用纯 docker,没装 Compose。docker run少一层依赖 |
| 风险控制 | 另起一份 Compose 会新建一套网络和生命周期,容易和已有服务交叉;docker run 完全独立,删掉不影响任何人 |
优劣:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 官方 Compose | 一键、可复现、便于整体迁移 | 需要 Compose,要改官方文件(打开注释) |
docker run | 零依赖、隔离清晰、易回滚 | 命令长,需手动记录参数 |
结论: 已有 AstrBot 独立部署 → 用我的方式;全新搭建 → 用官方 Compose。
2. 容器之间怎么互联
官方: URL 格式 ws(s)://<your-host>:6199/ws;同 Compose 网络可填 ws://astrbot:6199/ws
我的: ws://127.0.0.1:6199/ws(配合 --network host)
三种网络模式对比:
【官方 Compose 方案】
NapCat 容器 ──┐
├── 同一个自定义 docker network
AstrBot 容器 ─┘ 用「服务名」互相找
→ ws://astrbot:6199/ws
【我的 host 方案】
NapCat 容器(直接使用宿主机网络栈)
└──→ 127.0.0.1:6199 → Docker 端口映射 → AstrBot 容器
→ ws://127.0.0.1:6199/ws
- 服务名互访:优雅,但要求两容器在同一自定义网络,需要 Compose 编排
- host 网络:简单粗暴,直接共享宿主机端口,不用管容器名解析
⚠️ 关键澄清:端口映射 ≠ 公网放行
官方要求打开 - "6199:6199",那是把容器端口发布到宿主机,让同机的 NapCat 能通过 127.0.0.1:6199 连上。
它和"在云安全组放行 6199"是两件完全不同的事:
| 操作 | 作用 | 是否需要 |
|---|---|---|
Docker 端口映射 -p 6199:6199 | 容器 → 宿主机 | ✅ 需要 |
| 云安全组放行 6199 | 互联网 → 云服务器 | ❌完全不需要 |
结论: host 网络最省心,代价是牺牲容器网络隔离。单机自用完全够。
3. 心跳 / 重连间隔
官方: 建议改为 1000(1 秒)
我的: 保持界面默认 30000(30 秒)
为什么不一样: 不是有意的,这是默认值,没改。
但实际影响了排障体验:
排查"连上又断"时,因为间隔是 30 秒,观察窗口内只能抓到 1~2 次尝试,导致一开始不够确定是"没启用"还是"被拒绝"。
建议:
| 间隔 | 优点 | 缺点 |
|---|---|---|
1000(官方建议) | 断线秒级恢复,排障友好 | 心跳日志更密 |
30000(默认) | 省资源、日志干净 | 断线恢复慢,排障不直观 |
结论: 想少踩坑就按官方建议改成 1000。
4. Token 填不填
官方:
反向 Websocket Token:只有当 NapCat 网络配置中配置了 token 才需填写。
即:官方允许两边都不填。
我的: 两边都填,建议 openssl rand -hex 32 生成 32 位随机串。
为什么不一样: 纯安全考量。不填 Token 意味着:
- AstrBot 的
6199不校验任何身份 - 任何能访问到该端口的程序,都能直接接管机器人
"内网无认证"是很危险的默认假设。多一层 Token,成本几乎为零。
结论: 官方是"能用就行",我是"顺便把锁挂上"。建议保留 Token。
5. 验证方式
官方: WebUI → 数据与日志 → 日志,看 aiocqhttp(OneBot v11) 适配器已连接 表示成功
我的: 日志 + 在容器内读 /proc/net/tcp 看 6199 的连接状态
为什么不一样: 助手在容器里无法登录 WebUI,所以用了更底层的网络状态观测。
两种方式互补:
| 方式 | 能回答的问题 |
|---|---|
| 官方日志法 | "适配器最终连上了吗?" |
| 网络状态法 | "是压根没连,还是连上又被踢了?" |
后者在本次排障中立了大功——正是看到"源端口不断变化 + 全是 TIME_WAIT",才判断出 NapCat 在反复重连但被拒绝,从而锁定 Token 不匹配 这个根因。
结论: 日常用官方日志法;卡住时用网络状态法区分"未连接"和"连接被拒"。
⚠️ 另外,官方验证章节里"出现
适配器已被关闭即连接失败"这条判据本身有问题,详见 04 · 踩坑复盘。
6. 数据目录放哪
官方: 未做强制约定。用官方 Compose 时数据自然落在 Compose 文件所在目录。
我的:
/data/
├── astrbot/ ← 原有
└── napcat/ ← 新增,与 AstrBot 做邻居
├── config/
└── qq/ ← QQ 登录态,务必备份
为什么不一样:
| 原因 | 说明 |
|---|---|
| 与现状一致 | AstrBot 已在 /data下,新服务跟着走,避免散落 |
| 备份友好 | tar -czf backup.tar.gz /data/astrbot /data/napcat一把梭 |
| 权限清晰 | 数据目录属主与 NAPCAT_UID/GID对齐 |
结论: 纯粹施工规范差异,不影响功能。跟已有习惯保持一致就是最好的选择。
7. 公网端口暴不暴露
官方:
如果您对部署、网络配置不了解,请千万不要在公网暴露 Napcat 的端口。
我的: 完全一致,甚至更严格。
- 6199:删掉安全组规则
- 6099:删掉,改用 SSH 隧道
- 隧道:
ssh -L 6099:127.0.0.1:6099 root@服务器IP
为什么关掉也不影响:
NapCat(host 网络)──→ 127.0.0.1:6199 ──→ AstrBot 容器
↑
走回环,不出网卡
不受安全组/防火墙"外网规则"约束
结论: 与官方建议完全一致,这条是硬要求。
8. 电脑能力(Computer Use)的默认值
官方:
| 模式 | 含义 |
|---|---|
none | 不启用电脑能力,不给 Agent 挂载工具(官方默认) |
local | 在 AstrBot 所在机器上执行,能力边界接近 AstrBot 进程本身 |
sandbox | 在隔离沙盒中执行 |
我的实例实际值:
computer_use_runtime = local # 官方默认是 none computer_use_require_admin = True # 与官方默认一致 ✅
为什么不一样:
require_admin:与官方默认一致,不用改local:偏离了官方默认的none。这是有意开启的——否则 Agent 无法执行任何命令、读写任何文件
官方提醒要听进去:
"这不是完整安全沙箱,不能把它当作安全边界。"
真正的防线是:
require_admin = True(防外部用户)- 管理员名单只放自己
- 不要暴露给不可信的群聊(提示注入挡不住"动嘴")
结论: local 是能力与风险的取舍。要更安全就切 sandbox(需先部署 Shipyard Neo / Bay)。
9. 接 QQ 用官方机器人还是 NapCat
官方:
推荐使用 QQ 官方机器人(WebSockets),更稳定,支持一键扫码登录。
我的: 两条路都配。
| 平台 | 类型 | 说明 |
|---|---|---|
default_bot | qq_official | QQ 官方机器人 |
mybot | aiocqhttp | NapCat / OneBot v11 |
为什么不一样: 不是"不一样",而是互补:
| 方式 | 优势 | 限制 |
|---|---|---|
| QQ 官方机器人 | 官方支持、稳定、不怕风控 | 功能受平台限制 |
| NapCat(OneBot v11) | 能力全、可用普通 QQ 号、生态丰富 | 第三方协议端,有风控/封号风险 |
结论: 能用官方机器人解决的场景优先用官方;NapCat 用于官方覆盖不到的场景,且务必用小号。
四、哪些是合理变通,哪些值得改进
🟢 合理变通(保持即可)
- 用
docker run代替官方 Compose —— AstrBot 已独立部署 - 用
--network host代替容器名互访 —— 单机场景更简单 - 数据目录定为
/data/napcat—— 与已有习惯一致 - 两边都配 Token —— 安全加强,官方不反对
- 用网络状态辅助验证 —— 补充手段,不影响官方流程
🟡 值得改进(建议采纳)
- 心跳/重连间隔:
30000→ 建议改1000(官方建议值) computer_use_runtime:local→ 如果不再需要 Agent 动手,建议改回none- Token 长度:本次实际用了 9 位 → 建议换成 32 位(
openssl rand -hex 32)
📌 一页对比
| 项目 | 官方 | 我的 |
|---|---|---|
| 部署工具 | Docker Compose | 纯 docker run |
| 网络模式 | 自定义 bridge 网络 | --network host |
| NapCat 里的 URL | ws://astrbot:6199/ws | ws://127.0.0.1:6199/ws |
| 心跳/重连 | 1000 | 30000 |
| Token | 可选 | 必填(32 位随机) |
| 数据目录 | 未约定 | /data/napcat |
| 端口暴露 | 不要暴露 | 全部关闭 + SSH 隧道 |
| 验证手段 | 看日志 | 日志 + 连接状态 |
第五部分 · 踩坑复盘与官方文档勘误
**这份文档的定位:出问题时查这里。**记录了本次部署的完整踩坑过程、根因分析(附源码证据)、以及排错速查表。
环境:AstrBot 4.28.1(Docker)| RHEL 系 Linux日期:2026-09-21
一、结论速览
不是操作错了,是官方文档在两个地方会把人带沟里。
| # | 问题 | 严重度 | 影响 |
|---|---|---|---|
| 1 | "适配器已被关闭"被解释为"连接失败",但它其实是"适配器被正常关闭/重载"的日志 | 🔴 高 | 会让人在协议端还没部署时就误判失败,直接放弃 |
| 2 | 只给了"从零用 Docker Compose 一起部署"的方案,没有覆盖"已有独立 AstrBot,再单独加一个 NapCat" | 🔴 高 | 无路可循,只能瞎试 |
| 3 | 127.0.0.1在容器场景的含义没说明 | 🟡 中 | 地址填错,连不上 |
| 4 | 只说"去 NapCat WebUI 配置",没说服务器环境下怎么访问这个 WebUI | 🟡 中 | 进不去面板,卡死 |
核心那句话
官方文档写的 "若干秒后出现
aiocqhttp 适配器已被关闭则为连接超时(失败)",在 AstrBot 4.28.1 的源码里不成立——这条日志只在适配器被主动关闭时打印,而保存平台配置这个动作本身就会触发它。
二、实际发生的时间线(已确认)
以下来自部署者本人的完整口述复盘。
【阶段 0】AstrBot 本体部署
参考 docs.astrbot.app/deploy/astrbot/docker.html
→ 用 Docker 成功部署 AstrBot
→ 打开日志、通过服务器连接进入 WebUI 管理面板 ✅ 成功
【阶段 1】已有平台接入
→ 创建 QQ 官方机器人(qq_official) ✅ 成功
→ 创建个人微信机器人(weixin_oc) ✅ 成功
【阶段 2】新增诉求:接入真实 QQ 号(OneBot v11)
参考 docs.astrbot.app/platform/aiocqhttp.html
→ 第 1 步 配置 OneBot v11(WebUI 创建平台并保存) ✅ 完成
→ 第 2 步 配置协议实现端(NapCat) ⚠️ 未能跑通
→ 第 3 步 验证
└─► 日志中出现 "aiocqhttp 适配器已被关闭"
└─► 按文档判定为"连接超时(失败)"
└─► ❌ 中止,没有继续 ⛳ 卡点
【阶段 3】改用另一套方案
→ 用 docker run --network host 单独部署 NapCat
→ 临时放行防火墙端口,打开 NapCat WebUI
→ 配置反向 WebSocket(URL + Token)
→ 机器人正常回话 ✅ 成功
→ 关闭防火墙端口 ✅ 安全收尾
三、官方文档的三个缺口
缺口一(最关键):所谓"连接失败"的判据不准确
官方原文
3. 验证如果出现
aiocqhttp(OneBot v11) 适配器已连接。,说明连接成功。如果没有,若干秒后出现aiocqhttp 适配器已被关闭则为连接超时(失败),请检查配置是否正确。
源码里实际是怎样的
astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py:
async def shutdown_trigger_placeholder(self) -> None:
await self.shutdown_event.wait()
logger.info("aiocqhttp 适配器已被关闭") # ← 这行日志
shutdown_event 只在 terminate() 里被设置:
async def terminate(self) -> None:
if hasattr(self, "shutdown_event"):
self.shutdown_event.set() # ← 唯一触发点
await self._close_reverse_ws_connections()
terminate() 由谁调用?astrbot/core/platform/manager.py:
async def reload(self, platform_config: dict) -> None:
await self.terminate_platform(platform_config["id"]) # ← 先终止旧的
if platform_config["enable"]:
await self.load_platform(platform_config) # ← 再加载新的
async def terminate_platform(self, platform_id: str) -> None:
if platform_id in self._inst_map:
logger.info(f"Attempting to terminate platform adapter {platform_id} ...")
...
await self._terminate_inst_and_tasks(inst)
结论
aiocqhttp 适配器已被关闭 会出现在这些场景:
| 触发场景 | 是否代表"连接失败" |
|---|---|
| 在 WebUI 里保存/修改平台配置(触发 reload) | ❌不代表,这是正常重载 |
| 在 WebUI 里禁用/删除该平台 | ❌ 不代表 |
| AstrBot 关闭或重启 | ❌ 不代表 |
| 协议端连接超时失败 | ⚠️ 源码里没有为这种情况打这条日志 |
唯一可靠的"成功"判据是:
@self.bot.on_websocket_connection
def on_websocket_connection(_) -> None:
logger.info("aiocqhttp(OneBot v11) 适配器已连接。")
这条只在真的收到 WebSocket 连接时打印。
缺口二:缺少"已有独立 AstrBot"场景的部署方案
官方给的 NapCat 部署路径只有三种
| 场景 | 官方是否覆盖 |
|---|---|
| ① 全新部署,Windows 跑 NapCat Shell | ✅ |
| ② 全新部署,Linux 一键脚本 | ✅ |
| ③ 全新部署,官方 Compose 一起起 | ✅ |
| ④已有独立 AstrBot,单独加一个 Docker NapCat | ❌没有 |
本次正是第 ④ 种。 官方文档里找不到对应写法,只能自己摸索。
官方 Compose 为什么不能直接套用
astrbot:
image: soulter/astrbot:latest # ← 会新起一个 AstrBot!
container_name: astrbot
ports:
- "6185:6185" # ← 与已有实例撞端口
volumes:
- ./data:/AstrBot/data # ← 全新空数据目录
| 冲突点 | 后果 |
|---|---|
6185:6185端口占用 | 新 AstrBot 起不来 |
./data全新目录 | 即便起来,也是空的、与你无关 |
自定义网络 astrbot_network | 与已有 AstrBot 的默认 bridge 网络不通,服务名解析不到 |
缺口三:容器场景下"宿主机 IP"表述有歧义
官方原文
URL 填写
ws://宿主机IP:端口/ws。如ws://127.0.0.1:6199/ws。
为什么有歧义
127.0.0.1 的含义取决于 NapCat 跑在哪:
| NapCat 的部署方式 | 127.0.0.1指的是 | 能否连上 |
|---|---|---|
| 宿主机直接运行(一键脚本) | 宿主机 | ✅ |
| bridge 网络的容器 | NapCat 容器自己 | ❌不行 |
--network host的容器 | 宿主机 | ✅ |
官方那句"如 ws://127.0.0.1:6199/ws"只在特定部署方式下成立,但没标注前提。
缺口四:没说明"服务器上怎么访问 NapCat WebUI"
官方原文:
部署完毕之后,可以去 Napcat 的 WebUI(默认端口 6099)中新增 OneBot 连接实例
但在云服务器场景下:
6099默认不对公网开放(安全组)- 官方没说可以用 SSH 隧道
- 结果就是:进不去面板,配不了反向 WS,自然连不上
这正是本次部署不得不临时放行防火墙的原因。
四、根因详解:为什么会被"骗停"
关键在于第 3 步看到的日志,来源是第 1 步。
第 1 步:在 WebUI 点【保存】
│
└─► platform_manager.reload()
├─ terminate_platform(id) → 打印 "Attempting to terminate platform adapter ..."
│ → terminate() → shutdown_event.set()
│ → 打印 "aiocqhttp 适配器已被关闭" ⛳ 日志诞生于此
└─ load_platform(config) → 启动新实例,等待协议端连接
│
第 2 步:协议实现端(NapCat)没能跑通
└─► 因此没有任何 WebSocket 连上来
→ 永远不会出现 "适配器已连接" 日志
│
第 3 步:去日志页验证
└─► 日志里**只有**第 1 步留下的 "已被关闭"
+ 官方文档说"这就是连接超时(失败)"
→ 判定失败,中止
两个误会叠加:
| 误会 | 说明 |
|---|---|
| ① 日志归因错误 | 那句"已被关闭"是第 1 步保存配置时旧实例正常关闭留下的,与第 2、3 步无关 |
| ② 判据不可靠 | 官方把这条日志当作"连接失败"的证据,但它本身不携带任何连接状态信息 |
一条与连接无关的日志,被文档赋予了"失败"的含义,导致排查提前终止。
五、第 2 步为什么没跑通
部署者当时没有打开过 NapCat 管理面板(直到阶段 3 才第一次访问),说明 NapCat 服务当时并未处于可用状态。
✅ 已排除:套用官方 astrbot.yml 导致冲突
复核命令:
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
sudo find / -maxdepth 3 \( -name "ntqq" -o -name "astrbot.yml" \) 2>/dev/null
实际结果:
NAMES IMAGE STATUS napcat mlikiowa/napcat-docker:latest Up 42 minutes astrbot soulter/astrbot:latest Up 20 hours app-server myapp:latest Up 2 days blog-site node:24-bookworm-slim Up 4 days nginx nginx Up 3 weeks mysql mysql:8 Up 3 weeks (find 命令无任何输出)
| 观察 | 结论 |
|---|---|
| 没有多余的 AstrBot 容器 | 官方 Compose没有被执行过 |
find找不到 ntqq目录、也找不到 astrbot.yml | Compose 文件从未落到磁盘上 |
只有一个 napcat容器,运行 42 分钟 | 就是阶段 3 用 docker run起的那一个 |
官方 Compose 那条路根本没被走。此原因排除。
⚠️ 仍然可能:NapCat 没起来 / 起来了但进不去 WebUI
B-1:NapCat 压根没部署成功
官方给的非 Compose 路径:
| 路径 | 本环境适配情况 |
|---|---|
| Windows NapCat.Shell | ❌ 环境不符 |
| Linux 一键脚本 | ⚠️ 官方标注支持Ubuntu 20+ / Debian 10+ / CentOS 9 |
本机是 RHEL 系的国产发行版(不等于 CentOS 9)。一键脚本在未列入支持列表的系统上可能中途失败,且失败信息未必显眼。
B-2:NapCat 起来了,但 WebUI 访问不到 ← 几乎确定成立
证据:部署者是在阶段 3 才第一次打开 NapCat 面板("然后去增加防火墙,打开 Napcat 管理面板"),说明在此之前一直访问不到。
遗留的未知项
第 2 步具体执行了哪种方式(一键脚本 / 其他),部署者已记不清。
- ✅ 已排除:官方 Compose
- ⚠️ 仍不确定:是 B-1(没部署成功)还是 B-2(进不去面板)
但这不影响结论——无论第 2 步失败于哪种原因,官方的"验证"判据都是不可靠的。
附带确认到的环境信息
| 项 | 值 |
|---|---|
| AstrBot 镜像 | soulter/astrbot:latest |
| 同机其他服务 | app-server/blog-site/nginx/mysql |
| 系统 | RHEL 系 Linux |
⚠️ 多服务共存这点值得注意:宿主机端口是共享资源。
6099/6185/6199都要确认不与其他服务冲突。
六、排错速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
bash: deploy_napcat.sh: No such file or directory | 脚本放到了 /根目录,而执行时不在那个目录(本次实际踩到) | pwd→ls -l→find / -name "deploy_napcat*" 2>/dev/null,然后 cd到脚本所在目录再执行 |
浏览器打开 6099 显示 Not Found | 把 6099 和 6199 搞混了 | NapCat WebUI 一定是6099 |
SSH 命令报 Permission denied | 命令敲在了FinalShell 的服务器窗口里 | 要在本机PowerShell / CMD 执行 |
日志出现 适配器已被关闭 | ❌不是错误!是保存配置触发的正常重载 | 忽略它,看有没有"已连接" |
| NapCat 一直"连接中 / 断开" | Token 不匹配 | 两边 Token 逐字符核对,去除空格 |
| 连上后立刻断开,反复重连 | 同上,或 URL 路径错误 | 确认 ws://127.0.0.1:6199/ws |
TIME_WAIT反复出现、源端口不断变化 | NapCat 在重连但被拒绝 | 几乎肯定是 Token 不匹配 |
| 连接正常但机器人不回复 | 平台未启用 / 唤醒词问题 | 检查平台状态与唤醒设置 |
docker pull失败 | 镜像名变更 / 网络问题 | 到 Docker Hub 搜可用镜像名 |
| 容器起不来 | 端口 6099 被占用 | sudo ss -ltnp | grep 6099 |
| 机器人自问自答刷屏 | NapCat 开启了「上报自身消息」 | 关闭该选项 |
| 进不去 NapCat WebUI | 6099 未放行 / 不知怎么访问 | 用 SSH 隧道,或临时放行本机 IP |
七、正确的排错顺序
官方顺序是「配置 → 部署协议端 → 验证」,但验证的判据不可靠。
建议改成以"连接状态"为准,而不是以日志文本为准:
① 配置 AstrBot 平台(端口 6199 + 一个随机 Token)
↓
② 部署协议端 NapCat,扫码登录
↓
③ 在 NapCat 里配置反向 WS 客户端,URL + Token 填对,保存并启用
↓
④ 验证(按可靠性排序)
a. 看日志:出现 "aiocqhttp(OneBot v11) 适配器已连接。" ← 唯一可靠的"成功"判据
b. 容器内看 6199 是否有 ESTABLISHED 连接 ← 能区分"没连"和"连上又断"
c. 直接发消息测试
"已被关闭"到底该怎么理解
| 你看到的日志组合 | 真实含义 |
|---|---|
Attempting to terminate platform adapter ...+适配器已被关闭 | 适配器被重载/关闭(多半因为刚保存了配置),不代表连接失败 |
只有 适配器已被关闭,反复出现 | 平台在反复重载,检查是否有插件或外部程序在改配置 |
适配器已连接。 | ✅真的成功了 |
| 什么都没有,一直没动静 | 协议端从未连上来,去查网络地址和协议端状态 |
八、给官方文档的修订建议
如愿意,可整理成 issue 提给 AstrBot 官方。
建议 1:修正"验证"章节的判据
原文如果没有,若干秒后出现
aiocqhttp 适配器已被关闭则为连接超时(失败),请检查配置是否正确。建议改为出现
aiocqhttp(OneBot v11) 适配器已连接。表示成功。注意:日志中的aiocqhttp 适配器已被关闭是适配器被重载或关闭时的正常日志(保存平台配置、禁用平台、重启 AstrBot 时都会打印),不能作为连接失败的判据。若始终未见"已连接"日志,请检查协议端的反向 WS 地址与 Token 是否正确。
建议 2:补充"已有独立 AstrBot"的部署章节
新增一节 "已有 AstrBot 时,如何单独部署 NapCat":
docker run -d --name napcat --restart always --network host \ -e NAPCAT_UID=1000 -e NAPCAT_GID=1000 \ -v /data/napcat/config:/app/napcat/config \ -v /data/napcat/qq:/app/.config/QQ \ mlikiowa/napcat-docker:latest
并在 NapCat WebUI 里填 ws://127.0.0.1:6199/ws。
建议 3:明确"网络地址"的三种情况
| NapCat 运行方式 | 应该填的地址 |
|---|---|
| 宿主机直接运行 | ws://127.0.0.1:6199/ws |
--network host容器 | ws://127.0.0.1:6199/ws |
| bridge 容器(与 AstrBot 同网络) | ws://<服务名或容器名>:6199/ws |
| bridge 容器(不同网络) | ws://<宿主机内网IP>:6199/ws |
建议 4:补充"服务器上如何访问 NapCat WebUI"
说明两种方式:
- SSH 隧道(推荐):
ssh -L 6099:127.0.0.1:6099 user@host - 临时放行安全组
6099(仅限自己的 IP),配置完立即删除
建议 5:澄清"端口映射 ≠ 公网放行"
官方 Compose 里打开 - "6199:6199" 是把容器端口发布到宿主机,不等于需要在云安全组放行 6199。建议与"千万不要在公网暴露 Napcat 端口"这句合并说明。
附录:源码证据
文件:astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py
# 成功日志:收到 WebSocket 连接时
@self.bot.on_websocket_connection
def on_websocket_connection(_) -> None:
logger.info("aiocqhttp(OneBot v11) 适配器已连接。")
# 关闭日志:shutdown_event 被设置时
async def shutdown_trigger_placeholder(self) -> None:
await self.shutdown_event.wait()
logger.info("aiocqhttp 适配器已被关闭")
# shutdown_event 的唯一设置点
async def terminate(self) -> None:
if hasattr(self, "shutdown_event"):
self.shutdown_event.set()
await self._close_reverse_ws_connections()
文件:astrbot/core/platform/manager.py
async def reload(self, platform_config: dict) -> None:
await self.terminate_platform(platform_config["id"]) # ← 保存配置走这里
if platform_config["enable"]:
await self.load_platform(platform_config)
...
async def terminate_platform(self, platform_id: str) -> None:
if platform_id in self._inst_map:
logger.info(f"Attempting to terminate platform adapter {platform_id} ...")
推论链:保存平台配置 → reload() → terminate_platform() → inst.terminate() → 设置 shutdown_event → 打印 aiocqhttp 适配器已被关闭
因此该日志 ≠ 连接失败。
附录:复核命令记录
本次用于排障与验证的命令,可复现:
# 1. 查看所有容器(确认有没有多余的 AstrBot)
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# 2. 查找官方 Compose 的残留文件
sudo find / -maxdepth 3 \( -name "ntqq" -o -name "astrbot.yml" \) 2>/dev/null
# 3. 查看 6199 的连接状态
python3 - <<'PY'
import socket, struct
st={'01':'ESTABLISHED','06':'TIME_WAIT','0A':'LISTEN'}
ip=lambda h: socket.inet_ntoa(struct.pack('<L', int(h,16)))
for line in open('/proc/net/tcp').readlines()[1:]:
p=line.split()
lp=int(p[1].split(':')[1],16); rp=int(p[2].split(':')[1],16)
if lp==6199 or rp==6199:
print(f"{ip(p[1].split(':')[0])}:{lp} <-> {ip(p[2].split(':')[0])}:{rp} {st.get(p[3],p[3])}")
PY
# 4. 端口占用检查
sudo ss -ltnp | grep -E "6099|6199|6185"
# 5. NapCat 日志 / WebUI Token
sudo docker logs --tail 50 napcat
sudo docker logs napcat 2>&1 | grep -i -A2 "token\|webui"
一句话总结
官方文档的"验证"判据把"适配器被正常关闭"误当成了"连接失败",而保存平台配置这个动作本身就会产生这条日志——两者叠加,导致排查被误判中止。
- 「源码层面的调用链」——确定(见源码证据)
- 「实际走过的时间线」——已确认
- 「第 2 步失败的具体原因」——已排除官方 Compose,其余见第五节
正确且唯一可靠的"成功"判据是:日志中出现
aiocqhttp(OneBot v11) 适配器已连接。

评论区
评论加载中...