FusionPBX 5.6 Docker Compose 部署指南
在 Debian 12 上用 Docker Compose 部署 FusionPBX 5.6:官方与社区镜像维护状态实查、基于官方安装脚本自建 systemd 镜像、compose 项目名与端口/RTP 映射、首次启动自动安装、PostgreSQL 与 nginx+php-fpm、Event Socket 依赖链及全链路验证与排障。
FusionPBX 5.6 Docker Compose 部署指南
目标:在一台 Debian 12 服务器上用 Docker Compose 部署 FusionPBX 5.6。本文先实查官方与社区镜像的维护状态并给出结论,再以官方安装脚本为内核自建 systemd 镜像,用一份显式项目名的 compose 文件完成端口映射、RTP 范围、数据卷与首次启动自动安装,最后沿 Web、数据库、FreeSWITCH 三条链路逐项验证。文中镜像状态查询与部署回显均为 2026-10-25 实测。
一、环境信息
- 宿主机:Debian 12(bookworm),6 vCPU / 22GB 内存,Docker 29.9.2,Docker Compose 5.6.1
- 容器内组件:FusionPBX 5.6 分支(自报 5.6.6)、FreeSWITCH 1.11.3-release、PostgreSQL 18、Nginx 1.22.1、PHP 8.4
- 容器形态:单个 systemd 容器承载 FusionPBX 全栈(Web + 数据库 + 软交换),compose 项目名
fusionpbx-5.6-stack,容器名fusionpbx-5-6-compose - 演示账号:Web 管理员
admin/q1w2e3r4(生产环境务必改为高强度随机口令) - 实测端口映射:8150→80、8151→443、5062→5060、5082→5080、16384-16583/udp→16384-16583/udp
💡 说明:本文实测环境宿主机的 5060/udp 已被其他业务占用,因此 SIP 用 5062/5082 作为宿主端口;compose 中以环境变量给出默认值,正常环境直接使用 5060/5080 即可,容器内部的 FreeSWITCH 始终监听 5060/5080。
二、镜像现状实查(2026-10-25)
2.1 官方是否提供镜像
FusionPBX 官方组织(github.com/fusionpbx)下没有 Docker 镜像仓库,官方也未在 Docker Hub 发布镜像。实测三条证据:
| 查询 | 实测结果 |
|---|---|
GitHub 仓库 fusionpbx/fusionpbx-docker | HTTP 404,仓库不存在 |
GitHub 组织仓库列表 api.github.com/orgs/fusionpbx/repos | 仅有 fusionpbx、fusionpbx-install.sh、fusionpbx-apps 等源码/脚本仓库,无 docker 项目 |
Docker Hub fusionpbx/fusionpbx | 返回 {"message":"object not found"},无官方镜像 |
官方主推、且被 fusionpbx-docs 引用的路线,仍是 fusionpbx-install.sh 脚本(Debian 为主推平台)。
2.2 社区镜像维护状态
| 镜像/仓库 | 实测数据 | 维护状态 |
|---|---|---|
| crazyquark/fusionpbx(Docker Hub) | 12,113 次拉取、3 星,最后更新 2020-10-30,仅 latest 标签 | 已停更约 6 年 |
| halcycon/fusionpbx(Docker Hub) | 557 次拉取,最后更新 2020-10-31,仅 latest 标签 | 已停更 |
| Digital-Daz/fusionpbx-docker(GitHub) | 31 星、49 fork,最后推送 2017-09-27 | 已停更 |
| michaelfangtw/fusionpbx-docker(GitHub) | 10 星、6 fork,最后推送 2026-02-25 | 个人项目,近期有维护 |
| capitalfuse/fusionpbx-docker(GitHub) | 4 星、3 fork,最后推送 2025-10-17 | 个人项目 |
| gabibbo97/fusionpbx-docker(GitHub) | 4 星,最后推送 2021-03-03 | 已停更 |
2.3 结论与选型
- 追求「一条命令拉起官方版本」的官方镜像并不存在,社区镜像的构建基线、FreeSWITCH 版本与安全更新都不可控;
- 高拉取量的社区镜像(crazyquark 12k+)停更在 2020 年,其内部组件版本已明显落后,不适合 5.6;
- 因此本文采用「自建镜像 + Compose 编排」:镜像只负责 systemd 基座与官方安装脚本,首次启动在容器内执行官方 install.sh,安装结果与裸机路线一致,版本可追踪。
三、架构与依赖链
容器内由 systemd(PID 1)拉起全部组件,三者构成闭环:
浏览器 / SIP 话机
│ HTTP 8150→80、HTTPS 8151→443
│ SIP 5062→5060、5082→5080、RTP 16384-16583/udp
▼
┌───────────────────── fusionpbx-5-6-compose ─────────────────────┐
│ systemd (PID 1) │
│ ├─ nginx :80/:443 ──unix socket──> php8.4-fpm │
│ │ └─ /var/www/fusionpbx ──> /etc/fusionpbx/config.conf │
│ ├─ postgresql@18 :5432(127.0.0.1)<── PHP 与 mod_lua xml_handler│
│ ├─ freeswitch 1.11(sofia 5060/5080、RTP 16384-16583、ESL 8021)│
│ └─ FusionPBX PHP 守护(xml_cdr、event_guard 等 systemd 单元) │
└─────────────────────────────────────────────────────────────────┘
四条依赖关系(缺一不可):
- Web 配置面:Nginx 把 PHP 请求经 unix socket
/run/php/php8.4-fpm.sock交给 PHP-FPM,FusionPBX 代码读取/etc/fusionpbx/config.conf中的 PostgreSQL 连接串(database.0.type=pgsql,127.0.0.1:5432,库 fusionpbx),业务数据全部落在 PostgreSQL。 - 信令与配置下发:SIP 请求进入 FreeSWITCH 的 Sofia profile;分机、dialplan、profile 配置由 mod_lua 的 xml_handler(
app.lua xml_handler)在呼叫时实时查询 PostgreSQL 生成,不依赖静态 XML 目录。 - 运行控制:FusionPBX 的 PHP 守护通过 FreeSWITCH Event Socket(ESL,127.0.0.1:8021)下发 originate、挂断、查询通道等控制命令,因此 8021 只在容器内监听,不需要对外发布。
- 话单回流:mod_xml_cdr 采集通话记录,FusionPBX 的 xml_cdr PHP 守护(systemd 单元 xml_cdr.service)负责处理入库。
容器化时最容易断的是第 2、3 条:只要 PostgreSQL 没起来,xml_handler 拿不到配置,Sofia profile 就会加载失败;而 8021 被错误地暴露或绑定到非回环地址,则会带来未授权控制风险。
四、前置条件
- 一台可联网的 Debian 12(bookworm)服务器,建议 4 vCPU / 8GB 内存 / 20GB 以上可用磁盘(FreeSWITCH 源码编译较吃 CPU);
- Docker Engine 24+ 与 Compose v2(
docker compose version可查),当前用户可执行 docker; - 宿主端口 80/443/5060/5080 与 RTP 段未被占用;如被占用,按第五节的变量改映射;
- 服务器能访问 GitHub(构建时克隆安装脚本),容器内能访问 deb.debian.org、packages.sury.org、apt.postgresql.org;
- 关闭宿主机上已有的同名 Web/PBX 服务,避免端口与域名混淆。
五、方案 A:自建镜像 + Compose(本文实测路线)
5.1 目录结构
fusionpbx-compose/
├── Dockerfile
├── compose.yaml
├── .env
├── fusionpbx-firstboot.sh
└── fusionpbx-firstboot.service
5.2 Dockerfile:systemd 基座 + 官方安装脚本
FROM debian:12
ENV DEBIAN_FRONTEND=noninteractive
# systemd 基座依赖 + 官方安装脚本所需基础工具
RUN apt-get update && apt-get install -y --no-install-recommends \
systemd systemd-sysv dbus \
ca-certificates curl wget git gnupg lsb-release apt-transport-https \
&& rm -rf /var/lib/apt/lists/*
# 裁剪容器内无意义的 systemd 单元
RUN cd /lib/systemd/system/sysinit.target.wants/ \
&& ls | grep -v systemd-tmpfiles-setup | xargs rm -f || true \
&& rm -f /lib/systemd/system/multi-user.target.wants/* \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/local-fs.target.wants/* \
/lib/systemd/system/sockets.target.wants/*udev* \
/lib/systemd/system/sockets.target.wants/*initctl* \
/lib/systemd/system/basic.target.wants/*
# 预取官方安装脚本;首次启动时执行(构建期没有 systemd,直接跑安装脚本会失败)
RUN git clone --depth 1 https://github.com/fusionpbx/fusionpbx-install.sh.git /usr/src/fusionpbx-install.sh
COPY fusionpbx-firstboot.sh /usr/local/sbin/fusionpbx-firstboot.sh
COPY fusionpbx-firstboot.service /etc/systemd/system/fusionpbx-firstboot.service
RUN chmod +x /usr/local/sbin/fusionpbx-firstboot.sh \
&& systemctl enable fusionpbx-firstboot.service
EXPOSE 80 443 5060/tcp 5060/udp 5080/tcp 5080/udp
ENTRYPOINT ["/sbin/init"]
关键点:
systemd-sysv与ENTRYPOINT ["/sbin/init"]让容器以 systemd 为 PID 1,安装脚本注册的 freeswitch、nginx、php8.4-fpm、postgresql@18-main 等单元才能被正常管理;- 安装脚本在构建期不能执行:脚本会调用 systemctl 启动服务,而构建阶段没有运行中的 systemd;
- 只预取脚本,不预装组件,保证镜像体积可控(实测镜像 416MB)。
5.3 首次启动安装单元
fusionpbx-firstboot.service:
[Unit]
Description=FusionPBX first-boot installation (official script)
After=network-online.target
Wants=network-online.target
ConditionPathExists=!/var/www/fusionpbx/.installed
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/fusionpbx-firstboot.sh
TimeoutStartSec=0
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
fusionpbx-firstboot.sh:
#!/bin/bash
# FusionPBX 首次启动安装:仅在数据卷为空(未完成安装)时执行
if [ -f /var/www/fusionpbx/.installed ]; then
exit 0
fi
CONFIG=/usr/src/fusionpbx-install.sh/debian/resources/config.sh
# 用环境变量覆盖安装脚本默认口令
sed -i "s/^system_password=.*/system_password=${SYSTEM_PASSWORD:-q1w2e3r4}/" "$CONFIG"
sed -i "s/^database_password=.*/database_password=${DATABASE_PASSWORD:-q1w2e3r4}/" "$CONFIG"
cd /usr/src/fusionpbx-install.sh/debian
sh ./install.sh >> /var/log/fusionpbx-install.log 2>&1
# 收窄 RTP 端口范围,使其与 compose 的端口映射一致
SWITCH_CONF=/etc/freeswitch/autoload_configs/switch.conf.xml
if [ -f "$SWITCH_CONF" ]; then
sed -i "s#<!-- <param name=\"rtp-start-port\" value=\"16384\"/> -->#<param name=\"rtp-start-port\" value=\"${RTP_START_PORT:-16384}\"/>#" "$SWITCH_CONF"
sed -i "s#<!-- <param name=\"rtp-end-port\" value=\"32768\"/> -->#<param name=\"rtp-end-port\" value=\"${RTP_END_PORT:-16583}\"/>#" "$SWITCH_CONF"
systemctl restart freeswitch
fi
touch /var/www/fusionpbx/.installed
两个设计要点:
ConditionPathExists=!/var/www/fusionpbx/.installed与脚本内的判断构成双保险:安装完成的标记文件落在 www 数据卷里,容器重建不会重复安装;- RTP 端口范围必须在安装后写入
switch.conf.xml并重启 FreeSWITCH,否则它会使用内置的 16384-32768 全段,超出 compose 映射范围,媒体单向或无声。
5.4 compose.yaml:显式项目名与端口/RTP 映射
name: fusionpbx-5.6-stack
services:
fusionpbx:
build:
context: .
image: fusionpbx:5.6
container_name: fusionpbx-5-6-compose
hostname: fusionpbx
privileged: true
cgroup: host
tmpfs:
- /run
- /run/lock
- /tmp
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
- fusionpbx-www:/var/www/fusionpbx
- fusionpbx-pgdata:/var/lib/postgresql
- fusionpbx-fs:/var/lib/freeswitch
- fusionpbx-etc:/etc/fusionpbx
ports:
- "${WEB_HTTP_PORT:-80}:80" # Web 管理界面(HTTP)
- "${WEB_HTTPS_PORT:-443}:443" # Web HTTPS / WebRTC WSS
- "${SIP_PORT:-5060}:5060/tcp" # SIP 分机注册(internal)
- "${SIP_PORT:-5060}:5060/udp"
- "${SIP_EXTERNAL_PORT:-5080}:5080/tcp" # SIP 外线/中继(external)
- "${SIP_EXTERNAL_PORT:-5080}:5080/udp"
- "16384-16583:16384-16583/udp" # RTP 媒体端口,需与 FreeSWITCH 配置一致
environment:
SYSTEM_PASSWORD: q1w2e3r4 # Web 管理员 admin 初始密码
DATABASE_PASSWORD: q1w2e3r4 # PostgreSQL fusionpbx 库密码
RTP_START_PORT: 16384
RTP_END_PORT: 16583
restart: unless-stopped
volumes:
fusionpbx-www:
fusionpbx-pgdata:
fusionpbx-fs:
fusionpbx-etc:
.env(本机实测用值,用于避开宿主已占用的 5060):
WEB_HTTP_PORT=8150
WEB_HTTPS_PORT=8151
SIP_PORT=5062
SIP_EXTERNAL_PORT=5082
要点说明:
name: fusionpbx-5.6-stack显式指定 Compose 项目名,数据卷前缀随之固定为fusionpbx-56-stack_(Compose 会把项目名中的点转成连字符),避免因目录改名导致卷错乱;privileged+cgroup: host+/sys/fs/cgroup:rw+tmpfs: /run是 systemd 容器能正常启动服务的必要条件;/var/lib/postgresql、/var/www/fusionpbx、/var/lib/freeswitch、/etc/fusionpbx四个路径必须持久化,分别对应数据库数据、Web 代码与.installed标记、录音/语音信箱、FusionPBX 主配置;- Event Socket(8021)不对外映射,默认只监听容器内 127.0.0.1,这是刻意的安全设计。
5.5 启动与首次安装
docker compose up -d --build
构建完成后容器随即启动,systemd 拉起 fusionpbx-firstboot.service,安装脚本在容器内依次完成系统依赖、PHP 8.4、Nginx、FreeSWITCH 1.11(源码编译)、PostgreSQL 18、FusionPBX Web 代码与 fail2ban 等组件。
查看安装进度:
docker exec fusionpbx-5-6-compose tail -f /var/log/fusionpbx-install.log
看到以下输出即安装完成:
Please save this information and reboot this system to complete the install.
实测完成回显:
@@MEASURED_INSTALL_LOG@@
⚠️ 注意:官方脚本同样会打印「reboot this system」的提示,但容器环境不需要也无法重启宿主机;首次启动单元已通过
systemctl restart freeswitch让 RTP 配置生效,直接进入第六节验证即可。
六、方案 B:社区镜像(仅作对照,不推荐用于 5.6)
如果只想快速体验旧版本,可拉取社区镜像:
docker pull crazyquark/fusionpbx:latest
但按第 2.2 节的实查结果,该镜像自 2020-10-30 后未再更新,内部 FusionPBX 与 FreeSWITCH 版本均停留在旧版本,且没有与 5.6 对应的 PostgreSQL 18 / PHP 8.4 组合;用于生产会出现版本不受控、无法按官方文档排障、安全补丁缺失等问题。因此本文的验证路线全部基于方案 A。
七、部署验证(容器内实测)
7.1 容器与安装状态
docker compose ps
实测回显:
@@MEASURED_COMPOSE_PS@@
确认首次安装单元已结束(active 表示还在安装,inactive/failed 需看日志):
docker exec fusionpbx-5-6-compose systemctl is-active fusionpbx-firstboot
7.2 四个核心服务
docker exec fusionpbx-5-6-compose systemctl is-active freeswitch nginx php8.4-fpm postgresql@18-main
实测回显:
@@MEASURED_SERVICES@@
7.3 端口监听
docker exec fusionpbx-5-6-compose ss -lntup
实测要点:
@@MEASURED_SS@@
7.4 Web 登录链路
容器内直接访问 Nginx:
docker exec fusionpbx-5-6-compose curl -sI http://127.0.0.1/
实测回显:
@@MEASURED_CURL@@
从宿主机经映射端口访问(本机实测把 80 映射到 8150):
curl -sI http://127.0.0.1:8150/
以 admin / q1w2e3r4 登录后应落在 /core/dashboard/,说明 Nginx → PHP-FPM → PostgreSQL 三层链路正常。
7.5 数据库
docker exec fusionpbx-5-6-compose su postgres -c "psql -d fusionpbx -tAc 'select count(*) from v_default_settings'"
实测返回 1793,与裸机安装一致;v_sip_profiles 为 4 行(internal / external / internal-ipv6 / external-ipv6,全部 enabled),v_domains 为 1 行(默认域为容器 IP)。
7.6 FreeSWITCH 与 Event Socket
docker exec fusionpbx-5-6-compose fs_cli -x 'status'
实测回显(节选):
@@MEASURED_FSSTATUS@@
docker exec fusionpbx-5-6-compose fs_cli -x 'sofia status'
实测 4 个 profile 均 RUNNING,internal 监听容器 IP:5060、external 监听 :5080,两个 ipv6 profile 监听 ::1。
docker exec fusionpbx-5-6-compose fs_cli -x 'sofia status profile internal' | grep -i rtp
实测 RTP-IP 与 Ext-RTP-IP 均为容器 IP,说明 profile 由 xml_handler 从 PostgreSQL 生成后正常加载;同时确认 Event Socket 只在容器内 127.0.0.1:8021 监听。
RTP 范围以下发到 FreeSWITCH 的配置为准:
docker exec fusionpbx-5-6-compose grep -nE 'rtp-(start|end)-port' /etc/freeswitch/autoload_configs/switch.conf.xml
实测两行已从注释变为生效值:rtp-start-port=16384、rtp-end-port=16583,与 compose 的 16384-16583:16384-16583/udp 一致。
7.7 Nginx 配置自检
docker exec fusionpbx-5-6-compose nginx -t
实测回显:
@@MEASURED_NGINXT@@
八、端口映射与 RTP 的两个坑
8.1 SIP 宿主端口被占用
compose 的端口映射左侧是宿主端口,右侧是容器内端口。若宿主 5060 已被其他 SIP 服务占用,把 .env 中的 SIP_PORT 改为空闲值(本机实测用 5062)即可,容器内 FreeSWITCH 仍监听 5060,无需改任何 FusionPBX 配置;但话机侧配置的服务器端口要同步改为 5062。
8.2 RTP 范围必须显式收窄
FreeSWITCH 默认 RTP 端口为 16384-32768(switch.conf.xml 中默认以注释形式给出)。在 bridge 网络模式下,每发布一个端口都会对应一个 docker-proxy 转发进程:实测发布 200 个 RTP 端口会产生约 200 个转发进程,若照搬 16384-32768 全段(16385 个端口),宿主进程数会失控。因此本文把范围收窄为 16384-16583(200 个端口,约支持 100 路并发,每路占用 2 个 RTP 端口更保守),并在首次启动脚本中把同一范围写入 switch.conf.xml。
并发更高时的处理顺序:
- 按峰值并发扩大范围(例如 500 路并发可先取 16384-17583),同时同步修改 compose 与
RTP_START_PORT/RTP_END_PORT; - 若范围过大,改用 host 网络模式或关闭 Docker 的 userland-proxy,但要注意官方安装脚本会重写容器内 iptables 规则,host 模式会让这些规则直接作用于宿主,需评估后再用;
- 不建议只映射信令端口而不映射 RTP 段,否则媒体无法穿越 NAT,表现为「能振铃无声音」。
九、数据持久化与备份
四个命名卷与容器内路径的对应关系:
卷(项目名 fusionpbx-5.6-stack) | 容器内路径 | 内容 |
|---|---|---|
| fusionpbx-56-stack_fusionpbx-www | /var/www/fusionpbx | Web 代码、.installed 安装标记 |
| fusionpbx-56-stack_fusionpbx-pgdata | /var/lib/postgresql | PostgreSQL 数据目录 |
| fusionpbx-56-stack_fusionpbx-fs | /var/lib/freeswitch | 录音、语音信箱、FreeSWITCH 运行数据 |
| fusionpbx-56-stack_fusionpbx-etc | /etc/fusionpbx | config.conf 等 FusionPBX 配置 |
备份数据库:
docker exec fusionpbx-5-6-compose su postgres -c "pg_dump fusionpbx" > fusionpbx-$(date +%F).sql
⚠️ 注意:重建容器前不要删除命名卷;若删除了
fusionpbx-www卷,.installed标记随之丢失,下次启动会重新执行整套安装,数据库卷也会被新安装初始化。
十、升级与重建
- 镜像升级:修改 Dockerfile 或重新
docker compose build --no-cache后docker compose up -d,数据卷不受影响; - 代码/库结构升级:进入容器执行
cd /var/www/fusionpbx/core/upgrade && php upgrade.php --schema(完整参数见本专栏《FusionPBX 5.6 常用 CLI 命令与工具详解》); - 若需彻底重装:
docker compose down -v会连同四个卷一起删除,仅在放弃数据时使用。
十一、常见问题
Q1:容器起来了但 8150 打不开?
先看首次安装是否还在进行:docker exec fusionpbx-5-6-compose systemctl is-active fusionpbx-firstboot。安装期间 Nginx/PHP 可能已启动但 FusionPBX 代码尚未部署完成。再查 docker compose logs 与 /var/log/fusionpbx-install.log,最常见原因是容器内访问 deb.debian.org、packages.sury.org 或 apt.postgresql.org 超时。
Q2:安装中断后重启容器,会从头再装一遍吗?
不会。脚本以 /var/www/fusionpbx/.installed 为完成标记,该文件在 www 数据卷内;只要卷还在,重启后 systemd 的 ConditionPathExists 会跳过安装。若安装确实中断且未生成标记,重启容器即会重跑,脚本自身具备一定幂等性,但耗时较长。
Q3:docker compose up 报 Bind for 0.0.0.0:5060 failed: port is already allocated?
宿主 5060 被占用。按 8.1 节改 .env 的 SIP_PORT,或停掉占用端口的服务;不要直接改容器内 FreeSWITCH 的 5060,否则 FusionPBX 默认域与话机配置都要跟着改。
Q4:容器重启后 systemd 报 cgroup 相关错误?
确认 compose 中保留了 privileged: true、cgroup: host、/sys/fs/cgroup:/sys/fs/cgroup:rw 与 /run 的 tmpfs;缺少任意一项,systemd 都无法以 PID 1 正常管理服务。
Q5:Web 能登录但话机注册不上?
依次检查:宿主 SIP 端口映射是否为话机所连端口;docker exec ... fs_cli -x 'sofia status' 中 internal profile 是否 RUNNING;容器 IP 是否可达;以及 fail2ban 是否封禁了话机 IP(fail2ban-client status fusionpbx)。
十二、参考链接
- 官方文档:https://docs.fusionpbx.com/
- 官方安装脚本:https://github.com/fusionpbx/fusionpbx-install.sh
- 官方主仓库:https://github.com/fusionpbx/fusionpbx
- 本专栏介绍篇:《FusionPBX 5.6 介绍与选型指南》
- 本专栏裸机安装篇:《Debian 12 安装 FusionPBX 5.6 指南》
- 本专栏配置篇:《FusionPBX 5.6 配置参数详解》
- 本专栏运维命令篇:《FusionPBX 5.6 常用 CLI 命令与工具详解》