Movim 0.35.4 Docker Compose 部署指南

用官方镜像 ghcr.io/movim/movim(v0.35.4)通过 Docker Compose 部署完整 Movim 栈:PostgreSQL 15 + 自建 Prosody 13.0.1 XMPP + Movim 三服务编排,覆盖编排逐项说明、自签证书 CA 注入、管理员设置、登录链路端到端验证与 12 条实测排障记录;Ubuntu 24.04.5 / Docker 29.9.2 全量实测。

MovimMovimDockerDocker ComposeXMPPPHP即时通讯2026/11/02

Movim 0.35.4 Docker Compose 部署指南

目标:用官方镜像 ghcr.io/movim/movim(内含 VERSION=v0.35.4)通过 Docker Compose 从零部署一套完整可用的 Movim——PostgreSQL 15 数据库 + Prosody 13.0.1 XMPP 服务器 + Movim 应用三服务编排,完成数据库迁移、管理员设置与登录链路端到端验证,并沉淀 12 条实测排障记录。已在 Ubuntu 24.04.5 LTS 服务器(Docker 29.9.2 / Docker Compose v5.6.1)上全量实测通过。定位与选型见 《Movim 0.35.4 介绍与选型指南》。

一、环境信息

本文所有命令与输出均在以下环境实测采集:

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64)
  • Docker:29.9.2;Docker Compose:v5.6.1(docker compose version 实测)
  • Movim:v0.35.4(官方镜像 ghcr.io/movim/movim:latest,镜像内 VERSION 文件 = v0.35.4,构建于 2026-11-01,amd64,基于 serversideup/php:8.4-fpm-nginx-alpine3.21,PHP 8.4.15)
  • 数据库:PostgreSQL 15(官方镜像 postgres:15;官方 podman-compose 用 18.3-trixie,本地取已有 15 复用,功能一致)
  • XMPP 服务器:Prosody 13.0.1(prosodyctl --version 实测回显 Prosody 13.0.1;Debian 13 发行版 apt 包自建镜像)
  • 端口映射:宿主 8148 → Movim 容器 8443(HTTPS,自签证书);daemon WebSocket 容器内 8083;XMPP c2s 5222(仅内部网络)
  • Docker 网络:movim-net(bridge,子网 10.89.72.0/24),XMPP 服务挂网络别名 movim.local
  • 部署目录:/opt/movim-stack/deploy(Compose 项目名取目录名 deploy,故容器为 deploy-db-1 / deploy-xmpp-1 / deploy-movim-1)
  • 演示口令:数据库与管理员演示密码统一 q1w2e3r4(生产务必更换)

二、镜像选型说明

Movim 官方镜像已迁移到 GitHub Container Registry,选型要点(均 2026-11-02 实测):

镜像状态说明
ghcr.io/movim/movim:latest✅ 官方现行发布渠道README 明示;镜像内 VERSION=v0.35.4;内含 nginx+fpm+daemon 全套 s6 服务
Docker Hub movim/movim❌ 已弃用仓库存在但 无任何 tag(registry API 实测 tags=[],latest/master/0.35.4 等候选全 404;pull_count 110825、last_updated 2025-09-23)
movim/xmpp-broker、movim/bridge❌ 不存在API 返回 object not found;旧架构(≤0.20)组件,现行版本无需
XMPP 侧 prosody/prosody(Docker Hub)⚠️ 本文自建替代官方镜像存在,但本实验宿主 registry 白名单拒拉,改用 debian:13 + apt 自建(见 4.3)

两种编排来源对比(本文选方案 B):

  • 方案 A:官方 podman-compose.yml——仓库根自带(db=postgres:18.3-trixie + movim 本地 build,端口 8443),README 明示「仅测试用、缓存禁用」,且面向 podman;
  • 方案 B:自写 docker-compose.yml + 预构建镜像(本文采用)——直接引用 ghcr.io/movim/movim:latest,数据目录挂宿主卷,补一个 XMPP 服务组成完整闭环。

⚠️ 注意(国内/受限网络实测):本宿主配置了 registry-mirrors 白名单(daocloud/1ms.run/hub.rat.dev),非白名单镜像一律被拒。实际拉取 ghcr.io/movim/movim:latest 走的是「skopeo copy --proxy 经代理拉 docker-archive → docker load」的降级路径;prosody 官方镜像经代理直连 registry 仍报 access denied,才改自建。正常网络环境直接 docker pull 即可,无需这些绕行。

💡 技术拓展:XMPP 服务器不止 Prosody 一种,Movim 可对接的常见选择——

服务器语言特点适用
ProsodyLua轻量、模块全、配置简洁,Debian/Ubuntu apt 直装个人/小团队自建(本文采用)
ejabberdErlang高并发、企业级、MAM/推送支持完善中大规模部署
OpenfireJava管理后台完善、插件市场已有 Java 运维体系
Movim 官方镜像内置?—否,Movim 不内置 XMPP 服务器,必须外部提供—

本文选 Prosody:apt 一个包 + 一个配置文件即可跑通,与 Movim 官方文档示例一致。

三、前置条件

  1. Docker 与 Compose:宿主机 Docker 24+(实测 29.9.2)与 docker compose v2+ 插件(docker compose version → Docker Compose version v5.6.1);
  2. 网络可达:能访问 ghcr.io 与 Docker Hub(受限网络见第二章 ⚠️ 降级实录);
  3. 端口规划:宿主 8148 空闲——ss -ltn | grep 8148 无输出即空闲;
  4. 域名/别名规划:Movim 守护进程按 JID 域名(本文 movim.local)连接 XMPP 服务器,需保证应用侧能解析该域名(本文用 Docker 网络别名实现,见 4.4);
  5. 证书准备:XMPP 服务器 TLS 证书(自签可用,但 CA 必须注入 Movim 信任链,见 4.5);
  6. 资源:建议至少 2 核 4GB(daemon + 3 worker + PG 常驻),磁盘预留 5GB;
  7. 数据库选择:强烈建议 PostgreSQL——部分特性仅 PG 可用;MariaDB 需 10.2+ 且 utf8mb4/utf8mb4_bin;MySQL 不支持。

四、部署步骤

4.1 建立部署目录与证书

mkdir -p /opt/movim-stack/deploy/xmpp/certs /opt/movim-stack/deploy/data/{pg,cache,log,public-cache}
cd /opt/movim-stack/deploy

生成 XMPP 域自签证书(供 Prosody 使用):

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout xmpp/certs/movim.local.key -out xmpp/certs/movim.local.crt \
  -days 365 -subj "/CN=movim.local" -addext "subjectAltName=DNS:movim.local"

验证证书主体与 SAN(实测回显):

openssl x509 -in xmpp/certs/movim.local.crt -noout -subject -ext subjectAltName
# subject=CN = movim.local
# X509v3 Subject Alternative Name:
#     DNS:movim.local

⚠️ 注意:目录一旦建错,Compose 挂载会踩「Docker 自动创建空目录」的坑(把挂载点当目录建),详见第十一章 Q1。

4.2 编写 Prosody 配置文件

xmpp/prosody.cfg.lua(完整内容,逐项说明见第七章 7.2):

-- Movim 演示用 Prosody XMPP 服务器配置
daemonize = false

plugin_paths = {}

folders = {
    data = "/var/lib/prosody";
}

modules_enabled = {
    "roster";
    "s2s";
    "tls";
    "dialback";
    "disco";
    "private";
    "vcard";
    "carbons";
    "pep";
    "smacks";
    "mam";
    "bookmarks";
    "lastactivity";
    "register";
    "admin_adhoc";
}

allow_registration = true

-- Movim 守护进程以普通 XMPP 客户端身份连接,证书由自定义 CA 签发并已注入信任链
c2s_require_encryption = false

authentication = "internal_plain"

log = "/var/log/prosody/prosody.log"

ssl = {
    key = "/etc/prosody/certs/movim.local.key";
    certificate = "/etc/prosody/certs/movim.local.crt";
}

VirtualHost "movim.local"

Component "conference.movim.local" "muc"

admins = { "admin@movim.local" }

4.3 构建 XMPP 服务镜像

xmpp/Dockerfile:

FROM debian:13

RUN apt-get update \
    && apt-get install -y --no-install-recommends prosody lua-unbound openssl \
    && rm -rf /var/lib/apt/lists/*

COPY prosody.cfg.lua /etc/prosody/prosody.cfg.lua
COPY certs/movim.local.key certs/movim.local.crt /etc/prosody/certs/
RUN chown prosody:prosody /etc/prosody/certs/movim.local.key /etc/prosody/certs/movim.local.crt

EXPOSE 5222

CMD ["/usr/bin/prosody", "--no-daemonize"]

逐项说明:lua-unbound 供 Prosody 做 DNS SRV 查询(缺了会报 Prosody was unable to find lua-unbound,见 Q6);user: prosody 在 Compose 侧声明(Prosody 拒绝 root 启动,见 Q2);启动参数必须用 --no-daemonize(写成 --daemonize=off 会被解析成「开启后台化」导致容器秒退,见 Q4)。

4.4 编写编排文件

docker-compose.yml(完整内容):

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: movim
      POSTGRES_PASSWORD: q1w2e3r4
      POSTGRES_DB: movim
    volumes:
      - ./data/pg:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U movim -d movim"]
      interval: 2s
      timeout: 5s
      retries: 30
    networks:
      - movim-net
    restart: unless-stopped

  xmpp:
    build:
      context: ./xmpp
    image: movim-demo-xmpp:1.0
    user: prosody
    networks:
      movim-net:
        aliases:
          - movim.local
    restart: unless-stopped

  movim:
    image: ghcr.io/movim/movim:latest
    depends_on:
      db:
        condition: service_healthy
      xmpp:
        condition: service_started
    environment:
      DB_DRIVER: pgsql
      DB_HOST: db
      DB_PORT: 5432
      DB_DATABASE: movim
      DB_USERNAME: movim
      DB_PASSWORD: q1w2e3r4
      DAEMON_URL: https://127.0.0.1:8148/
      DAEMON_DEBUG: "true"
      DAEMON_VERBOSE: "true"
    volumes:
      - ./xmpp/certs/ca-bundle.crt:/etc/ssl/certs/ca-certificates.crt:ro
      - ./data/cache:/var/www/html/cache
      - ./data/log:/var/www/html/log
      - ./data/public-cache:/var/www/html/public/cache
    ports:
      - "8148:8443"
    networks:
      - movim-net
    restart: unless-stopped

networks:
  movim-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.72.0/24

关键设计逐条说明:

  • xmpp 服务的网络别名 movim.local:Movim 容器内 Docker DNS 只认服务名,而守护进程按 JID 域名 movim.local 发起 SRV/A 查询——加别名后容器内 getent hosts movim.local 解析到 10.89.72.4、TCP 5222 连通(不加别名则连接失败,见 Q7);
  • depends_on + PG healthcheck:pg_isready 就绪后才拉起 movim,避免迁移连不上库;
  • DAEMON_URL:必须是用户浏览器实际访问的公网 URL(本文 https://127.0.0.1:8148/)。不配它 daemon 直接拒绝启动,实测报错 Please configure DAEMON_URL in .env;
  • ca-bundle.crt 只读覆盖挂载:把自签 XMPP 证书的 CA 注入 Movim 信任链(制作过程见 4.5);
  • 端口 8148:8443:镜像内 nginx 监听 8443(SSL_MODE=full 自签 HTTPS),宿主端口按本机资源规划取 8148。

4.5 制作 Movim 侧 CA 信任包

Movim 镜像(Alpine)的 TLS 校验默认 allow_self_signed=false,Prosody 用自签证书时 daemon 的 STARTTLS 校验会失败。解法:以镜像自带 CA 文件为底,拼接自签证书生成新 bundle:

docker run --rm --entrypoint cat ghcr.io/movim/movim:latest \
  /etc/ssl/certs/ca-certificates.crt > xmpp/certs/ca-bundle-base.crt
cat xmpp/certs/ca-bundle-base.crt xmpp/certs/movim.local.crt > xmpp/certs/ca-bundle.crt
grep -c "BEGIN CERTIFICATE" xmpp/certs/ca-bundle.crt
# 147(基础 146 张系统 CA + 1 张 movim.local 自签证书)

compose 已把该文件只读覆盖挂载回 /etc/ssl/certs/ca-certificates.crt(见 4.4),重建 movim 服务后生效。

4.6 启动与验证服务状态

docker compose up -d --build
docker compose ps

实测回显:

NAME             IMAGE                        COMMAND                  SERVICE   CREATED          STATUS                    PORTS
deploy-db-1      postgres:15                  "docker-entrypoint.s…"   db        42 minutes ago   Up 42 minutes (healthy)   5432/tcp
deploy-movim-1   ghcr.io/movim/movim:latest   "docker-php-serversi…"   movim     42 minutes ago   Up 15 minutes (healthy)   8080/tcp, 9000/tcp, 0.0.0.0:8148->8443/tcp, [::]:8148->8443/tcp
deploy-xmpp-1    movim-demo-xmpp:1.0          "/usr/bin/prosody --…"   xmpp      21 minutes ago   Up 21 minutes             5222/tcp

三个服务 Up(db 与 movim 均 healthy)即编排成功。镜像内 s6 服务链会自动完成首启初始化:movim-migrations(oneshot,执行 composer movim:migrate)→ movim-daemon(longrun,php daemon.php start)→ tail-log。daemon 启动横幅实测(docker compose logs movim 节选):

Please set at least one user as an admin once its account is logged in
To set an existing user admin
Movim daemon launched
Base URL: https://127.0.0.1:8148/
😋 Avatar Handler Worker launched
🌍 Resolver Worker launched
🔔 Pusher Worker launched
Templates cache cleared
po files compiled
Files compiled in Opcache
Stickers compiled
Connect to tcp://movim.local:5222, peer_name: movim.local
XMPP socket launched

💡 说明:Connect to tcp://movim.local:5222 一行证明守护进程已按「无 SRV → 回退 5222」逻辑发起 XMPP 连接;首启时还没有用户,横幅提示先设置管理员(见 4.7)。

4.7 设置管理员

docker compose exec -u www-data movim php daemon.php setAdmin movim@movim.local
# User movim@movim.local is now admin

数据库侧验证(psql -U movim -d movim):users 表主键列名是 id(存 JID,不是叫 jid 的列),该行 admin=t:

select id, admin from users;
--       id        | admin
-- ----------------+-------
--  movim@movim.local | t

4.8 端到端访问验证

Web 层(实测回显):

curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1:8148/
# 302
curl -skL https://127.0.0.1:8148/ | grep -o '<title>.*</title>'
# <title>Login</title>

nginx 访问日志同步印证:"GET /login HTTP/2.0" 200 15670。

登录链路(无浏览器环境用 curl + Python WebSocket 脚本复现):Movim 登录是「页面 WebSocket → daemon → XMPP」三段式,手工复现必须凑齐三个要素——

  1. WebSocket 握手必须携带 Cookie: MOVIM_SESSION_ID=<32位hex>、Origin、Sec-Fetch-Site: same-origin 三个头(缺头时 daemon 报 Core.php:397 Undefined array key 0;sessionId 非 hex 会被 validateCookie 拒绝并报 Invalid password format);
  2. 收到 {"func":"started","sid":"..."} 帧后,必须先发注册帧 {"func":"register","host":"movim.local"}(浏览器端由 login.js 取输入框 @ 后的域名发出,见 movim_websocket.js)——daemon 收到它才会发起 XMPP 连接;
  3. 再发 ajaxLogin 提交账号密码。

实测成功证据链:

HANDSHAKE 101
{"func":"started","sid":"75598950..."}
→ Toast "已连接到服务器" → {"func":"registered"} → Login.setQuick

daemon 日志:Connect to tcp://movim.local:5222, peer_name: movim.local → XMPP socket launched → Handler Firing Payload\STARTTLS;客户端收到 presence from movim@movim.local/movimvYMlbRDWR2xjexzL(资源绑定成功);Prosody 日志印证:

Nov 02 07:38:22 c2s59decaa62e30	info	Client connected

至此登录闭环全通。日常运维命令见第十章,配置项逐项解释见 《Movim 0.35.4 配置参数详解》,CLI 命令全集见 《Movim 0.35.4 常用 CLI 命令与工具详解》。

五、数据持久化说明

本文把全部状态挂到宿主 ./data/ 下(Compose 绑定挂载,非命名卷),重建服务不丢数据:

宿主路径容器路径内容备份建议
./data/pg/var/lib/postgresql/dataPostgreSQL 全部业务数据(users/configuration/phinxlog 等)核心,定期 pg_dump
./data/cache/var/www/html/cache模板/语言/图片缓存可再生,可不备
./data/log/var/www/html/logMovim 运行日志(loglevel=2 时落盘)按合规要求保留
./data/public-cache/var/www/html/public/cache头像/图片等对外缓存可再生(重新从 XMPP 网络拉取)

持久化实测:docker compose restart movim 后,users 表管理员行与 configuration 表 CLI 写入值均保留(psql 复查通过);Prosody 侧账号数据在其容器 /var/lib/prosody(本文未外挂,生产部署应补一条 - ./data/prosody:/var/lib/prosody 挂载)。

六、环境变量表

Movim 服务通过环境变量注入配置(三级来源:.env 文件 → 同名环境变量覆盖 → config/*.php 默认值;逐项详解见 《Movim 0.35.4 配置参数详解》)。本文用到的键:

变量本文取值默认值说明
DB_DRIVERpgsqlpgsql数据库驱动,pgsql/mysql 二选一(MySQL 实际不支持)
DB_HOSTdblocalhost数据库主机(Compose 服务名)
DB_PORT54325432数据库端口
DB_DATABASEmovimmovim库名
DB_USERNAME / DB_PASSWORDmovim / q1w2e3r4movim账号口令(演示值,生产更换)
DAEMON_URLhttps://127.0.0.1:8148/null必填,公网访问 URL;未配置 daemon 拒绝启动
DAEMON_PORT(未设,镜像内 8083)8080(config 默认)/8083(镜像改)WebSocket 监听端口
DAEMON_INTERFACE(未设)127.0.0.1监听地址,容器内对外需放开
DAEMON_DEBUG / DAEMON_VERBOSE"true" / "true"false调试与详细输出
GALENER_XMPP_IP / GALENER_XMPP_PORT(未设)127.0.0.1 / 5347Galener 音视频组件对接参数
SSL_MODE(镜像默认 full)full镜像运行时 HTTPS 模式(自签)

七、配置参数说明

7.1 Movim 镜像内部服务形态

官方镜像用 s6 编排三件套:movim-migrations(oneshot,composer movim:migrate)→ movim-daemon(longrun,php daemon.php start)→ tail-log;nginx 通过 server-opts.d/movim-websocket.conf 反代 WebSocket 到 daemon(容器内 8083)。SSL_MODE=full 时以镜像自签证书(/etc/ssl/private/self-signed-web.crt)提供 8443 HTTPS——生产应在反代层终结 TLS 或替换证书文件。

7.2 prosody.cfg.lua 关键项

  • daemonize = false + CMD --no-daemonize:双保险禁止后台化(容器内后台化=主进程退出=秒退循环,见 Q4);
  • modules_enabled:Movim 依赖的核心模块集(roster/pep/mam/carbons/smacks/vcard/bookmarks 等)。注意 Prosody 13 中 groupchat 已改名 muc,且 muc 只能以 Component 形式加载——全局启用会报 MUC should be loaded as a component(见 Q5);
  • authentication = "internal_plain":明文存储于内部库,供 daemon 以普通客户端身份注册/登录(Movim 需要拿到账号密码走 SASL);
  • log = "/var/log/prosody/prosody.log":字符串形式——表格式日志配置在 Prosody 13.0.1 会被静默忽略(源码 loggingmanager apply_sink_rules 只认 [level]=filename、数组 sink 表或字符串文件名,见 Q3);
  • c2s_require_encryption = false:放宽 c2s 强制加密(本文 CA 信任已注入,生产可收紧);
  • Component "conference.movim.local" "muc":群组聊天服务。

八、配置文件挂载

挂载形态说明
./xmpp/certs/ca-bundle.crt → /etc/ssl/certs/ca-certificates.crt:ro只读覆盖注入自签 XMPP 证书 CA(4.5 制作);:ro 防应用写回
./data/cache、./data/log、./data/public-cache读写绑定挂载应用缓存/日志持久化(第五章)
./data/pg读写绑定挂载数据库数据目录
prosody.cfg.lua / 证书打进镜像(Dockerfile COPY)也可改为挂载覆盖 /etc/prosody/prosody.cfg.lua,改配置后 docker compose restart xmpp 生效

⚠️ 注意:绑定挂载路径必须真实存在且类型正确(文件对文件、目录对目录)。路径写错时 Docker 会自动创建同名空目录顶在挂载点位置,导致后续报 not a directory——本次实测踩过两次(../xmpp/certs、../data 相对路径写错),统一改为 ./xmpp/certs 与 ./data 后解决(见 Q1)。

九、等价 docker run

Compose 编排等价的手动单容器命令(供不使用 Compose 的环境参考;本文实测载体为 Compose):

docker network create --subnet 10.89.72.0/24 movim-net

docker run -d --name movim-db --network movim-net \
  -e POSTGRES_USER=movim -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=movim \
  -v $(pwd)/data/pg:/var/lib/postgresql/data \
  postgres:15

docker run -d --name movim-xmpp --network movim-net --network-alias movim.local \
  -u prosody \
  movim-demo-xmpp:1.0

docker run -d --name movim --network movim-net -p 8148:8443 \
  -e DB_DRIVER=pgsql -e DB_HOST=movim-db -e DB_PORT=5432 \
  -e DB_DATABASE=movim -e DB_USERNAME=movim -e DB_PASSWORD=q1w2e3r4 \
  -e DAEMON_URL=https://127.0.0.1:8148/ -e DAEMON_DEBUG=true -e DAEMON_VERBOSE=true \
  -v $(pwd)/xmpp/certs/ca-bundle.crt:/etc/ssl/certs/ca-certificates.crt:ro \
  -v $(pwd)/data/cache:/var/www/html/cache \
  -v $(pwd)/data/log:/var/www/html/log \
  -v $(pwd)/data/public-cache:/var/www/html/public/cache \
  ghcr.io/movim/movim:latest

差异说明:手动形态无 depends_on 健康门控,需自行确认 movim-db healthy 后再起 movim;--network-alias 仅默认网络生效,与 Compose 的 services.xmpp.networks.movim-net.aliases 等价。

十、常用运维命令

docker compose ps                 # 服务状态(回显见 4.6)
docker compose logs -f movim      # 跟踪 daemon/迁移日志
docker compose logs xmpp          # Prosody 日志(或读 ./data 外挂路径)
docker compose exec -u www-data movim php daemon.php sessionsTree

sessionsTree 有活跃会话时实测回显:

⚙️  Movim Daemon 🔧 1 👤 1
╰─ 🔧 moUbKF2sctYINewz (1)
   ╰─ 👤 🟠 75598950ab73ae0d7d5cc6a4f80cc88e movim@movim.local (0)

(图例:🔴 Launched / 🟠 XMPP Socket connected / 🟢 XMPP Session started;无会话时为 ⚙️ Movim Daemon 🔧 0 👤 0。)

docker compose restart movim      # 改配置/换 CA 后重启应用(数据保留,见第五章)
docker compose down               # 停止并删除容器(保留 ./data 数据)
docker compose down -v --rmi local && rm -rf ./data   # 彻底清理实验环境

CLI 命令(daemon.php 12 个子命令)的完整参数与回显,见 《Movim 0.35.4 常用 CLI 命令与工具详解》。

十一、常见问题(排障实录)

以下 12 条全部来自本次部署的真实踩坑过程,按出现顺序编号。

Q1:挂载报 not a directory? 现象:容器起不来,日志提示挂载点类型错误。定位:docker-compose.yml 里把证书与数据路径写成 ../xmpp/certs、../data(相对部署目录之外),Docker 在容器侧自动创建了空目录顶掉预期挂载点。解决:统一改为 ./xmpp/certs、./data(宿主机真实存在对应目录/文件)。教训:绑定挂载前先 ls 确认路径存在且类型正确。

Q2:Prosody 启动即退出,日志 Danger, Will Robinson!? 原因:Prosody 拒绝以 root 运行。解决:Compose 的 xmpp 服务加 user: prosody(Dockerfile 内 apt 安装已创建 prosody 用户)。

Q3:Prosody 日志文件不生成? 实测三阶段:log = { levels = { ... }, to = "file" } 报 io_open nil;log = { file = { ... } } 表格式在 Prosody 13.0.1 被静默忽略(不报错也不写文件);读 loggingmanager 源码确认 apply_sink_rules 只认 [level]=filename、数组 sink 表、字符串文件名三种形态。解决:log = "/var/log/prosody/prosody.log" 字符串形式,立即生效(4.8 的 c2s 日志即从此文件读出)。

Q4:xmpp 容器秒退、无限重启? 原因:启动命令写成 prosody --daemonize=off——该语法非法,被解析为「开启后台化」,Prosody 转后台后容器主进程退出。解决:prosodyctl/prosody --help 实测确认正确参数是 -F / --no-daemonize,配置文件同时保留 daemonize = false。

Q5:Prosody 报模块错误 MUC should be loaded as a component? 原因:沿用旧配置把 groupchat 写进 modules_enabled。Prosody 13 中该模块已改名 muc,且只能由 Component "conference.<域名>" "muc" 形式加载(本文即 Component "conference.movim.local" "muc");另外 blocking 模块在 Debian 13 发行版缺失,也要从 modules_enabled 移除。解决:按 4.2 配置——muc 走 Component,删掉 blocking。

Q6:Prosody 启动报 Prosody was unable to find lua-unbound? 原因:DNS SRV 查询依赖 lua-unbound 绑定,最小化镜像未带。解决:apt 加装 lua-unbound(已并入 4.3 Dockerfile)。

Q7:daemon 连不上 XMPP(域名解析失败)? 现象:daemon 反复报无法连接 movim.local。定位:容器内 Docker DNS 只解析服务名,不认 JID 域名。解决:xmpp 服务在网络下声明别名 aliases: [movim.local](4.4),容器内 getent hosts movim.local → 10.89.72.4,nc -z 5222 通。

Q8:STARTTLS 证书校验失败? 现象:连接建立但 TLS 握手被拒。原因:Movim Linker.php 默认 allow_self_signed=false、peer_name=JID 域名,自签证书不在镜像 CA 内。解决:4.5 制作 ca-bundle(镜像原 CA + 自签证书拼接),只读覆盖挂载 /etc/ssl/certs/ca-certificates.crt,重启后 Handler Firing Payload\STARTTLS 通过。

Q9:手工 WebSocket 握手成功但 daemon 报 Core.php:397 Undefined array key 0? 原因:握手缺 Sec-Fetch-Site: same-origin(及 Origin、MOVIM_SESSION_ID Cookie)头,daemon 解析请求上下文取不到值。解决:补齐三个头;MOVIM_SESSION_ID 必须是 32 位 hex,否则 validateCookie 报 Invalid password format。

Q10:发了登录请求但 daemon 不发起 XMPP 连接? 原因:漏了 register 帧。浏览器在 login.js 里用输入框 @ 后域名发 {"func":"register","host":"movim.local"}(源码 movim_websocket.js:12),daemon 收到后才建 XMPP 连接;手工脚本必须在 started 帧后、ajaxLogin 前补发。

Q11:GHCR 镜像拉不下来? 本宿主 registry-mirrors 白名单拒绝非白名单镜像。降级路径(实测有效):skopeo copy --proxy 192.168.1.72:7897 docker://ghcr.io/movim/movim:latest docker-archive:movim-ghcr-latest.tar → docker load -i。prosody 官方镜像同法仍报 access denied,最终改 debian:13 + apt 自建(4.3)。正常网络直接 pull 即可。

Q12:daemon 启动横幅提示 Please configure DAEMON_URL in .env 后退出? DAEMON_URL 是启动硬检查项(源码 DaemonCommand.php 检查链:迁移状态 → PHP 扩展 → DAEMON_URL → 目录可写 → admin)。按 4.4 配置后,URL 缺 scheme 时 daemon 会自动补 https:// 再校验。

十二、参考链接

Movim 0.35.4 Docker Compose 部署指南 - Quzuo