FusionPBX 5 Freeswitch 1.10 Debian 11 PostgreSQL Nginx Install Guide

Fusionpbx

Fusionpbx is a full featured mult-tenant GUI for Freeswitch.  This guide covers the installation of Fusionpbx and Freeswitch® with PostgreSQL and NGINX on Debian 11. 

Tested on:

Debian 11 (Bullseye) x64 minimal install
Freeswitch 1.10
FusionPBX 5
Nginx 1.1x
PHP 8.1
PostgreSQL 16

Assumptions:

Console text mode (multi-user.target)
Installation done as root user (su).  

Prerequisites

Verify locale is set to C.UTF-8.

locale

If it is not then set it now.

# Select C.UTF-8 UTF-8
apt update && apt -y install locales && dpkg-reconfigure locales

Log out/in or close/open shell for changes to take effect.  This should be done before PostgreSQL is installed.

Install base packages

apt update && apt upgrade -y && apt -y remove apache2
apt -y install wget git nano dbus sudo nginx curl dialog qrencode net-tools lsb-release sqlite3 haveged ghostscript libtiff5-dev libtiff-tools at tftpd ssl-cert gnupg2 software-properties-common

SNMP

apt install -y snmpd
echo "rocommunity public" > /etc/snmp/snmpd.conf
systemctl restart snmpd

IPtables

If ufw is not installed, you can ignore any resulting error.

apt install -y iptables
update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
ufw --force disable && ufw --force reset && apt remove -y ufw

sngrep

wget http://packages.irontec.com/public.key -q -O - | apt-key add -
echo "deb http://packages.irontec.com/debian $(lsb_release -sc) main" > \
/etc/apt/sources.list.d/irontec.list

apt update && apt -y install sngrep

PostgreSQL

echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -sc)-pgdg main" > \
/etc/apt/sources.list.d/postgresql.list

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
apt update && apt install postgresql-16 postgresql-client-16

PHP

Install Dependencies

# Remove any existing PHP files
apt remove -y php*

# Install dependencies
apt -y install apt-transport-https ca-certificates

Add Repository

# Add php 8.1 repository
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > \
/etc/apt/sources.list.d/php.list

Install

apt update && apt -y install php8.1 php8.1-cli php8.1-dev php8.1-fpm php8.1-pgsql php8.1-sqlite3 php8.1-odbc php8.1-curl php8.1-imap php8.1-xml php8.1-gd php8.1-mbstring php8.1-ldap
update-alternatives --set php /usr/bin/php8.1

Disable Firewall

It is sometimes helpful to disable the firewall during installation.  If the packages are not installed or enabled, you can ignore any resulting errors.

systemctl disable firewalld
systemctl disable iptables
systemctl stop firewalld
systemctl stop iptables

Set Timezone

Using tzselect is one way to find the correct timezone.

## FIND YOUR TIMEZONE
tzselect
## SET TIMEZONE EXAMPLE
timedatectl set-timezone America/Vancouver

## CHECK TIMEZONE
​timedatectl status
systemctl restart rsyslog

Install

Freeswitch

Freeswitch

Get your signalwire token according to these instructions:
https://freeswitch.org/confluence/display/FREESWITCH/HOWTO+Create+a+SignalWire+Personal+Access+Token

Once you have your signalwire token install freeswitch packages

TOKEN=YOURSIGNALWIRETOKEN
 
apt-get update && apt-get install -y gnupg2 wget lsb-release
 
wget --http-user=signalwire --http-password=$TOKEN -O /usr/share/keyrings/signalwire-freeswitch-repo.gpg https://freeswitch.signalwire.com/repo/deb/debian-release/signalwire-freeswitch-repo.gpg
 
echo "machine freeswitch.signalwire.com login signalwire password $TOKEN" > /etc/apt/auth.conf

echo "deb [signed-by=/usr/share/keyrings/signalwire-freeswitch-repo.gpg] https://freeswitch.signalwire.com/repo/deb/debian-release/ `lsb_release -sc` main" > /etc/apt/sources.list.d/freeswitch.list

echo "deb-src [signed-by=/usr/share/keyrings/signalwire-freeswitch-repo.gpg] https://freeswitch.signalwire.com/repo/deb/debian-release/ `lsb_release -sc` main" >> /etc/apt/sources.list.d/freeswitch.list
 
apt update && apt install -y freeswitch-meta-all

Database

Check that localhost connections are always allowed.  This is necessary for the backup script.

nano +125 /etc/postgresql/16/main/pg_hba.conf
host all all 127.0.0.1/32 trust
systemctl restart postgresql

Check PostgreSQL locale

sudo -u postgres psql -l

If Encoding is not UTF8 and Collate & Ctype is not C.UTF-8 then set it now.

sudo -u postgres psql

update pg_database set encoding = 6, datcollate = 'C.UTF-8', datctype = 'C.UTF-8' where datname = 'template0';
update pg_database set encoding = 6, datcollate = 'C.UTF-8', datctype = 'C.UTF-8' where datname = 'template1';

Create databases and user

cd /tmp

# Set the DB password variable.
password=fusionpbx
sudo -u postgres psql -c "SET client_encoding = 'UTF8';"
sudo -u postgres psql -c "CREATE DATABASE fusionpbx;" 
sudo -u postgres psql -c "CREATE DATABASE freeswitch;"
sudo -u postgres psql -c "CREATE ROLE fusionpbx WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres psql -c "CREATE ROLE freeswitch WITH SUPERUSER LOGIN PASSWORD '$password';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE fusionpbx to fusionpbx;"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to fusionpbx;"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE freeswitch to freeswitch;"

Fusionpbx

There may be enhancements and fixes in our repository that are not in the official repository.  We also strive to merge updates from the official repository on a regular basis.

mkdir -p /etc/fusionpbx
mkdir -p /var/www/fusionpbx
mkdir -p /var/cache/fusionpbx
git clone -b 5.1 https://github.com/powerpbx/fusionpbx.git /var/www/fusionpbx

Copy Directories

Replace default freeswitch conf files with fusionpbx conf files
mv /etc/freeswitch /etc/freeswitch.orig
mkdir /etc/freeswitch
cp -R /var/www/fusionpbx/app/switch/resources/conf/* /etc/freeswitch

Copy music directories to default location

mkdir -p /usr/share/freeswitch/sounds/music/default
mv /usr/share/freeswitch/sounds/music/*000/ /usr/share/freeswitch/sounds/music/default/

Php-fpm config

sed 's#post_max_size = .*#post_max_size = 80M#g' -i /etc/php/8.1/fpm/php.ini
sed 's#upload_max_filesize = .*#upload_max_filesize = 80M#g' -i /etc/php/8.1/fpm/php.ini
sed 's#max_execution_time = .*#max_execution_time = 120#g' -i /etc/php/8.1/fpm/php.ini
sed 's#;max_input_vars = .*#max_input_vars = 8000#g' -i /etc/php/8.1/fpm/php.ini
systemctl restart php8.1-fpm

Nginx config

mkdir -p /etc/nginx/ssl
cd /etc/nginx/sites-available
wget https://raw.githubusercontent.com/powerpbx/fusionpbx-install.sh/master/debian/resources/nginx/fusionpbx
sed -i /etc/nginx/sites-available/fusionpbx -e 's#unix:.*;#unix:/var/run/php/php8.1-fpm.sock;#g'

ln -s /etc/nginx/sites-available/fusionpbx /etc/nginx/sites-enabled/fusionpbx
rm /etc/nginx/sites-enabled/default

# link to self signed certificate
ln -s /etc/ssl/private/ssl-cert-snakeoil.key /etc/ssl/private/nginx.key
ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/ssl/certs/nginx.crt

# For future use
mkdir -p /var/www/letsencrypt/

systemctl restart nginx

Systemd

Create custom Freeswitch unit file

systemctl stop freeswitch
rm -r /run/freeswitch
nano /etc/systemd/system/freeswitch.service
[Unit]
Description=freeswitch
After=syslog.target network.target local-fs.target postgresql.service

[Service]
Type=forking
RuntimeDirectory=freeswitch
PIDFile=/run/freeswitch/freeswitch.pid
Environment="DAEMON_OPTS=-ncwait -nonat"
EnvironmentFile=-/etc/default/freeswitch
ExecStart=/usr/bin/freeswitch $DAEMON_OPTS
TimeoutSec=45s
Restart=always

User=www-data
Group=www-data

LimitCORE=infinity
LimitNOFILE=100000
LimitNPROC=60000
LimitSTACK=250000
LimitRTPRIO=infinity
LimitRTTIME=infinity
IOSchedulingClass=realtime
IOSchedulingPriority=2
CPUSchedulingPriority=89
UMask=0007

; Comment this out if using OpenVZ
CPUSchedulingPolicy=rr

[Install]
WantedBy=multi-user.target

Create environment file.

cat >> /etc/default/freeswitch << EOF
# Uncommented variables will override variables in unit file
# User=""
# Group=""
# DAEMON_OPTS=""
EOF

Set ownership and permissions

Run this on a fresh install and anytime there are any updates or changes or if experiencing problems.

# Set ownership
chown -R www-data. /etc/freeswitch /etc/fusionpbx /var/cache/fusionpbx /var/lib/freeswitch \
/var/log/freeswitch /usr/share/freeswitch /var/www/fusionpbx /var/run/freeswitch

## Set directory permissions to 755 (u=rwx,g=rx,o='rx')
find /etc/freeswitch -type d -exec chmod 755 {} \;
find /etc/fusionpbx -type d -exec chmod 755 {} \;
find /var/cache/fusionpbx -type d -exec chmod 755 {} \;
find /var/lib/freeswitch -type d -exec chmod 755 {} \;
find /var/log/freeswitch -type d -exec chmod 755 {} \;
find /usr/share/freeswitch -type d -exec chmod 755 {} \;
find /var/www -type d -exec chmod 755 {} \;

## Set file permissions to 664 (u=rw,g=rw,o=r)
find /etc/freeswitch -type f -exec chmod 664 {} \;
find /etc/fusionpbx -type f -exec chmod 664 {} \;
find /var/lib/freeswitch -type f -exec chmod 664 {} \;
find /var/log/freeswitch -type f -exec chmod 664 {} \;
find /usr/share/freeswitch -type f -exec chmod 664 {} \;
find /var/www -type f -exec chmod 664 {} \;

## Set misc permissions to 755 (u=rwx,g=rx,o='rx')
chmod -R 755 /var/www/fusionpbx/secure

## These two files do not yet exist on a fresh install
chmod 755 /etc/cron.daily/fusionpbx-backup
chmod 755 /etc/cron.daily/fusionpbx-maintenance

Enable services

systemctl daemon-reload
systemctl enable freeswitch
systemctl restart freeswitch
Browse to the public IP address of the server

 http://xx.xx.xx.xx 

to complete the install using the following:

Username: superadmin (or whatever you want)
Password: someSuperAdminPassword 

Database Name: fusionpbx
Database Username: fusionpbx
Database Password: fusionpbx (The database user password created earlier)

The install may take a minute or two on slower servers.

Post install tasks are optional but recommended.

Post Install

Enable freeswitch database connection

This optional configuration changes FreeSWITCH's internal data storage from SQLite to PostgreSQL. SQLite is suitable for testing, troubleshooting, and moderate usage but may not be optimal for busy servers requiring high performance. Additionally, SQLite has a tendency to become corrupted if the server experiences an unclean shutdown.

nano +185 /etc/freeswitch/autoload_configs/switch.conf.xml
<param name="core-db-dsn" value="pgsql://hostaddr=127.0.0.1 dbname=freeswitch user=fusionpbx password='fusionpbx'" />
systemctl restart freeswitch

Firewalld

apt -y install firewalld
systemctl enable firewalld
sed 's#FirewallBackend=.*#FirewallBackend=iptables#g' -i /etc/firewalld/firewalld.conf
systemctl start firewalld

firewall-cmd --permanent --zone=public --add-service={http,https}
firewall-cmd --permanent --zone=public --add-port={5060,5061,5080,5081}/tcp
firewall-cmd --permanent --zone=public --add-port={5060,5061,5080,5081}/udp
firewall-cmd --permanent --zone=public --add-port=16384-32768/udp
firewall-cmd --reload
firewall-cmd --list-all

Daily Backup and Housekeeping

Backup script

nano /etc/cron.daily/fusionpbx-backup
#!/bin/sh

db_host=127.0.0.1
db_port=5432
now=$(date +%Y-%m-%d)
mkdir -p /var/backups/fusionpbx/postgresql

echo "Backup Started"

# Delete postgres backups if older than 4 days
find /var/backups/fusionpbx/postgresql/fusionpbx_pgsql* -mtime +4 -exec rm -f {} \;

# Delete the main backup if older than 4 days
find /var/backups/fusionpbx/*.tgz -mtime +4 -exec rm -f {} \;

# Create database backup
pg_dump --verbose -Fc --host=${db_host} --port=${db_port} -U fusionpbx fusionpbx --schema=public -f /var/backups/fusionpbx/postgresql/fusionpbx_pgsql_${now}.sql

# Combine and compress to create the main backup
tar --exclude='/var/lib/freeswitch/recordings/*/archive' \
-zvcf /var/backups/fusionpbx/backup_${now}.tgz /var/backups/fusionpbx/postgresql/fusionpbx_pgsql_${now}.sql \
/var/www/fusionpbx \
/usr/share/freeswitch/scripts \
/usr/share/freeswitch/sounds/music \
/var/lib/freeswitch/storage \
/var/lib/freeswitch/recordings \
/etc/fusionpbx \
/etc/freeswitch

echo "Backup Completed"
chmod 755 /etc/cron.daily/fusionpbx-backup

Housekeeping script

nano /etc/cron.daily/fusionpbx-maintenance
#!/bin/sh

echo "Maintenance Started"

# Delete freeswitch logs older than 7 days
find /var/log/freeswitch/freeswitch.log.* -mtime +7 -exec rm {} \;

#delete php sessions older than 7 days
find /var/lib/php/sessions/* -name 'sess_*' -mtime +7 -exec rm {} \;

## Uncomment to delete call recordings older than 90 days
#find /var/lib/freeswitch/recordings/*/archive/*  -name '*.wav' -mtime +90 -exec rm {} \;
#find /var/lib/freeswitch/recordings/*/archive/*  -name '*.mp3' -mtime +90 -exec rm {} \;

## Uncomment to delete faxes older than 90 days 
#find /var/lib/freeswitch/storage/fax/*  -name '*.tif' -mtime +90 -exec rm {} \; 
#find /var/lib/freeswitch/storage/fax/*  -name '*.pdf' -mtime +90 -exec rm {} \;

## Uncomment to delete faxe_queue older than 30 days
psql --host=127.0.0.1 --username=fusionpbx -c "delete from v_fax_queue where fax_status = 'sent' and fax_date < NOW() - INTERVAL '30 days'"

## Uncomment to delete voicemails older than 90 days
#find /var/lib/freeswitch/storage/voicemail/default/*  -name 'msg_*.wav' -mtime +90 -exec rm {} \;
#find /var/lib/freeswitch/storage/voicemail/default/*  -name 'msg_*.mp3' -mtime +90 -exec rm {} \;
#psql --host=127.0.0.1 --username=fusionpbx -c "delete from v_voicemail_messages WHERE to_timestamp(created_epoch) < NOW() - INTERVAL '90 days'"

## Uncomment to delete call detail records older than 90 days
#psql --host=127.0.0.1 --username=fusionpbx -c "delete from v_xml_cdr WHERE start_stamp < NOW() - INTERVAL '90 days'"

## Uncomment to delete database_transactions older than 90 days
psql --host=127.0.0.1 --username=fusionpbx -c "delete from v_database_transactions where transaction_date < NOW() - INTERVAL '90 days'"

## Uncomment to delete email_queue older than 30 days
psql --host=127.0.0.1 --username=fusionpbx -c "delete from v_email_queue where email_status = 'sent' and email_date < NOW() - INTERVAL '30 days'"

echo "Maintenance Completed"
chmod 755 /etc/cron.daily/fusionpbx-maintenance

TFTP

If you plan to use hardware SIP phones you may want to enable TFTP.

Create tftp config

apt -y install xinetd
cat >> /etc/xinetd.d/tftp << EOF
service tftp
{
protocol        = udp
port            = 69
socket_type     = dgram
wait            = yes
user            = nobody
server          = /usr/sbin/in.tftpd
server_args     = /tftpboot
disable         = no
}
EOF

Make the directory and restart the daemon to start tftp.

mkdir /tftpboot
chmod 777 /tftpboot
systemctl restart xinetd
firewall-cmd --permanent --zone=public --add-port=69/udp
firewall-cmd --reload

Go to GUI Advanced > Default Settings > Provision.  About 20 rows down set enabled = Trueand reload

Add a new subcategory in Provision category by pressing the + icon and fill in as follows:

Category = provision
Subcategory = path
type = text
Value = /tftpboot
Enabled = True

Fail2ban

apt -y install fail2ban

URL=https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/debian/resources/fail2ban
DIR=/etc/fail2ban/filter.d

wget $URL/freeswitch.conf -O $DIR/freeswitch.conf
wget $URL/freeswitch-acl.conf -O $DIR/freeswitch-acl.conf
wget $URL/sip-auth-failure.conf -O $DIR/sip-auth-failure.conf
wget $URL/sip-auth-challenge.conf -O $DIR/sip-auth-challenge.conf
wget $URL/auth-challenge-ip.conf -O $DIR/auth-challenge-ip.conf
wget $URL/freeswitch-ip.conf -O $DIR/freeswitch-ip.conf
wget $URL/fusionpbx.conf -O $DIR/fusionpbx.conf
wget $URL/fusionpbx-mac.conf -O $DIR/fusionpbx-mac.conf
wget $URL/fusionpbx-404.conf -O $DIR/fusionpbx-404.conf
wget $URL/nginx-404.conf -O $DIR/nginx-404.conf
wget $URL/nginx-dos.conf -O $DIR/nginx-dos.conf
wget $URL/jail.local -O /etc/fail2ban/jail.local

systemctl restart fail2ban

If connecting from external LANs, it is a good idea to add their public IP addresses in fail2ban to prevent accidental banning of the entire LAN.

nano /etc/fail2ban/jail.conf
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
systemctl restart fail2ban

Email Queue

cp /var/www/fusionpbx/app/email_queue/resources/service/debian.service /etc/systemd/system/email_queue.service
systemctl daemon-reload
systemctl enable email_queue
systemctl start email_queue

Event Guard

cp /var/www/fusionpbx/app/event_guard/resources/service/debian.service /etc/systemd/system/event_guard.service
systemctl daemon-reload
systemctl enable event_guard
systemctl start event_guard

Troubleshooting the Dialplan

To view the dyamically generated xml files.

nano /usr/share/freeswitch/scripts/resources/functions/config.lua
## Change debug options from false to true to see lua generated output in fs_cli
       debug.params = true;
       debug.sql = true;
       debug.xml_request = true;
       debug.xml_string = true;
       debug.cache = true;
Sections: