Skip to main content

Install with Docker Compose

The production bundle in deploy/compose/prod runs MonoPbx with Postgres, Valkey, ClickHouse and rtpengine. The VM installer wraps exactly this bundle. Use the bundle directly if you already manage Docker hosts.

Requirements: Docker Engine 24+ with the Compose plugin v2.24+, on Linux (Ubuntu 24.04 LTS is the supported host). Docker Desktop (macOS, Windows) is for development and trials only, and Windows is never a production host.

warning

The development sandbox (make dev) is a different thing: it publishes the databases on loopback and uses throwaway secrets. Never use it in production.

Install​

The first run creates .env from the template and stops, so you can set it. MPBX_IMAGE, MPBX_RTPENGINE_IMAGE and MPBX_MEDIA_IMAGE must name your release (pin them by digest), and MPBX_PUBLIC_URL is the address users open. Then run it again:

cd deploy/compose/prod
./start.sh # first run: creates .env and stops. Set the images and MPBX_PUBLIC_URL in .env, then:
./start.sh # second run: mpbx init (once), then start the stack

start.sh runs three steps, which you can also run yourself (the block is also what the guide check runs):

set -eu
cd deploy/compose/prod
docker compose -f compose.init.yaml run --rm init # 1. mpbx init: secrets into .env, recovery key shown ONCE
docker compose -f compose.yaml -f compose.linux.yaml up -d --wait # 2. start (Docker Desktop: compose.desktop.yaml)
scripts/smoke.sh # 3. optional: health, isolation and login checks

Write the recovery key down when mpbx init shows it. It is not stored on the host. Without it, a lost .env means lost trunk passwords, API keys and encrypted recordings. In a terminal, init shows the key before it writes anything and asks you to re-type one group of it. Without a terminal it prints the key once to stdout after storing the secrets, so do not keep that output. Running init again changes nothing.

.env (mode 0600, git-ignored) holds your secrets. Back it up together with the volumes.

What runs​

ServiceProfilePublished
postgres, valkey, clickhousedefaultnothing (internal network)
mpbx (API and embedded web client)defaultMPBX_HTTP_BIND:MPBX_HTTP_PORT, default 0.0.0.0:8080
rtpenginedefaultUDP RTPE_PORT_MIN-MAX (30000-40000) on the host
coturnturn3478 and the relay range
prometheus, grafanaobservabilityGrafana on 127.0.0.1 only
mpbx-mediamedianothing; its image is still a stub, so leave the profile off
mpbx-edgeedgethe SIP edge role does not exist yet

Enable profiles with COMPOSE_PROFILES=observability,turn in .env.

Networking notes​

  • The data network is internal: it has no route out, and no database port is published.
  • rtpengine, coturn and the SIP edge use host networking on Linux. rtpengine's control port listens on 127.0.0.1 only. Open UDP RTPE_PORT_MIN-RTPE_PORT_MAX in your firewall. Behind NAT set RTPE_ADVERTISE=<public IP>. start.sh detects RTPE_INTERFACE.
  • mpbx itself stays on the bridge networks so it can reach the databases.

Secrets and database roles​

  • There are no default secrets. Compose refuses to start until mpbx init has written them.
  • The application connects to Postgres as a non-superuser without BYPASSRLS, and row-level security is forced on tenant tables. The Postgres superuser mpbx_admin has no password and is reachable only over the container's local socket: docker compose exec postgres psql -U mpbx_admin -d mpbx.
  • Every container drops all capabilities and sets no-new-privileges.

Operations​

./start.sh # idempotent; init is skipped once the secrets exist
./start.sh ps | logs -f mpbx # any docker compose command
./start.sh down # stop, keep data
docker compose -f compose.yaml -f compose.linux.yaml down -v # DELETES the databases

Set COMPOSE_PROJECT_NAME to run a second copy next to this one. Upgrade and rollback commands are not built yet. HTTPS and ACME for the Compose bundle come from the installer's overlay (deploy/installer/compose.installer.yaml); see Certificates and HTTPS.

For backups see Backup and restore.