PearlPBX2
← All posts

Install, update and roll back Asterisk with Ansible

Deploying an Asterisk PBX by hand means compiling from source, a dozen config files, secrets you have to generate somewhere, and a firewall that is easy to configure in a way that locks you out over SSH. PearlPBX2 covers that whole path with one Ansible playbook and three scripts: install.sh, update.sh and rollback.sh.

The fastest start: one command

On a clean Debian or Ubuntu server, one command run as root is enough:

curl -fsSL https://pearlpbx2.com/quickstart-install.sh | sudo bash

The script clones the repository, runs the Ansible installer without asking any questions, and finishes by creating an admin user with a generated password.

If you only want to look at the product, there is a Docker variant. Docker must already be installed:

curl -fsSL https://pearlpbx2.com/quickstart-docker.sh | bash

It also clones the repository, generates a .env, brings up the stack with Docker Compose and creates an admin user with a generated password. How the two paths differ in practice is covered in Docker install vs. install.sh on a clean OS. The rest of this article is about what happens inside the first one.

One playbook, nine roles

install.sh runs a playbook made of nine Ansible roles: system, postgres, redis, asterisk, pearlpbx2, services, nginx, tftp, firewall. Each role owns one part of the stack, so an install is repeatable and reads as a sequence of understandable steps rather than one long script.

A tenth role, asterisk_update, is not part of installation. Only update-asterisk.sh uses it, more on that below.

What install.sh does

install.sh compiles Asterisk 22 from source (the version is pinned to 22.11.0, not “latest”), generates all the secrets it needs, sets up cron jobs and applies the nftables firewall rules. Compiling takes 15–30 minutes depending on the CPU, and the script warns you about that up front.

The cron jobs the installer sets up on its own:

  • a database backup every day at 01:30;
  • an Asterisk configuration backup every day at 02:30;
  • Asterisk log rotation;
  • conversion of call recordings to mp3 every 15 minutes and a daily offload at 03:00;
  • system monitoring every 15 minutes;
  • a daily email report of the longest calls at 06:00, if recipients are set in the configuration.

The --domain <fqdn> and --email <address> flags are for the Let’s Encrypt certificate, covered below.

No pauses, no questions

The installer is fully unattended. Before applying the nftables rules it detects the SSH port with sshd -T. That returns the value sshd will actually listen on, taking every file in sshd_config.d into account, and that is the port it opens. The detected port goes into the log and the install carries on without asking for confirmation.

This matters when there is no terminal: ansible-pull, CI or any script no longer hangs on an interactive prompt. The only thing left after installation is creating an admin user with ./manage.sh createsuperuser. The quickstart script does that for you.

HTTPS and wss:// without manual setup

WebRTC needs a valid TLS certificate. The nginx role obtains one automatically, in three tiers, depending on what the server has:

  1. If a domain is given (--domain), a Let’s Encrypt certificate through HTTP-01.
  2. If there is no domain but a public IPv4 address is detected, Let’s Encrypt’s short-lived IP-address certificate.
  3. Otherwise, a self-signed certificate as a fallback.

For the first two tiers, automatic renewal is handled by the pearlpbx2-certbot-renew timer. There is no manual TLS step.

update.sh

Updating PearlPBX2 takes two steps. First you update the repository clone:

git pull
sudo ./update.sh

update.sh does not download anything itself. It deploys whichever commit is currently checked out in the clone: it syncs the code into the install directory, updates Python dependencies, applies migrations, collects static files and restarts PearlPBX2 and the auxiliary services. That keeps updates reproducible: you know exactly which commit goes live, because you picked it.

Before migrating, the playbook records the state of applied migrations and adds an entry to the deploy ledger, which rollback.sh uses later. update.sh only updates the application. It leaves nginx, the firewall and the seed data alone, so it is safe to run on a live system.

update-asterisk.sh: upgrading Asterisk in place

You can change the Asterisk version separately from PearlPBX2 itself. update-asterisk.sh upgrades an already installed Asterisk on the same server, with no reinstall. By default the target version comes from group_vars/all.yml, and you can pass another one with --version X.Y.Z.

The steps are ordered so that anything that can fail happens while the old Asterisk is still taking calls:

  1. First it backs up /etc/asterisk, the binaries, modules and headers, and /var/lib/asterisk.
  2. The new version is compiled while Asterisk keeps serving calls.
  3. Then Asterisk is stopped gracefully: the script waits for active calls to finish (up to 15 minutes by default, adjustable with --timeout; --no-wait stops it immediately).
  4. Only the binaries and modules are installed. Configs, sounds, logs and spool are left untouched.
  5. Asterisk and the services connected through AMI are restarted.

This script has its own rollback too: update-asterisk.sh --rollback restores the previous version from the backup, and --list shows the upgrade history. --restore-configs and --restore-sounds additionally restore the configuration and the sounds.

rollback.sh: the deploy-state ledger

If an update goes wrong, rollback.sh -n <number of steps> reverts the code, the database migrations and the services to a previous deploy state. The mechanism keeps a ledger (the deploy-state ledger) for every deployment, so a rollback is not an attempt to “restore from a backup and hope” but a controlled operation with a known result.

What is useful to know about rollback:

  • rollback.sh --list shows the deploy history and changes nothing. Before rolling back you can see which commit you will land on.
  • The script asks you to type yes to confirm. The -y flag skips the question, for example in automation.
  • The ledger keeps the last ten deployments, so you can go back at most ten steps.
  • Migrations are reverted first, while the files for the newer migrations are still on disk: Django needs them to work out the way back. Only then does the code return to the target commit.
  • If the repository clone has uncommitted changes, the rollback stops and tells you why. Otherwise git checkout would silently destroy your work.
  • After a rollback, ledger entries newer than the target commit are removed, because they are no longer valid return points.

Docker Compose for evaluation and CI

For a quick look at the product without waiting for a full Ansible deployment, there is docker compose up: Django, Asterisk, PostgreSQL, Redis, FastAGI and the dashboard listener. The callback daemon is added separately with --profile callback, because it places real outbound calls. A separate docker-compose file for integration tests runs the tests/integration directory against a real Asterisk 22 container in CI, not against mocks.

Where the automated installer runs

The Ansible path (install.sh and the production quickstart script) targets Debian and Ubuntu, the supported distributions for automated installation. For evaluation, development or CI on any other system there is Docker Compose: the same stack, without depending on a particular host distribution.

Summary

PearlPBX2 installs with one command or one nine-role Ansible playbook, with no pauses, gets its own HTTPS certificate, updates in a controlled way through update.sh and rolls back from a state ledger with rollback.sh -n. Asterisk is upgraded separately through update-asterisk.sh. For evaluation and CI there is Docker Compose with a real Asterisk 22 in a container.