Snikket stable.20260611.1 常用运维命令与排障详解

Snikket 实例运维手册:版本/状态查证、升级回滚、用户与邀请管理、证书链路维护、prosodyctl check 自检全解、数据备份与 5 个排障实录,基于 stable.20260611.1 实测。

SnikketSnikketXMPPProsody IM2026/10/30

Snikket stable.20260611.1 常用运维命令与排障详解

目标:给已上线的 Snikket 实例一份可照抄的运维手册——版本与状态查证、升级回滚、用户与邀请管理、证书链路维护、日志诊断,以及排障实录(现象→定位→处置)。全部命令与回显来自 stable.20260611.1 实例实测(2026-10-30),部署见《Snikket Docker Compose 部署指南》,配置机制见《Snikket stable.20260611.1 配置参数详解》。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64);Docker 29.9.2 + Compose v5.6.1
  • Snikket Server:stable.20260611.1(四镜像 stable 通道),内置引擎 Prosody trunk nightly build 2079 (2026-05-27, fa492a9d1a20)
  • 实例形态:测试域 snikket.local(自签证书路径),四组件 compose 栈,Web 端口映射 8140→80、8141→443
  • 演示账号:管理员 admin@snikket.local,密码 q1w2e3r4xy(⚠️ Snikket 密码策略最小长度 10,8 位的 q1w2e3r4 会被拒绝,实测见 7.4)

二、运维工具全景

动手之前先认清"手上的工具"和"名字的坑"。Snikket 的运维入口有三类:

入口载体管什么
prosodyctl / prosodyctl shellsnikket_server 内置 CLIXMPP 核心:状态、用户、配置查询、自检
create-invite 等 /usr/local/bin 工具snikket_server 内置邀请签发、TURN 地址计算
docker compose部署目录四组件生命周期:启停、日志、升级

snikket_server 镜像内 /usr/local/bin 实测清单(docker exec snikket ls /usr/local/bin):

create-invite
smtp-url-to-msmtp
snikket-turn-addresses

create-invite 签发注册邀请(5.2 详解);smtp-url-to-msmtp 是启动脚本内部把 SNIKKET_SMTP_URL 转成 msmtp 配置用的,运维无需直呼;snikket-turn-addresses 计算 TURN 服务对外的地址列表(需传域名参数,直接跑会报 no domain specified),配合防火墙放行 TURN 端口时使用。

服务名与容器名对照(compose 服务名 ≠ 容器名,混用必踩坑):

compose 服务名容器名典型命令用谁
snikket_serversnikketdocker compose logs snikket_server;docker exec snikket prosodyctl ...
snikket_proxysnikket-proxydocker compose restart snikket_proxy
snikket_portalsnikket-portaldocker compose logs snikket_portal
snikket_certssnikket-certsdocker compose logs snikket_certs

实测踩坑:docker compose logs snikket(拿容器名当服务名)直接报错:

no such service: snikket

用 docker compose config --services 拿权威清单:

snikket_certs
snikket_portal
snikket_proxy
snikket_server

规则:docker compose 子命令一律用服务名(带 _server 后缀),docker exec 一律用容器名(连字符/无后缀)。

三、前置条件

  • 已按《Snikket Docker Compose 部署指南》完成部署,四组件 Up,且手上有部署目录(官方约定 /etc/snikket,本文以 /etc/snikket 书写);
  • 具备部署目录的 sudo 权限(compose 命令需能读 docker-compose.yml 与 snikket.conf);
  • 正式实例需有真实域名与 DNS 记录——本文多处自检命令在测试域上"报错"属预期(报错本身即诊断素材,见 5.4 解读)。

四、状态查看与版本管理

4.1 版本查证(三种方式)

官方升级文档推荐命令行方式查版本:

docker exec snikket prosodyctl about | head -n1

实测回显:

Prosody trunk nightly build 2079 (2026-05-27, fa492a9d1a20)

注意这是引擎(Prosody)版本;Snikket 发行版版本要用 XMPP 版本查询(XEP-0092)问服务本体——用任一 XMPP 客户端库对 5222 端口发起会话并发版本查询即得。实测本实例的自报(Python slixmpp 客户端登录后 disco#info 携带 XEP-0092 查询的返回):

name: Prosody
version: Snikket release stable.20260611.1

第三种在 Web 界面:管理员登录门户 → 页脚 "Snikket service" → "Software versions"(官方升级文档口径,适合交给非运维同事自查)。

4.2 运行状态与健康检查

组件层状态用 compose 看:

cd /etc/snikket && docker compose ps

实测回显(节选 STATUS/PORTS 列):

NAME             IMAGE                                 SERVICE          STATUS          PORTS
snikket          snikket/snikket-server:stable         snikket_server   Up 6 minutes    0.0.0.0:3478->3478/tcp, 0.0.0.0:5222-5223->5222-5223/tcp, 0.0.0.0:5269->5269/tcp, 0.0.0.0:3478->3478/udp
snikket-certs    snikket/snikket-cert-manager:stable   snikket_certs    Up 10 minutes
snikket-portal   snikket/snikket-web-portal:stable     snikket_portal   Up 10 minutes
snikket-proxy    snikket/snikket-web-proxy:stable      snikket_proxy    Up 10 minutes   0.0.0.0:8140->80/tcp, 0.0.0.0:8141->443/tcp

四个服务全 Up 即正常;官方宿主网络形态下 PORTS 列为空属预期(host 网络不显示映射)。

XMPP 核心进程状态:

docker exec snikket prosodyctl status
Prosody is running with PID 18

门户健康检查端点 /_health(Nginx 已代理到门户):

curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: snikket.local' https://127.0.0.1:8141/_health
200

⚠️ 实测踩坑:这个 curl 必须带 Host: <你的域名> 头。Nginx 按域名路由,用 IP 直接访问不带 Host 头会落到默认站点返回 404(实测 https://127.0.0.1:8141/_health 不带 Host 头 → 404,带上 → 200);用 http 端口测会得到 301(跳 https)。监控脚本从本机拨测时,用域名作 URL 或显式 -H 'Host: <域名>'。

4.3 升级与回滚

官方升级流程(quick-start 安装方式)就两条命令:

cd /etc/snikket
docker compose pull
docker compose up -d

pull 拉取 :stable 浮动 tag 的最新镜像,up -d 依次重建有变化的组件;数据在 snikket_data 卷里,升级不动数据。升级前留意官方博客/Mastodon 的发布公告,实例自身也会收到更新通知(配置参数 SNIKKET_UPDATE_CHECK 控制,见《Snikket stable.20260611.1 配置参数详解》4.2)。

版本固定与回退:把 compose 里四个 image: 的 :stable 改成历史日期 tag(如 snikket/snikket-server:stable.20260611,Docker Hub 可查),docker compose up -d 即回退。⚠️ 回退跨过大版本可能涉及数据格式变更,先备份(5.5)再动手。

五、日常运维操作详解

5.1 用户管理

Snikket 是邀请制,没有"直接开账号"的常规路径;运维高频操作是查用户与重置密码。

查全部用户:

docker exec snikket prosodyctl shell user list snikket.local
admin@snikket.local
OK: Showing all 1 users

(snikket.local 换成你的 SNIKKET_DOMAIN。)

重置用户密码——prosodyctl shell user password 的参数是 完整 JID + 新密码,不是 user host password 三段式:

# 错误形态:把用户名当主机名解析,实测报错
$ docker exec snikket prosodyctl shell user password admin snikket.local q1w2e3r4xy
Error: No such host: admin

# 正确形态:JID 整体作第一个参数
$ docker exec snikket prosodyctl shell user password admin@snikket.local q1w2e3r4xy
OK: User password changed

⚠️ 新密码同样受 10 位最小长度策略约束(见 7.4)。查询单用户是否存在可用 prosodyctl shell user list 的输出对账,或经门户 "Manage users" 页核对(管理员登录 → Admin → Users)。

5.2 邀请管理

管理员邀请用镜像内置 create-invite(在 snikket_server 里执行)。给新管理员签发邀请:

docker exec snikket create-invite --admin --group default

实测回显:


Your invite link: https://snikket.local/invite/Bbvlmws-jPGYL1qXtfYeT_7N/

要点:

  • --admin 授予管理员权限;--group default 把受邀人挂进默认群组(Circle)。给普通用户签发就去掉 --admin;
  • 输出的邀请链接发给受邀人,浏览器打开即走注册流(设置账号名与密码、导入客户端配置),注册成功邀请即失效;
  • 邀请默认 28 天有效(模板 invites_bootstrap_ttl 默认 28×86400 秒);
  • ⚠️ 该工具不认识 --help:实测 create-invite --help → Error: Unknown parameter: --help。要批量管理邀请(列表/撤销)走门户界面:管理员登录 → Admin → Invitations;
  • 受邀注册时的密码规则与 5.1 相同(10 位起)。

5.3 证书运维

先懂链路再动手。Snikket 的证书是一条三级流水线(实测各组件行为归纳):

snikket_certs(certbot/anacron,每小时跑一次续期检查)
   │  产出 /snikket/letsencrypt/live/<域>/fullchain.pem + privkey.pem
   ▼
snikket_server(启动时 wait-for-certs 等证书,出现即 prosodyctl cert import)
   │  导入为 /etc/prosody/certs/<域>.crt/.key,XMPP 各端口启用 TLS
   ▼
snikket_proxy(cert-monitor 每 10 秒比对证书文件指纹,变了就重渲染 Nginx 并 reload;
               另有每日兜底 reload,间隔由 SNIKKET_TWEAK_WEB_PROXY_RELOAD_INTERVAL 控制)

日常维护命令:

# 证书自检(覆盖主域 + groups./share. 两个子域)
docker exec snikket prosodyctl check certs
Checking certificates...
Checking certificate for groups.snikket.local
  Certificate: /etc/prosody/certs/snikket.local.crt
Checking certificate for share.snikket.local
  Certificate: /etc/prosody/certs/snikket.local.crt
Checking certificate for snikket.local
  Certificate: /etc/prosody/certs/snikket.local.crt

All checks passed, congratulations!
# 看有效期( openssl 直接读导入后的证书)
docker exec snikket openssl x509 -enddate -noout -in /etc/prosody/certs/snikket.local.crt
notAfter=Oct 27 14:33:32 2036 GMT

(本实例走自签降级路径,有效期 10 年;正式 LE 证书有效期 90 天,靠每小时续期检查 + proxy 自动 reload 兜底,无需人工介入。)

💡 技术拓展:服务器证书的两种获取方式——

方式原理取舍
ACME HTTP-01(Let's Encrypt,正式部署用)cert-manager 经 80 端口完成域名校验后签发 90 天证书,自动续期公网可信、零维护;要求域名可公网解析且 80 端口可达
自签证书(无公网域名/内网测试)手工生成自签对并注入证书目录无外部依赖;客户端必须手动信任或跳过校验,联邦与官方 App 推送受限

Snikket 的 cert-manager 默认走 ACME HTTP-01;无公网域名时 LE 签发必然失败,此时才用自签降级(完整实测与注入方法见《Snikket Docker Compose 部署指南》8.1)。本方案实测环境无公网域名,采用自签 + 客户端跳过校验。

5.4 日志与自检诊断

日志按服务取(注意用服务名,见第二章):

docker compose logs --tail 100 snikket_server    # XMPP 核心最近 100 行
docker compose logs -f snikket_proxy             # 跟踪代理日志(Ctrl-C 退出)
docker compose logs snikket_certs 2>&1 | grep -i certbot   # 证书申请记录

server 日志实测尾部(测试域桥接形态):

snikket  | Waiting for certificates to become available...
snikket  | WARN: Unable to determine external IP address from DNS
snikket  | ERROR: Unable to determine any public IP addresses, required for TURN
snikket  | Waiting for certificates to become available...

证书组件日志实测(测试域上 certbot 每日任务签发失败并返回码 1,属自签降级场景的预期噪音;正式实例此处应为签发/续期成功记录):

snikket-certs  | /etc/cron.daily/certbot:
snikket-certs  | Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
snikket-certs  | run-parts: /etc/cron.daily/certbot exited with return code 1

读日志要点:Waiting for certificates to become available... 是首次启动的正常等待(证书流水线就位后消失);TURN 公网 IP 的 WARN/ERROR 在桥接/NAT 形态下会持续出现(coturn 探测不到公网 IP 拒绝启动,处置见 7.2),宿主网络形态不应出现。

prosodyctl check 是比日志更主动的自检工具,五个子命令逐个跑一遍胜过翻半天日志。测试域实例上的真实输出与解读:

# 配置自检:配置文件能否加载、语法是否成立
docker exec snikket prosodyctl check config
Checking config...
    The following configuration files have been loaded:
      -  /etc/prosody/prosody.cfg.lua
Done.

All checks passed, congratulations!
# DNS 自检:三个服务主机名的解析与 SRV 记录
docker exec snikket prosodyctl check dns
Checking DNS for component groups.snikket.local...
    Host groups.snikket.local does not seem to resolve to this server (IPv4/IPv6)

Checking DNS for component share.snikket.local...
    Problem looking up A record for 'share.snikket.local': ServFail
    Problem looking up AAAA record for 'share.snikket.local': ServFail
    HTTP service share.snikket.local has no A/AAAA record. Create one or change 'http_external_url' to the correct host.
    Host share.snikket.local does not seem to resolve to this server (IPv4/IPv6)

Checking DNS for host snikket.local...
    No _xmpps-client SRV record found for snikket.local, but it looks like you need one.
    Problem looking up A record for 'snikket.local': ServFail
    Problem looking up AAAA record for 'snikket.local': ServFail
    File transfer proxy snikket.local has no A/AAAA record. Create one or set 'proxy65_address' to the correct host.

解读:测试域 snikket.local 本无公网 DNS,这批报错是预期现象,恰好完整演示了诊断能力——正式实例上出现同样的行就是真问题:ServFail = 解析失败查 DNS 服务;does not seem to resolve to this server = 记录指向了别的 IP;No _xmpps-client SRV record = 建议补 SRV 记录让客户端自动发现直连 TLS 端口。

# TURN 自检:向内置 TURN 发起真实测试
docker exec snikket prosodyctl check turn
Identified 1 TURN services.

Testing TURN service snikket.local:3478...
Error: Unable to perform TURN test: setpeername: temporary failure in name resolution

Problems found, see above.

测试域解析失败导致测试无法进行(同上属预期);正式实例上这条命令通过即音视频通话的中继链路就绪。

# 联邦连通性自检:经 observe.jabber.network 探测本域可达性
docker exec snikket prosodyctl check connectivity
Checking external connectivity for groups.snikket.local via observe.jabber.network
  xmpp-server: Check service failed to establish (secure) connection

Checking external connectivity for snikket.local via observe.jabber.network
  xmpp-client: Check service failed to establish (secure) connection
  xmpps-client: Check service failed to establish (secure) connection
  xmpp-server: Check service failed to establish (secure) connection

Note: The connectivity check only checks the reachability of the domain.
Note: It does not ensure that the check actually reaches this specific prosody instance.
Problems found, see above.

解读同上:测试域无公网记录必然全失败;正式实例全绿的前提是 5222/5223/5269 对公网放行且 DNS 正确(末尾两行 Note 提醒:该检查验证的是"域能被连到",不是"连到的就是你这台")。

5.5 数据备份与恢复要点

所有状态都在 snikket_data 卷(挂载点 /snikket)里,构成与备份要点:

  • /snikket/prosody/:用户账号、消息存档、群组、OAuth2/TURN 密钥、按域目录(snikket%2e<域> 形式)——核心备份对象;
  • /snikket/letsencrypt/:证书与续期状态(正式实例需备份,丢了可重签,但会触发重新验证);
  • 备份动作:停写窗口内整卷打包,实测(测试实例数据量小,产物仅 8KB;正式实例随消息/文件量增长):
docker run --rm -v snikket-20260611-stack_snikket_data:/data -v /tmp/snk_backup:/backup ubuntu \
  tar czf /backup/snikket-data.tgz -C / data
tar: data/prosody/prosody.sock: socket ignored
ls -la /tmp/snk_backup/
-rw-r--r--   1 root root 5995 Oct 30 23:13 snikket-data.tgz

(卷名 snikket-20260611-stack_snikket_data 为 compose 项目名 + 卷名拼接,docker volume ls | grep snikket 可查;socket ignored 是 unix socket 不入包的正常提示。)

  • 恢复:新栈把卷填回 /snikket 后 docker compose up -d,server 会按目录属主自动修正 UID/GID,证书导入与端口激活自动完成;
  • 配置另存一份 snikket.conf + docker-compose.yml(几十行文本,跟着备份走)。

验证备份可用性的最低标准:恢复后 prosodyctl shell user list <域> 用户齐全 + curl 健康检查 200(4.2 的命令)。

六、注意事项

  1. restart ≠ up -d:docker compose restart 不重读 snikket.conf(环境变量是创建时注入的);改配置后必须 up -d(实测闭环见《Snikket stable.20260611.1 配置参数详解》3.4)。
  2. 服务名/容器名别混用:docker compose 用服务名(snikket_server),docker exec 用容器名(snikket),对照表见第二章。
  3. 拨测脚本带 Host 头:本机 curl 健康检查必须 -H 'Host: <域名>',否则 404(4.2 实测)。
  4. TWEAK 参数谨慎:生产实例不要随手加 TWEAK_ 变量做实验,逐项风险评估见《Snikket stable.20260611.1 配置参数详解》4.6。
  5. 升级前看公告、跨版本先备份::stable 浮动 tag 自动跟进安全修复,跨大版本回退涉及数据格式(4.3、5.5)。
  6. create-invite 产出的链接即凭据:邀请链接在有效期内任何人可注册(管理员邀请获得管理员权限),走可信渠道传递,用完即过期。

七、常见问题(排障实录)

7.1 门户 502 Bad Gateway(重启某组件后出现)

  • 现象:重启 portal(或宿主机网络变动)后访问门户 502;docker compose logs snikket_proxy 可见 upstream 连接拒绝。
  • 定位:实测过程——桥接形态下 portal 容器重建拿到新 IP(如 10.89.60.4:5765),而 proxy 内 Nginx 的 upstream 缓存了旧容器 IP,proxy_pass http://snikket_portal:5765 解析结果过期,报 Connection refused。
  • 处置:
cd /etc/snikket && docker compose restart snikket_proxy

Nginx 重新解析服务名后恢复。宿主网络形态无此问题(同一网络栈,无 IP 缓存)。本坑只在"桥接 + 重建个别组件"组合下出现,遇到 502 先重启 proxy 再查别的。

7.2 coturn 报 "Unable to determine any public IP addresses, required for TURN"

  • 现象:server 日志持续刷 ERROR: Unable to determine any public IP addresses, required for TURN,音视频通话中继不可用。
  • 定位:coturn 启动时经公网 DNS 探测自身公网 IP;桥接/NAT 环境探测不到即拒绝启动(实测日志见 5.4)。
  • 处置:正式部署用官方宿主网络形态(公网 IP 直绑,探测天然成功);确需 NAT 后部署时,按官方参数 SNIKKET_TWEAK_TURNSERVER_INTERNAL_IP 指定内网 IP 且保证 NAT 保端口号映射(参数细节见《Snikket stable.20260611.1 配置参数详解》5.6)。测试/内网环境若不需要通话中继,可接受该 ERROR 常驻(不影响文字聊天)。

7.3 首次启动 proxy/portal 报 "host not found in upstream"

  • 现象:全新启动后 docker compose logs snikket_proxy 刷 host not found in upstream "localhost:5765" 类错误。
  • 定位:Nginx 启动渲染配置时上游主机名尚未可解析(各组件启动无强顺序依赖,proxy 先于 portal 就绪时必然出现)。
  • 处置:无需干预。各组件自带重试/重渲染逻辑,全栈就绪后自愈;实测方法:稍候重跑 curl -H 'Host: <域名>' https://127.0.0.1:<https端口>/_health 至 200。持续不恢复才按 7.1 重启 proxy。

7.4 注册/改密提示密码强度不足

  • 现象:邀请注册或重置密码设置 8 位密码(如 q1w2e3r4)被拒。
  • 定位:内置密码策略最小长度 10(配置模板 password_policy = { length = 10 },运行时可用 prosodyctl shell config get <域> password_policy 查证)。
  • 处置:用 ≥10 位密码(本文演示用 q1w2e3r4xy)。策略由镜像模板固化,snikket.conf 未暴露调整参数,别尝试从配置侧放宽。

7.5 日志里的噪音辨别

首次启动与测试环境常见三类"看着吓人实则无害"的日志:Waiting for certificates to become available...(证书流水线就位前的正常等待);桥接形态的 TURN 公网 IP WARN/ERROR(7.2,环境使然);WARN: Unable to determine external IP address from DNS(同 TURN 探测)。辨别原则:对照 5.4 的自检命令结果说话——check config/check certs 全绿、健康检查 200、客户端能登录收发消息,日志里的 WARN/ERROR 就按环境差异归类,不必逐条消灭。

八、参考链接