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 stack — apache2, 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