Code Explanation
Dockerfile
FROM debian:bookworm
...
RUN apt-get update && apt-get install -y \
wget curl gnupg2 sudo git vim ca-certificates lsb-release software-properties-common \
systemd systemd-sysv dbus net-tools openssh-client \
&& apt-get clean
WORKDIR /usr/src
COPY sng_freepbx_debian_install.sh .
RUN chmod +x ./sng_freepbx_debian_install.sh
RUN ln -s /var/lib/asterisk/bin/fwconsole /usr/sbin/fwconsole || true
EXPOSE 80 443 5060/udp 5160/udp 18000-18100/udp
CMD ["/bin/bash"]The image is deliberately thin:
- Base:
debian:bookworm— Sangoma’s install script hard-codes support for Debian 12 only (it checksVERSION_CODENAME//etc/debian_versionand exits if it isn’tbookworm). - Installed at build time: only
systemd/systemd-sysv/dbus(so the container can boot like a real init system) plus generic tooling the install script or an admin would want (wget,curl,git,vim, etc). FreePBX, Asterisk, MariaDB, Apache, and PHP are not installed here — that all happens later, at container runtime, by running the copied-in script manually. - The
fwconsolesymlink:fwconsoleis FreePBX’s CLI (module management, restarts, etc). The install script normally expects it at a path that isn’t always onPATHyet immediately after install in a fresh container; the symlink into/usr/sbinpre-empts that sofwconsoleworks without extra steps once FreePBX is installed. The|| truemakes the build tolerant of the target not existing yet at build time. CMD ["/bin/bash"]: the image’s default command is just a shell — the actual PID 1 process is overridden bydocker-compose.yml’scommand: ["/sbin/init"], not this line.
Installing FreePBX at build time isn’t possible here: the installer needs a running init system managing
services (systemctl), which only exists once the container is actually started, not while docker build
is assembling image layers.
docker-compose.yml
services:
freepbx:
build: { context: ., dockerfile: Dockerfile }
container_name: freepbx17
hostname: freepbx
privileged: true
ports:
- "8060:80"
- "4436:443"
- "5060:5060/udp"
- "5160:5160/udp"
- "18000-18100:18000-18100/udp"
volumes:
- freepbx_www:/var/www/html
- freepbx_etc:/etc/asterisk
- mysql_data:/var/lib/mysql
tty: true
stdin_open: true
security_opt:
- seccomp:unconfined
command: ["/sbin/init"]
volumes:
freepbx_www:
freepbx_etc:
mysql_data:privileged: trueandsecurity_opt: [seccomp:unconfined]— required for systemd inside the container to manage cgroups, mount points, and services the way it would on a real host. This is the trade-off mentioned in the Introduction: it’s a heavier security posture than a typical single-process container.command: ["/sbin/init"]overrides the Dockerfile’s defaultCMD, making systemd PID 1 instead of a shell — this is what letssystemctl-managed services (Apache, MariaDB, Asterisk) actually run inside the container.tty: true/stdin_open: truekeep an interactive terminal available, mainly useful fordocker attach/debugging.- Port mapping: container ports 80/443 (FreePBX web UI) are remapped to host ports 8060/4436 to avoid colliding with anything else already bound to 80/443 on the host; SIP (5060, 5160) and the RTP range (18000–18100) are passed through with the same port numbers since SIP/RTP endpoints generally expect to see the port they dialed.
- Named volumes (
freepbx_www,freepbx_etc,mysql_data) persist the FreePBX web app, Asterisk config, and the MariaDB database outside the container’s writable layer — see Usage for what’s in each.
sng_freepbx_debian_install.sh
This is Sangoma’s own, unmodified official FreePBX 17 install script — it’s copied into the image as-is, not authored by this project. At a high level, run against a fresh Debian 12 system (or, here, a freshly-started container), it:
- Validates the environment — confirms it’s running as root, on Debian 12 (
bookworm), and on a kernel it recognizes (check_kernel_compatibility). - Parses CLI flags (
--skipversion,--dev,--testing,--nofreepbx,--noasterisk,--dahdi/--dahdi-only,--debianmirror,--npmmirror, etc.) that control which parts of the install run and which package mirrors are used. - Sets up APT repositories (
setup_repositories) — adds Sangoma’s FreePBX repo (signed with their GPG key), Debian’s own repos, and pins them so the system doesn’t drift onto an unsupported Debian release. - Installs the actual stack —
apache2,mariadb-server/mariadb-client,nodejs, ODBC drivers (odbc-mariadb), Asterisk (viainstall_asterisk, pinned toASTVERSION=22), and the FreePBX package itself (sangoma-pbx17), holding those packages afterwards (hold_packages) so a strayapt upgradedoesn’t silently break the install. - Configures Apache/PHP — disables
expose_php, raisesmax_input_vars, disables Apache’sServerTokens/ServerSignaturebanners, enables the FreePBX vhost and required Apache modules (SSL,mod_expires), then restarts Apache. - Brings up FreePBX itself via
fwconsole— installs/upgrades all modules (fwconsole ma installlocal,fwconsole ma upgradeall), refreshes module signatures, thenfwconsole reloadandfwconsole restartto bring the PBX fully online. - Runs post-install validation (
check_services,check_php_version,check_freepbx,check_asterisk) and prints a summary with total execution time before exiting.
Everything from step 3 onward only happens when you actually run the script inside the running container
(bash sng_freepbx_debian_install.sh --skipversion, per Usage) — the Docker build step only gets
you to a bootable Debian + systemd environment with the script sitting ready to run.
docker (reset script)
A short, unrelated helper at the repo root (not referenced by the Dockerfile or compose file) that stops and removes every container/image on the host and prunes unused volumes/networks — a blunt “start over” button for local development. See Usage for the caveat about its scope.