FreePBX 17 Docker Compose 部署指南

Docker Compose 部署 FreePBX 17 实战:escomputers/freepbx:17-nofail2ban + MariaDB 10.11 双容器编排(栈 freepbx-17-stack)、php install 数据库初始化、Web 向导、sipsettings/bulkhandler 补装、ionCube 加载器安装、分机批量导入与 sipsak 注册验证、三卷持久化与等价 docker run(2026-10-26 实测)。

FreepbxFreePBXAsterisk2026/10/26

FreePBX 17 Docker Compose 部署指南

目标:用 Docker Compose 一键拉起 FreePBX 17 + Asterisk + MariaDB 双容器栈,完成数据库初始化、Web 向导、模块补装、ionCube 加载器安装与 SIP 分机注册验证。全流程已在 Docker Engine 29.9.2 + Compose v5.6.1 环境实测通过(栈名 freepbx-17-stack)。

一、环境信息

  • 宿主操作系统:Linux x86_64(实测 Debian 12 bookworm)。
  • Docker Engine:29.9.2(实测 docker --version → Docker version 29.9.2, build a82e528)。
  • Docker Compose:v5.6.1(实测 docker compose version → Docker Compose version v5.6.1)。
  • 镜像清单(实测):
    • escomputers/freepbx:17-nofail2ban(3.21GB)——FreePBX 17 全家桶;
    • mariadb:10.11(459MB)——数据库。
  • Compose 栈名:freepbx-17-stack;容器名 freepbx-17-stack-freepbx-1、freepbx-17-stack-db-1(Compose 按「栈名-服务名-序号」自动生成)。
  • 专用网络:freepbx-17-net(10.89.72.0/24,固定 IP:db=10.89.72.30、freepbx=10.89.72.40);SIP 测试客户端 10.89.72.50。
  • 宿主端口映射:8137→80(Web HTTP)、8140→443(HTTPS)、5062→5060/udp(SIP 信令)、16384-16484/udp(RTP 媒体)。
  • 容器内软件版本(实测):FreePBX framework 17.0.33.2(模块在线升级后)、Asterisk 21.10.2(镜像自带)、PHP 8.2.31、MariaDB 10.11、Apache 2.4.65、NodeJS 18.20.4。

二、镜像选型说明

FreePBX 官方不提供容器镜像,社区镜像中escomputers/freepbx维护活跃、开箱即用。选型对照如下:

维度escomputers/freepbxtiredofit/freepbx
基础系统debian:bookworm-slimdebian(s6-overlay 多进程管理)
内置组件Asterisk + FreePBX + PHP + Apache + MariaDB 客户端Asterisk + FreePBX + PHP + Apache(数据库外置)
定制性单一 entrypoint,结构简单环境变量驱动,可配置项多、较复杂
上手成本低(适合快速验证/中小部署)中高(适合需要深度定制的场景)
实测结论本文采用,17 全家桶完整可用未深入实测

选择 escomputers/freepbx 的理由:镜像把 LAMP + Asterisk + FreePBX 源码(/usr/local/src/freepbx)全部打齐,entrypoint 脚本(/usr/local/src/entrypoint.sh)负责拉起 cron、postfix、Asterisk 与 Apache,数据库初始化步骤清晰可控。标签选择 17-nofail2ban(不带 fail2ban 的 17 变体),安全防护交给宿主层统一做,容器内组件更精简。

💡 说明:该镜像的组件版本以镜像发布时为准(README 标注 FreePBX 17.0.21 / Asterisk 21.10.2 / MariaDB 10.11.14 / Apache 2.4.65 / NodeJS 18.20.4)。FreePBX 框架版本可通过 fwconsole ma upgradeall 在线升级到模块市场最新(实测升级到 17.0.33.2);Asterisk 大版本跟随镜像,容器内不能换。

镜像不支持的组件:DAHDI 通道(PCI 语音卡直通场景不适用,需物理机安装,见 《Debian 12 安装 FreePBX 17 指南》)。

三、前置条件

  1. Docker 与 Compose 插件已安装:
docker --version
docker compose version
Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
  1. 创建专用外部网络(固定 IP 需要;不固定 IP 可省略此步并把 compose 里的网络定义改为普通内部网络):
docker network create freepbx-17-net --subnet 10.89.72.0/24

创建成功会输出网络 ID,并可用 docker network ls | grep freepbx-17-net 复核。

  1. 规划部署目录(compose 与配置文件集中存放):
mkdir -p /opt/freepbx-17-stack && cd /opt/freepbx-17-stack
  1. 端口占用检查(8137/8140/5062/16384-16484 需空闲):
ss -lntup | grep -E ':(8137|8140|5062)\b'
(无输出即端口空闲)

四、部署步骤

4.1 编写 docker-compose.yaml

在 /opt/freepbx-17-stack/docker-compose.yaml 写入(与实测文件一致):

name: freepbx-17-stack

volumes:
  mysql_data:
  var_data:
  etc_data:

networks:
  defaultnet:
    external: true
    name: freepbx-17-net

services:
  db:
    restart: always
    image: mariadb:10.11
    environment:
      MARIADB_ROOT_PASSWORD: q1w2e3r4
      MARIADB_DATABASE: asterisk
      MARIADB_USER: freepbxuser
      MARIADB_PASSWORD: q1w2e3r4
    volumes:
      - mysql_data:/var/lib/mysql
      - ./my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    networks:
      defaultnet:
        ipv4_address: 10.89.72.30

  freepbx:
    restart: always
    image: escomputers/freepbx:17-nofail2ban
    volumes:
      - var_data:/var
      - etc_data:/etc
    networks:
      defaultnet:
        ipv4_address: 10.89.72.40
    ports:
      - "8137:80/tcp"
      - "8140:443/tcp"
      - "5062:5060/udp"
      - "16384-16484:16384-16484/udp"
    depends_on:
      - db

4.2 编写 MariaDB 配置与初始化 SQL

my.cnf(FreePBX 对 SQL 模式有要求,必须放宽默认严格模式):

[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION

💡 说明:MariaDB 默认 sql_mode 包含 STRICT_TRANS_TABLES 等严格项,FreePBX 的模块安装与 CDR 写入对空值/隐式转换敏感,遗留严格模式会报错。NO_ENGINE_SUBSTITUTION 是 FreePBX 官方安装脚本实际采用的 SQL 模式,容器场景同样适用。文件名带 zz- 前缀保证它在 MariaDB 官方镜像的默认配置之后加载。

init.sql(首次初始化时建库并授权):

CREATE DATABASE IF NOT EXISTS asterisk;
CREATE DATABASE IF NOT EXISTS asteriskcdrdb;
GRANT ALL PRIVILEGES ON `asterisk`.* TO 'freepbxuser'@'%';
GRANT ALL PRIVILEGES ON `asteriskcdrdb`.* TO 'freepbxuser'@'%';
FLUSH PRIVILEGES;

⚠️ 注意:init.sql 挂载到 MariaDB 官方镜像的 /docker-entrypoint-initdb.d/,仅在 mysql_data 卷为空(首次启动)时执行。复用旧卷重建栈时不会重跑,需按十、10.4 节手动补授权。

4.3 启动栈

docker compose up -d

实测状态:

docker compose ps
NAME                         IMAGE                               COMMAND                  SERVICE   CREATED       STATUS       PORTS
freepbx-17-stack-db-1        mariadb:10.11                       "docker-entrypoint.s…"   db        3 hours ago   Up 3 hours   3306/tcp
freepbx-17-stack-freepbx-1   escomputers/freepbx:17-nofail2ban   "/usr/bin/env bash /…"   freepbx   3 hours ago   Up 2 hours   0.0.0.0:16384-16484->16384-16484/udp, [::]:16384-16484->16384-16484/udp, 0.0.0.0:8137->80/tcp, [::]:8137->80/tcp, 0.0.0.0:8140->443/tcp, [::]:8140->443/tcp, 0.0.0.0:5062->5060/udp, [::]:5062->5060/udp

验证数据库就绪:

docker exec freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 -e 'show databases;'
Database
asterisk
asteriskcdrdb
information_schema
mysql
performance_schema
sys

asterisk 与 asteriskcdrdb 两个库已由 init.sql 建好,即可进入关键的第 4.4 步。

4.4 FreePBX 数据库初始化(关键步骤)

镜像的 entrypoint 不会自动安装 FreePBX 数据库(entrypoint 只拉起 cron/postfix/Asterisk/Apache),首次部署必须手动执行框架安装。进入容器,在框架源码目录执行:

docker exec -it freepbx-17-stack-freepbx-1 bash -c 'cd /usr/local/src/freepbx && php install -n --dbuser=freepbxuser --dbpass=q1w2e3r4 --dbhost=db'

参数说明:-n 非交互模式;--dbuser/--dbpass 使用 init.sql 授权的 freepbxuser;--dbhost 用 Compose 网络中的 db 服务名(固定 IP 场景写 10.89.72.30 亦可)。

执行完成后验证框架可用(实测回显):

docker exec freepbx-17-stack-freepbx-1 fwconsole --version
FW Console - FreePBX Utility 17.0.33.2
docker exec freepbx-17-stack-freepbx-1 asterisk -V
Asterisk 21.10.2

⚠️ 注意:此时 Web 界面已可访问但尚未创建管理员账号,需先完成 4.5 的初始化向导。

4.5 Web 初始化向导(创建 admin 账号)

浏览器访问 http://<宿主IP>:8137/,首次进入强制进入初始化向导,按表单填写(与裸机安装的向导一致):

表单字段说明实测填写值
Username管理员用户名admin
Password1 / Password2管理员密码(两次一致)q1w2e3r4
Email管理员邮箱admin@example.lan
System Identifier系统标识pbx-compose-01
Automatic Module Updates自动模块更新Disabled(生产建议手动)
Automatic Security Updates安全更新自动安装Enabled
Update Every / Update Period更新检查周期/时段Daily / Nightly

提交后进入管理后台。验证账号落库(实测回显):

docker exec freepbx-17-stack-db-1 mariadb -u freepbxuser -pq1w2e3r4 asterisk -e 'select username from ampusers;'
username
admin

4.6 补装批量导入等模块

该镜像内置模块为精简开源集(实测 fwconsole ma list | grep -c Enabled 仅 14 个),批量导入分机所需的 bulkhandler 与 SIP 参数配置所需的 sipsettings 均需在线补装(需可访问 mirror.freepbx.org):

docker exec -it freepbx-17-stack-freepbx-1 fwconsole ma downloadinstall sipsettings
docker exec -it freepbx-17-stack-freepbx-1 fwconsole ma downloadinstall bulkhandler
docker exec freepbx-17-stack-freepbx-1 fwconsole reload

💡 说明:downloadinstall 会自动解析并安装依赖——sipsettings 会连带装上 filestore 与 backup(实测 sipsettings 17.0.6.10、filestore 17.0.3.2、backup 17.0.13),bulkhandler 实测装到 17.0.7.1。安装中途卡住多为到 mirror.freepbx.org 的网络不稳,重跑同一命令即可续装。

补装后的模块版本抽样(实测回显):

| backup           | 17.0.13    | Enabled                           | GPLv3+  | Sangoma   |
| bulkhandler      | 17.0.7.1   | Enabled                           | GPLv3+  | Sangoma   |
| core             | 17.0.18.54 | Enabled                           | GPLv3+  | Sangoma   |
| filestore        | 17.0.3.2   | Enabled                           | AGPLv3  | Sangoma   |
| framework        | 17.0.33.2  | Enabled                           | GPLv2+  | Sangoma   |
| sipsettings      | 17.0.6.10  | Enabled                           | AGPLv3+ | Sangoma   |

4.7 安装 ionCube 加载器(可选但建议)

镜像未内置 ionCube Loader,未安装时部分依赖加密的模块不可用。安装步骤(实测):

cd /tmp
wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
tar -xzf ioncube_loaders_lin_x86-64.tar.gz

拷贝 PHP 8.2 对应的 loader 进容器(PHP 8.2 的扩展目录为 /usr/lib64/php/20220829):

docker cp ioncube/ioncube_loader_lin_8.2.so freepbx-17-stack-freepbx-1:/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so

写入 CLI 与 Apache 两套 PHP 的加载配置:

docker exec freepbx-17-stack-freepbx-1 bash -c 'echo "zend_extension=/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so" > /etc/php/8.2/cli/conf.d/00-ioncube.ini && echo "zend_extension=/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so" > /etc/php/8.2/apache2/conf.d/00-ioncube.ini'

重启 freepbx 服务使 loader 生效,并验证:

docker compose restart freepbx
docker exec freepbx-17-stack-freepbx-1 php -m | grep -i ioncube
ionCube Loader
the ionCube PHP Loader

loader 文件校验(实测 sha256):

27563b15c28e63800c74defe39fad06229b68267734187bf088f47a9170c985e  ioncube_loader_lin_8.2.so

⚠️ 注意:docker cp 的文件位于容器可写层、不在数据卷内,容器重建(docker compose down 后重新 up)会丢失,需重做本节(根治方案见十一、11.5)。

4.8 创建分机并验证 SIP 注册

用 bulkhandler 批量导入分机 2001:

docker exec freepbx-17-stack-freepbx-1 bash -c 'printf "extension,name,tech,secret,voicemail,ringtimer,noanswer,recording,outboundcid,sipname,id,devicetype,user,description\n2001,Sales-01,pjsip,q1w2e3r4,disabled,0,,inherit,,2001,,fixed,,Sales desk extension\n" > /tmp/ext.csv'
docker exec -it freepbx-17-stack-freepbx-1 fwconsole bulkimport --type=extensions --replace /tmp/ext.csv
Importing bulk extensions

确认端点生成:

docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show endpoints' | grep 'Endpoint:.*2001'
 Endpoint:  2001/2001                                            Unavailable   0 of inf

在测试客户端用 sipsak 发起注册(客户端 IP 10.89.72.50,PBX 容器 IP 10.89.72.40):

sipsak -U -s sip:2001@10.89.72.40 -H 10.89.72.50 --auth-username=2001 --password=q1w2e3r4

PBX 侧确认注册落地(实测回显):

docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show contacts'

  Contact:  <Aor/ContactUri..............................> <Hash....> <Status> <RTT(ms)..>
  Contact:  2001/sip:2001@10.89.72.50:55571                ce7f0fc0b2 NonQual         nan
Objects found: 1

注册链路打通,Compose 部署全流程完成。

五、数据持久化说明

栈使用三个命名卷(docker volume ls 实测输出):

local     freepbx-17-stack_etc_data
local     freepbx-17-stack_mysql_data
local     freepbx-17-stack_var_data
卷挂载点(容器内)存放内容重要性
freepbx-17-stack_mysql_data/var/lib/mysql两个业务库(asterisk 配置数据 + asteriskcdrdb 通话记录)最高(删库即丢全部配置与话单)
freepbx-17-stack_etc_data/etcAsterisk 与 FreePBX 全部配置文件高(可用 fwconsole 从库重建,但耗时)
freepbx-17-stack_var_data/var录音、话单 spool、日志、模块二进制、cron 数据高(含录音/话单原始文件)

要点:

  • 三个卷均为命名卷,docker compose down 不删除卷;docker compose down -v 会连数据一起删除,生产环境禁止随手加 -v。
  • ionCube loader 与手工放进容器的其他文件(如 /tmp/ext.csv)不在卷内,重建即丢。
  • 备份示例(配置库 + 通话记录库导出到宿主当前目录):
docker exec freepbx-17-stack-db-1 mariadb-dump -u root -pq1w2e3r4 --databases asterisk asteriskcdrdb > freepbx-db-backup.sql

六、环境变量表

仅 db 服务使用环境变量(mariadb 官方镜像约定),freepbx 服务的 entrypoint 不读取任何环境变量:

变量实测值说明
MARIADB_ROOT_PASSWORDq1w2e3r4root 密码,运维/备份用
MARIADB_DATABASEasterisk首次初始化自动创建的库名(init.sql 亦会创建,双保险)
MARIADB_USERfreepbxuser业务账号(FreePBX 连库用)
MARIADB_PASSWORDq1w2e3r4业务账号密码,需与 4.4 节 php install --dbpass 传值一致

⚠️ 注意:修改 MARIADB_* 环境变量对已初始化的卷不生效(root 密码、用户只在首次建库时写入),改完需清卷重建或手动在库内改密。

七、配置参数说明

7.1 compose 关键参数逐项

参数取值说明
namefreepbx-17-stack显式指定栈名,决定容器/卷/网络资源命名前缀,避免随目录名漂移
restart: always两服务均开启宿主重启或进程崩溃后自动拉起
networks.defaultnet.externalfreepbx-17-net复用预建外部网络并固定 IP;固定 IP 不是必需项,删去 ipv4_address 后走 DHCP 亦可(服务名 db 的 DNS 解析不受影响)
depends_onfreepbx 依赖 db只保证启动顺序(db 先起),不保证 MariaDB 已就绪——php install 失败时等几秒重试即可
ports见 7.2Web/SIP/RTP 三类端口映射
volumes三卷 + 两个 bind 挂载见第八章

7.2 端口映射与 RTP 范围

映射用途说明
8137:80/tcpWeb 管理台 HTTP浏览器访问 http://宿主IP:8137
8140:443/tcpWeb 管理台 HTTPS镜像自带自签证书,浏览器会告警
5062:5060/udpSIP 信令宿主侧用 5062 避让宿主上可能存在的其他 PBX
16384-16484:16384-16484/udpRTP 媒体见下方说明

RTP 范围说明(实测):容器内 Asterisk 默认 RTP 范围为 10000-20000(asterisk -rx 'rtp show settings' 输出 Port start: 10000 / Port end: 20000)。本次映射只开放 16384-16484 共 101 个 UDP 端口,原因是 Docker 大段 UDP 端口映射会生成海量转发规则、消耗宿主资源(镜像 README 引用的上游问题 moby#11185)。要让媒体流完全走通宿主 NAT,应在 FreePBX Web 界面 Settings → Asterisk SIP Settings → RTP Settings 中把 RTP Port Start/End 改为 16384/16484 并 Apply Config,使实际媒体范围与映射段一致。

7.3 SQL 模式

my.cnf 中 sql_mode=NO_ENGINE_SUBSTITUTION 与 FreePBX 官方安装脚本一致,避免严格模式导致模块安装与 CDR 写入报错(详见 4.2 节说明)。

八、配置文件挂载

挂载方向作用
./my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro宿主→db 容器注入 FreePBX 兼容的 SQL 模式;zz- 前缀保证加载顺序在镜像默认配置之后
./init.sql:/docker-entrypoint-initdb.d/init.sql:ro宿主→db 容器首次建库授权;仅空卷首启执行(见 4.2 节 ⚠️)
mysql_data:/var/lib/mysql命名卷数据库文件持久化
var_data:/var命名卷freepbx 容器数据持久化
etc_data:/etc命名卷freepbx 容器配置持久化

💡 说明:/etc、/var 整目录挂卷是 FreePBX 容器化的事实做法——FreePBX 的运行时状态分散在 /etc/asterisk、/var/lib/asterisk、/var/spool/asterisk、/var/www/html 等处,整目录挂卷比逐个子目录挂载可靠。ionCube loader 位于 /usr/lib64/php/(不在上述卷内),是唯一需要重建后重做的文件。

九、等价 docker run

不用 Compose 时,以下两条 docker run 与 compose 定义完全等价(逐条给出,先 db 后 freepbx):

docker network create freepbx-17-net --subnet 10.89.72.0/24

docker run -d --name freepbx-17-stack-db-1 \
  --restart always \
  --network freepbx-17-net --ip 10.89.72.30 \
  -e MARIADB_ROOT_PASSWORD=q1w2e3r4 \
  -e MARIADB_DATABASE=asterisk \
  -e MARIADB_USER=freepbxuser \
  -e MARIADB_PASSWORD=q1w2e3r4 \
  -v freepbx-17-stack_mysql_data:/var/lib/mysql \
  -v /opt/freepbx-17-stack/my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro \
  -v /opt/freepbx-17-stack/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
  mariadb:10.11

docker run -d --name freepbx-17-stack-freepbx-1 \
  --restart always \
  --network freepbx-17-net --ip 10.89.72.40 \
  -p 8137:80/tcp -p 8140:443/tcp \
  -p 5062:5060/udp \
  -p 16384-16484:16384-16484/udp \
  -v freepbx-17-stack_var_data:/var \
  -v freepbx-17-stack_etc_data:/etc \
  escomputers/freepbx:17-nofail2ban

要点:--ip 固定 IP 依赖 --network 指向的用户自定义网络;两个卷需先 docker volume create(或省略让 Docker 自动创建)。后续的 4.4-4.8 步骤与 Compose 场景完全相同。

十、常用运维命令

10.1 日常状态与日志

docker compose ps                    # 栈内容器状态
docker compose logs -f freepbx       # 跟踪 freepbx 容器日志(Apache/Asterisk 启动输出)
docker compose restart freepbx       # 重启 Web/PBX 容器(ionCube 安装后必做)
docker compose down                  # 停栈(保留三个数据卷)

10.2 容器内 fwconsole 操作

FreePBX 全部 CLI 能力都在 freepbx 容器内:

docker exec freepbx-17-stack-freepbx-1 fwconsole --version
docker exec freepbx-17-stack-freepbx-1 fwconsole ma list          # 模块清单
docker exec freepbx-17-stack-freepbx-1 fwconsole ma upgradeall    # 模块在线升级
docker exec freepbx-17-stack-freepbx-1 fwconsole reload           # 应用配置
docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show endpoints'

10.3 数据库备份与恢复

# 备份(配置库 + 通话记录库)
docker exec freepbx-17-stack-db-1 mariadb-dump -u root -pq1w2e3r4 --databases asterisk asteriskcdrdb > freepbx-db-backup.sql

# 恢复
docker exec -i freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 < freepbx-db-backup.sql

10.4 复用旧卷时手动补授权

栈重建但卷保留时,init.sql 不会重跑;若此时新换了 MARIADB_USER/MARIADB_PASSWORD,需手动补授权:

docker exec freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 -e "GRANT ALL PRIVILEGES ON \`asterisk\`.* TO 'freepbxuser'@'%' IDENTIFIED BY 'q1w2e3r4'; GRANT ALL PRIVILEGES ON \`asteriskcdrdb\`.* TO 'freepbxuser'@'%' IDENTIFIED BY 'q1w2e3r4'; FLUSH PRIVILEGES;"

10.5 数据卷备份

docker run --rm -v freepbx-17-stack_var_data:/data:ro -v /opt/freepbx-17-stack:/backup alpine tar czf /backup/var-data.tar.gz -C /data .

十一、常见问题(FAQ)

11.1 php install 报数据库连接失败

现象:4.4 节的 php install -n --dbuser=freepbxuser --dbpass=q1w2e3r4 --dbhost=db 报连不上 db 或授权被拒。排查顺序:

  1. docker compose ps 确认 db 容器 Up;
  2. docker exec freepbx-17-stack-db-1 mariadb -u freepbxuser -pq1w2e3r4 -e 'select 1;' 验证业务账号可用;
  3. 若报 Access denied,多半是 mysql_data 卷非空导致 init.sql 未执行(见 10.4 节手动补授权),或 MARIADB_PASSWORD 与 php install 的 --dbpass 不一致。

11.2 Web 界面部分模块报 ionCube 相关错误

现象:模块市场或部分模块页面提示 ionCube/loader 缺失。原因:镜像未内置 ionCube Loader(实测 php -m | grep -i ioncube 无输出)。处理:按 4.7 节安装 loader 并 docker compose restart freepbx,复查 php -m 出现 ionCube Loader 即恢复。

11.3 手改 /etc/asterisk 下的配置文件被自动覆盖

现象:直接编辑 /etc/asterisk/rtp_additional.conf 等文件,fwconsole reload 后改动丢失,文件恢复为 FreePBX 生成的值。实测该文件头部即有声明:

;          Do NOT edit this file as it is auto-generated by FreePBX.             ;

原因:FreePBX 17 的模块(BMO 架构)以数据库中的设置为准,reload 时自动重生成这些配置文件。处理:改动必须走 Web 界面对应模块(如 RTP 范围在 Settings → Asterisk SIP Settings → RTP Settings),修改后 Apply Config。确需追加自定义参数时使用 FreePBX 的自定义文件(如 extensions_custom.conf、pjsip_custom.conf),这类文件不受重生成影响。

11.4 SIP 注册成功但通话无音频/单向音频

原因:RTP 媒体端口范围与宿主映射段不一致(容器默认 10000-20000,仅映射了 16384-16484),超出映射段的媒体包被宿主丢弃。处理:按 7.2 节把 FreePBX 的 RTP Port Start/End 改为 16384/16484 并 Apply Config;或者反过来扩大映射段覆盖实际 RTP 范围(注意大段 UDP 映射的资源开销,见 7.2 节)。

11.5 容器重建后 ionCube 又丢了

原因:loader 经 docker cp 写入容器可写层,不在 /var、/etc 数据卷内,docker compose down && up 即丢。根治方案:基于该镜像构建自己的镜像层,把 loader 与 ini 固化进去:

FROM escomputers/freepbx:17-nofail2ban
COPY ioncube_loader_lin_8.2.so /usr/lib64/php/20220829/ioncube_loader_lin_8.2.so
COPY 00-ioncube.ini /etc/php/8.2/cli/conf.d/00-ioncube.ini
COPY 00-ioncube.ini /etc/php/8.2/apache2/conf.d/00-ioncube.ini
docker build -t freepbx17-ioncube .

然后把 compose 中 freepbx 服务的 image: 改为 freepbx17-ioncube,重建后永久生效。

11.6 fwconsole ma downloadinstall 长时间卡住

原因:模块市场 mirror.freepbx.org 网络不稳(容器内 DNS 或出口带宽问题)。处理:Ctrl+C 中断后重跑同一命令可续装;多次失败时检查容器 DNS(docker exec freepbx-17-stack-freepbx-1 cat /etc/resolv.conf)与到 mirror.freepbx.org 的连通性。

11.7 端口 5062 收到注册但宿主防火墙拦截 RTP

现象:注册正常、拨打回铃音正常但接通后无声音。排查:宿主防火墙(ufw/firewalld/云安全组)需放行映射的 UDP 段 16384-16484;仅放行 TCP 的 Web 端口不够,RTP 全部走 UDP。

十二、参考链接