Skip to main content

Air-gapped installs and degraded mode

An air-gapped install has no route to the internet. It runs phones, calls, voicemail, the web client and call history on your network. Some features depend on internet services and stop working there.

What keeps working​

  • Calls between phones, softphones and trunks you reach over your own network.
  • Voicemail, call history, reports, the web client, the admin console and backups.
  • Softphones on the same network as the PBX.
  • Certificates you supply yourself. Automatic certificates from a public authority need internet access.

What stops​

FeatureWhyWhat to do
Mobile push for softphonesNeeds the relay on the internetKeep the app open on the LAN; accept that incoming calls do not wake a closed app
WhatsApp, Messenger, TelegramThe providers call the PBX from the internetNot available
Live chat widgetBrowsers on other websites reach the PBX over public HTTPSUse it only where the website can reach the PBX
AI featuresBase URLs must be public HTTPS; a model on your LAN is refusedNot available
Automatic certificatesThe certificate authority is on the internetInstall your own certificate
Hosted SMS providers over HTTPSPublic providersUse an SMS centre on your network over SMPP, after a test

Installing​

This procedure was run on an isolated lab server (Ubuntu 24.04, a network with no route, no DNS and no internet). You need one connected machine with Docker and the same CPU architecture as the server (uname -m: amd64 or arm64), and a way to carry files across (USB disk, one-way file gateway).

1. On the connected machine, download the release. Get install.sh, mpbx-bundle.tar.gz and SHA256SUMS from the release channel you use. Read the image references the bundle names:

tar -xzOf mpbx-bundle.tar.gz mpbx-bundle/images.env

2. Export the images. Release images are named by digest (name@sha256:...). A digest name does not survive docker save and docker load, so give each image a tag first and install by tag. Pull for the server's architecture:

docker pull --platform linux/amd64 <MPBX_IMAGE from images.env> # repeat for MPBX_RTPENGINE_IMAGE and MPBX_MEDIA_IMAGE
docker tag <MPBX_IMAGE> mpbx:1.0.0 && docker tag <MPBX_RTPENGINE_IMAGE> mpbx-rtpengine:1.0.0 && docker tag <MPBX_MEDIA_IMAGE> mpbx-media:1.0.0
docker pull --platform linux/amd64 postgres:17-alpine valkey/valkey:8-alpine clickhouse/clickhouse-server:25.8-alpine
docker save -o mpbx-images.tar mpbx:1.0.0 mpbx-rtpengine:1.0.0 mpbx-media:1.0.0
docker save -o third-party-images.tar postgres:17-alpine valkey/valkey:8-alpine clickhouse/clickhouse-server:25.8-alpine

Add coturn/coturn:4.12.0 if you will use --turn, and prom/prometheus, prom/alertmanager, quay.io/prometheus/node-exporter, grafana/grafana (the tags in deploy/compose/prod/compose.yaml) for --observability. For Grafana's ClickHouse plugin see deploy/compose/prod/compose.grafana-offline.yaml.

3. Export Docker for the server. install.sh --air-gapped never downloads Docker. On a connected Ubuntu 24.04 machine of the same architecture, collect the packages with their dependencies (Docker Engine 24+ and Compose 2.24+ are required):

mkdir debs && sudo apt-get update && sudo apt-get -o Dir::Cache::archives="$PWD/debs" install --download-only -y docker.io docker-compose-v2 iptables

4. Carry and verify. Put everything in one directory with a checksum list and check it on the server:

sha256sum install.sh mpbx-bundle.tar.gz mpbx-images.tar third-party-images.tar debs/*.deb > TRANSFER.sha256 # connected machine
sha256sum -c TRANSFER.sha256 # server

Also compare mpbx-bundle.tar.gz with the line in the release SHA256SUMS.

5. On the server, install Docker and load the images.

sudo dpkg -i debs/*.deb && sudo systemctl enable --now docker
sudo docker load -i mpbx-images.tar && sudo docker load -i third-party-images.tar

6. Run the installer from the local files. Set the bundle file and its checksum, name the address phones use, and choose the plain web port or a TLS proxy you run (automatic certificates need the internet):

sudo MPBX_BUNDLE_FILE=$PWD/mpbx-bundle.tar.gz MPBX_BUNDLE_SHA256=<sha256 of the bundle> \
MPBX_IMAGE=mpbx:1.0.0 MPBX_RTPENGINE_IMAGE=mpbx-rtpengine:1.0.0 MPBX_MEDIA_IMAGE=mpbx-media:1.0.0 \
bash install.sh --air-gapped --public-ip <server LAN address> --no-tls

--air-gapped uses the loaded tags of Postgres, Valkey and ClickHouse (the file compose.airgap.yaml) so Docker never tries to reach a registry. --public-ip skips the address lookup. A server without a default gateway is accepted: the installer takes its first global address. The installer prints the recovery key once and the single-use setup URL; keep both as the install guide says.

7. Check. All services must be healthy:

sudo mpbx-ctl ps
sudo mpbx-ctl selftest # service, containers, web, database and isolation checks
sudo docker exec mpbx-prod-mpbx-1 /usr/local/bin/mpbx doctor # the `mpbx` command lives in the container

Then open the setup URL from a machine on the same network. Keep the install files: mpbx-ctl upgrade later takes a bundle file the same way (mpbx-ctl upgrade ./mpbx-bundle.tar.gz), after you docker load the new images.

Degraded-mode checklist​

  1. Run docker exec mpbx-prod-mpbx-1 /usr/local/bin/mpbx ports and open only the inbound rows you need. Internal rows stay closed to other networks.
  2. Confirm phones register (docker exec mpbx-prod-mpbx-1 /usr/local/bin/mpbx doctor, and the Devices page).
  3. Confirm voicemail and call history are working.
  4. Tell users which features are off, so they do not wait for them.

Operator runbook: docs/runbooks/air-gapped-degraded-mode.md in the repository.