Docker Compose 部署 ejabberd 指南
ejabberd 26.09 官方 ecs 镜像 Compose 从零部署实战:镜像选型、端口规划、数据持久化、管理员注册、WebAdmin 验证与 CTL_ON_CREATE 踩坑实录,Ubuntu 22.04 逐命令实测。
Docker Compose 部署 ejabberd 指南
目标:在一台 Ubuntu 22.04 服务器上,用 Docker Compose 从零部署 ejabberd 26.09 XMPP 即时通信服务器,覆盖镜像选型、端口规划、数据持久化、管理员账号开通、WebAdmin 验证与日常运维命令,全程命令均在 Ubuntu 22.04.4 LTS + Docker 29.8.1 环境实测通过。
一、环境信息
本章记录部署实测所用的真实环境,所有命令与回显均在此环境逐条执行验证。
- 操作系统:Ubuntu 22.04.4 LTS(x86_64);
- Docker:29.8.1;Docker Compose:v5.5.1(
docker composev2 语法); - ejabberd 版本:26.09(镜像
ejabberd/ecs:26.09,ProcessOne 官方维护,2026-10-21 时官方最新稳定版); - 服务端口规划:5222(XMPP 客户端)、5269(服务器联邦)直通宿主;WebAdmin HTTP 映射 8143→5280、TLS 综合口 8144→5443(5280/5443 若无冲突也可直通);
- 数据目录:
/srv/ejabberd/(compose 项目目录,含 conf/database/logs/upload 四个子目录); - 管理员账号:
admin@localhost/q1w2e3r4(示例演示口令,生产环境务必更换为强口令)。
二、镜像选型说明
本章说明为什么选这个镜像、它与其他获取途径的差异,选型错误会直接影响后续配置路径。
ProcessOne 官方维护两个 ejabberd 服务端镜像,均随官方部署文档(CONTAINER.md)发布:
| 镜像 | 构建仓库 | 架构 | 配置路径 | 特点 |
|---|---|---|---|---|
ejabberd/ecs(Docker Hub) | processone/docker-ejabberd | x64 | /home/ejabberd/conf/(与 /opt/ejabberd/conf/ 同一文件) | Community Server 版,默认配置已预置 admin 账号的 ACL,开箱即用 |
ghcr.io/process-one/ejabberd(GitHub Packages) | processone/ejabberd 主仓库 .github/container | x64 + arm64 | /opt/ejabberd/conf/ | 稳定版与 master 双通道,环境变量宏机制(EJABBERD_MACRO_*)更完整 |
本篇实测采用 ejabberd/ecs:26.09,理由有三:
- 官方同源:
ejabberd/ecs由 ProcessOne 官方docker-ejabberd仓库构建发布,与 ghcr 版同属官方两条产品线,且 ecs 的默认配置文件对新手更友好(内置宏定义与完整模块清单,官方 Compose 文档主线即以此形态演示); - 拉取可达:实测环境直接
docker pullDocker Hub 官方源成功;ghcr.io 通道在部分网络下会遇到 registry 拒绝(实测出现过denied,见第七章排障记录); - 版本一致:Docker Hub 的 ecs 仓库与主仓库同步发布 26.09 tag,版本号与源码 Release 一一对应。
⚠️ 注意:两个镜像的目录布局存在差异——ecs 用
/home/ejabberd/(家目录)与/opt/ejabberd/双路径(指向同一份文件,实测 inode 相同);ghcr 版文档只写/opt/ejabberd/。挂载卷时务必与你所选镜像的文档路径一致,混用会挂错位置导致配置不生效。
💡 说明:ejabberd 的版本认定双源依据、架构与同类横向对比见 《ejabberd 26.09 介绍与选型指南》。
💡 技术拓展:拉取官方镜像时常见的三个 tag 策略对比——
tag 形态 含义 适用 26.09(精确版本)锁定到具体 release 生产部署,升级可控 latest跟随最新稳定版 实验/试用,升级不可预期 master跟随开发主干 功能验证,禁用于生产 本文实际采用
26.09精确版本 tag,便于版本回溯与升级对比。
三、前置条件
部署前需确认服务器具备以下条件,逐项验证避免中途卡壳。
- Docker 与 Compose 已安装且版本可用:
docker --version && docker compose version
实测回显:
Docker version 29.8.1, build 4a63305
Docker Compose version v5.5.1
- 端口未被占用:5222/5269 直通,8143/8144 作为 WebAdmin 映射口,先检查:
ss -ltn | grep -E ':(5222|5269|8143|8144)\b' || echo 'ports free'
实测回显(部署前):
ports free
若 5222/5269 已被占用(比如宿主已跑其他 XMPP 服务),把映射改为 5223:5222 这类高位端口并在客户端相应调整;若 8143/8144 被占,从 8145 顺延取空闲端口即可。
- 数据目录已规划:本文使用
/srv/ejabberd/存放 compose 项目与全部持久化数据:
mkdir -p /srv/ejabberd/{conf,database,logs,upload}
⚠️ 注意:
database/logs/upload三个目录会被容器内ejabberd账号(UID:GID = 9000:9000)读写,bind mount 场景必须预先修正属主,否则启动时报权限拒绝:
chown -R 9000:9000 /srv/ejabberd/database /srv/ejabberd/logs /srv/ejabberd/upload
- 镜像已拉取:
docker pull ejabberd/ecs:26.09
实测回显(末行):
Status: Downloaded newer image for ejabberd/ecs:26.09
四、部署步骤
本章按「准备配置 → 编写 compose 文件 → 启动 → 注册管理员 → 验证」的顺序完成从零到可用,每一步都给出实测回显。
4.1 导出默认配置文件
ecs 镜像内置一份带完整注释的默认 ejabberd.yml(含宏定义、监听端口、模块清单),把它导出到宿主机再挂载回去,后续改配置不需要进容器:
cd /srv/ejabberd
docker run --rm --entrypoint sh ejabberd/ecs:26.09 -c 'cat /home/ejabberd/conf/ejabberd.yml' > conf/ejabberd.yml
chown 9000:9000 conf/ejabberd.yml
wc -l conf/ejabberd.yml
实测回显:
339 conf/ejabberd.yml
默认配置的核心结构(节选,完整内容见文件本体):
define_macro:
HOST: localhost
ADMIN: "admin@localhost"
PORT_C2S: 5222
PORT_C2S_TLS: 5223
PORT_S2S: 5269
PORT_HTTP_TLS: 5443
PORT_HTTP: 5280
hosts:
- HOST
acl:
local:
user_regexp: ""
loopback:
ip:
- 127.0.0.0/8
- ::1/128
admin:
user:
- ADMIN
逐项说明:define_macro 定义宏变量,配置其余位置用 HOST、PORT_C2S 等宏名引用,改域名只需改宏值;hosts 声明本服务器服务的 XMPP 域;acl 的 admin 组引用 ADMIN 宏(即 admin@localhost),注册同名账号即自动获得管理权限。
4.2 编写 docker-compose.yml
在 /srv/ejabberd/ 下创建 docker-compose.yml。必须显式设置项目名 name: ejabberd-26.09-stack,防止同机其他 ejabberd 编排同名项目互相干扰;网络子网显式指定避免与既有网络冲突:
name: ejabberd-26.09-stack
services:
ejabberd:
image: ejabberd/ecs:26.09
container_name: ejabberd-26.09-main
hostname: ejabberd-server
environment:
- CTL_ON_CREATE=register admin localhost q1w2e3r4
- CTL_ON_START=registered_users localhost ; status
ports:
- "5222:5222"
- "5269:5269"
- "8143:5280"
- "8144:5443"
volumes:
- ./conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro
- ./database:/opt/ejabberd/database
- ./logs:/opt/ejabberd/logs
- ./upload:/opt/ejabberd/upload
restart: unless-stopped
逐项说明:
CTL_ON_CREATE:仅首次启动时执行的 ejabberdctl 命令,这里注册管理员账号;多条命令用;分隔;CTL_ON_START:每次启动都执行的命令,这里列出已注册账号并打印状态,便于在日志中确认启动结果;ports:5222(客户端)、5269(联邦)直通;5280(WebAdmin HTTP)与 5443(TLS 综合口)映射到宿主 8143/8144,避免与宿主已有 5280 冲突;volumes:配置文件以只读方式挂入;database(Mnesia 数据)、logs(日志)、upload(HTTP 上传文件)三个目录持久化到宿主;restart: unless-stopped:服务器重启后服务自动拉起。
⚠️ 实测踩坑:官方文档对挂载路径写的是
/opt/ejabberd/conf、/opt/ejabberd/database,而 ecs 镜像内配置实际存放在/home/ejabberd/conf/。两个路径指向同一份文件(实测 inode 一致),按文档写/opt/ejabberd/即可正常生效;若改用 ghcr 版镜像,路径以/opt/ejabberd/为准。
4.3 启动服务
cd /srv/ejabberd
docker compose up -d
实测回显:
Container ejabberd-26.09-main Created
Container ejabberd-26.09-main Starting
Container ejabberd-26.09-main Started
等待约 10 秒让 Mnesia 完成初始化,确认运行状态:
docker compose ps
实测回显(输出节选,PORTS 列有截断):
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
ejabberd-26.09-main ejabberd/ecs:26.09 "/home/ejabberd/bin/…" ejabberd 12 seconds ago Up 12 seconds 0.0.0.0:5222->5222/tcp, [::]:5222->5222/tcp, 0.0.0.0:5269->5269/tcp, ...
查看启动日志确认 ejabberd 内核已就绪:
docker compose logs ejabberd | grep -E 'started in the node|accepting TCP'
实测回显(节选):
2026-10-21 08:47:20.535 [info] ejabberd 26.9.0 is started in the node :ejabberd@localhost in 0.80s
2026-10-21 08:47:20.562 [info] Start accepting TCP connections at [::]:1880 for :ejabberd_http
4.4 注册管理员账号并验证
⚠️ 实测踩坑(重要):compose 里虽然配置了
CTL_ON_CREATE=register admin localhost q1w2e3r4,但因为部署前已按第三章预创建了database目录并 chown,ejabberdctl 的首次启动判定(脚本中[ -d "$SPOOL_DIR" ] || FIRST_RUN=true,即数据库目录不存在才视为首次运行)不会触发,CTL_ON_CREATE被静默跳过。bind mount 方式下 database 目录必须提前存在,因此首次部署后请手动执行一次注册命令,并把它当作固定步骤:
docker exec ejabberd-26.09-main ejabberdctl register admin localhost q1w2e3r4
实测回显:
User admin@localhost successfully registered
验证账号与密码:
docker exec ejabberd-26.09-main ejabberdctl registered_users localhost
docker exec ejabberd-26.09-main ejabberdctl check_password admin localhost q1w2e3r4 && echo 'password OK'
实测回显:
admin
password OK
4.5 服务状态与端口验证
docker exec ejabberd-26.09-main ejabberdctl status
实测回显:
The node ejabberd@localhost is started with status: started
ejabberd 26.9.0 is running in that node
宿主侧确认端口监听:
ss -ltn | grep -E ':(5222|5269|8143|8144)\b'
实测回显:
LISTEN 0 4096 0.0.0.0:8143 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8144 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:5222 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:5269 0.0.0.0:*
4.6 WebAdmin 管理台验证
浏览器访问 http://<服务器IP>:8143/admin/,弹出 HTTP Basic 认证,用户名填 admin@localhost(完整 JID)、密码填 q1w2e3r4。命令行验证(HTTP 401 表示需要认证,带凭据返回管理台首页):
curl -s -o /dev/null -w '%{http_code}\n' --max-time 10 http://127.0.0.1:8143/admin/
curl -s -u 'admin@localhost:q1w2e3r4' --max-time 10 http://127.0.0.1:8143/admin/ | grep -oE '<title>[^<]*</title>'
实测回显:
401
<title>ejabberd Web Admin</title>
WebAdmin 首页可查看节点状态、已注册账号(Nodes → localhost → Accounts)、群聊、会话等,此处不再展开截图式描述。另外 8144(容器内 5443)是 TLS 综合口,同时承载 HTTPS 版 WebAdmin(https://<服务器IP>:8144/admin/)、REST API(/api)、WebSocket(/websocket)与 BOSH(/bosh)。
💡 说明:WebAdmin 的登录账号能登录的前提是
acl.admin覆盖该账号——默认配置的ADMIN宏即admin@localhost,故注册同名账号即可;若你改了HOST宏(换域名),管理员 JID 与注册命令都要同步改为admin@<新域名>。
4.7 数据持久化验证
重启服务后账号与配置应原样保留(数据落在 Mnesia 磁盘表):
docker compose restart ejabberd
sleep 8
docker exec ejabberd-26.09-main ejabberdctl registered_users localhost
实测回显:
Container ejabberd-26.09-main Started
admin
重启后 CTL_ON_START 仍会执行(每次启动都跑),日志可见:
:> ejabberdctl registered_users localhost
:> ejabberdctl status
数据目录落盘情况(宿主侧查看):
ls /srv/ejabberd/database/ | head -5
实测回显(节选):
archive_msg.DCD
archive_msg.DCL
last_activity.DCD
passwd.DCD
roster.DAT
五、环境变量表
ecs 镜像通过环境变量注入启动行为,下表为实测验证过的关键变量(全部来自官方部署文档并在本环境复核):
| 变量 | 作用 | 实测取值示例 |
|---|---|---|
CTL_ON_CREATE | 首次启动执行的 ejabberdctl 命令(数据库目录已存在时不触发,见 4.4 排障) | register admin localhost q1w2e3r4 |
CTL_ON_START | 每次启动执行的命令,多条用 ; 分隔,命令前加 ! 可忽略失败 | registered_users localhost ; status |
ERLANG_NODE_ARG | 覆盖 Erlang 节点名(集群用) | ejabberd@main |
ERLANG_COOKIE | Erlang 节点互联 cookie(集群各节点必须一致) | dummycookie123 |
EJABBERD_BYPASS_WARNINGS | 置 true 时忽略配置警告(live 模式常用) | true |
💡 说明:ghcr 版镜像额外支持
EJABBERD_MACRO_*环境变量(24.12 起),可把ejabberd.yml里的同名宏值用环境变量覆盖,例如EJABBERD_MACRO_HOST=example.com覆盖HOST宏;REGISTER_ADMIN_PASSWORD配合EJABBERD_MACRO_ADMIN自动注册管理员。ecs 镜像以「挂配置文件 + CTL_ON_* 命令」为主要定制手段。
六、配置文件挂载与参数速改
日常改配置的最短路径:编辑宿主 /srv/ejabberd/conf/ejabberd.yml → 重启服务(配置文件以只读挂载,热重载也可用 ejabberdctl reload_config,但 ecs 只读挂载下更稳妥的是重启):
vim /srv/ejabberd/conf/ejabberd.yml
docker compose restart ejabberd
三个最高频的改动点:
- 改 XMPP 域名:把宏
HOST: localhost改为你的域名(如HOST: chat.example.com),同时ADMIN宏改为admin@chat.example.com,再重新注册对应管理员账号; - 开注册:默认
mod_register的ip_access: trusted_network只允许回环网段自助注册(防滥用),生产若要开放自助注册需调整trusted_network的 ACL; - 日志级别:顶层
loglevel: info可改为warning降噪或debug排障。
配置文件的逐段详解(hosts/listeners/auth/数据库/ACL/modules 等每一节的参数含义与实测改法)见 《ejabberd 26.09 配置参数详解》。
七、等价 docker run 命令
不使用 compose 时,下面一条 docker run 等价于本文的 compose 部署(多行只为阅读方便):
mkdir -p /srv/ejabberd/{conf,database,logs,upload}
chown -R 9000:9000 /srv/ejabberd/database /srv/ejabberd/logs /srv/ejabberd/upload
docker run -d --name ejabberd-26.09-main \
-p 5222:5222 -p 5269:5269 -p 8143:5280 -p 8144:5443 \
-v /srv/ejabberd/conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro \
-v /srv/ejabberd/database:/opt/ejabberd/database \
-v /srv/ejabberd/logs:/opt/ejabberd/logs \
-v /srv/ejabberd/upload:/opt/ejabberd/upload \
-e CTL_ON_START="registered_users localhost ; status" \
ejabberd/ecs:26.09
生产建议统一用 compose 管理(版本声明、网络规划、一键升降级),docker run 仅用于临时验证。
八、常用运维命令
以下命令均在部署目录 /srv/ejabberd/ 下实测:
docker compose ps # 查看服务状态
docker compose logs -f ejabberd # 跟踪日志
docker compose restart ejabberd # 重启服务
docker compose stop # 停止服务(保留数据)
docker compose start # 再启动
docker compose down # 停止并移除服务(数据目录保留)
docker compose up -d # 重新拉起
进容器执行 ejabberdctl 的两种形态:
# 每条命令显式 exec(本文主线用法)
docker exec ejabberd-26.09-main ejabberdctl status
# 交互式进入后直接执行
docker exec -it ejabberd-26.09-main sh
ejabberdctl status
连接真实 XMPP 客户端做端到端验证:手机/电脑装任意 XMPP 客户端(如 Conversations、Gajim、Pidgin),账号填 admin@localhost、密码 q1w2e3r4、服务器地址填本机 IP、端口 5222,登录后在线人数即 +1:
docker exec ejabberd-26.09-main ejabberdctl stats onlineusers
docker exec ejabberd-26.09-main ejabberdctl connected_users
⚠️ 注意:
localhost域仅供同机/内网验证;要让外部客户端连上,需把HOST宏改为真实域名、配置 TLS 证书(官方支持 ACME 自动申请)并开放防火墙 5222 端口。
九、常见问题(FAQ)
Q1:拉取 ghcr.io/process-one/ejabberd:26.09 报 denied?
实测现象:docker pull ghcr.io/process-one/ejabberd:26.09 返回 Error response from daemon: error from registry: denied。定位:ghcr.io 匿名拉取在部分网络出口会被拒。解决:改用 Docker Hub 官方同步发布的 ejabberd/ecs:26.09(同为 ProcessOne 官方构建,本文主线即用它),或配置可访问 ghcr.io 的代理后重试。
Q2:CTL_ON_CREATE 配置的注册命令没有生效?
实测现象:compose 里配了 CTL_ON_CREATE=register admin localhost q1w2e3r4,启动后 registered_users localhost 仍为空。定位:ecs 入口脚本以「数据库 spool 目录是否存在」判定首次运行([ -d "$SPOOL_DIR" ] || FIRST_RUN=true),bind mount 部署必须预创建 database 目录,于是永远不满足首次运行条件,CTL_ON_CREATE 被跳过。解决:首次部署后手动执行一次 docker exec ejabberd-26.09-main ejabberdctl register admin localhost q1w2e3r4;CTL_ON_START(每次启动都执行)不受此影响,可留作启动自检。
Q3:启动失败,日志报 Mnesia/目录权限错误?
定位:bind mount 的 database/logs/upload 目录属主不是容器内 ejabberd 账号(9000:9000)。解决:宿主执行 chown -R 9000:9000 /srv/ejabberd/{database,logs,upload} 后 docker compose up -d 重启。
Q4:WebAdmin 打开一直 401?
正常现象——WebAdmin 就是 HTTP Basic 认证。用户名必须填完整 JID admin@localhost(只填 admin 会认证失败);若密码遗忘,用 ejabberdctl change_password admin localhost 新密码 重置后重试。
Q5:外部客户端连不上 5222?
按顺序排查:ss -ltn | grep 5222 确认宿主监听 → 防火墙放行 5222/TCP → 客户端服务器地址填宿主 IP 而非 localhost → 域名非 localhost 的部署要确认客户端 JID 域与 hosts 配置一致。
十、卸载与清理
cd /srv/ejabberd
docker compose down # 停止并移除服务与网络,数据目录保留
确认不再需要数据后删除持久化目录:
rm -rf /srv/ejabberd
如需彻底清理镜像:
docker rmi ejabberd/ecs:26.09
十一、参考链接
- 官方部署文档 CONTAINER.md(镜像、端口、环境变量、Compose 示例):https://github.com/processone/ejabberd/blob/master/CONTAINER.md
- 官方 ecs 镜像仓库:https://hub.docker.com/r/ejabberd/ecs/
- 官方 Compose 编排仓库 docker-ejabberd:https://github.com/processone/docker-ejabberd
- ejabberd 26.09 Release(版本来源):https://github.com/processone/ejabberd/releases.atom
- ejabberd 官方文档站:https://docs.ejabberd.im/
- 官方文档防火墙与安全端口表:https://docs.ejabberd.im/admin/guide/security/#firewall-settings
- 同系列配置详解篇:《ejabberd 26.09 配置参数详解》
- 同系列命令详解篇:《ejabberd 常用 CLI 命令详解》