Skip to content

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 checks VERSION_CODENAME//etc/debian_version and exits if it isn’t bookworm).
  • 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 fwconsole symlink: fwconsole is FreePBX’s CLI (module management, restarts, etc). The install script normally expects it at a path that isn’t always on PATH yet immediately after install in a fresh container; the symlink into /usr/sbin pre-empts that so fwconsole works without extra steps once FreePBX is installed. The || true makes 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 by docker-compose.yml’s command: ["/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: true and security_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 default CMD, making systemd PID 1 instead of a shell — this is what lets systemctl-managed services (Apache, MariaDB, Asterisk) actually run inside the container.
  • tty: true / stdin_open: true keep an interactive terminal available, mainly useful for docker 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:

  1. Validates the environment — confirms it’s running as root, on Debian 12 (bookworm), and on a kernel it recognizes (check_kernel_compatibility).
  2. 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.
  3. 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.
  4. Installs the actual stackapache2, mariadb-server/mariadb-client, nodejs, ODBC drivers (odbc-mariadb), Asterisk (via install_asterisk, pinned to ASTVERSION=22), and the FreePBX package itself (sangoma-pbx17), holding those packages afterwards (hold_packages) so a stray apt upgrade doesn’t silently break the install.
  5. Configures Apache/PHP — disables expose_php, raises max_input_vars, disables Apache’s ServerTokens/ServerSignature banners, enables the FreePBX vhost and required Apache modules (SSL, mod_expires), then restarts Apache.
  6. Brings up FreePBX itself via fwconsole — installs/upgrades all modules (fwconsole ma installlocal, fwconsole ma upgradeall), refreshes module signatures, then fwconsole reload and fwconsole restart to bring the PBX fully online.
  7. 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.

Last updated on